001/*
002 * (C) Copyright 2006-2016 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 *     Tiago Cardoso <tcardoso@nuxeo.com>
018 */
019package org.nuxeo.ecm.platform.threed.convert;
020
021import org.apache.commons.io.FileUtils;
022import org.nuxeo.common.utils.Path;
023import org.nuxeo.ecm.core.api.Blob;
024import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
025import org.nuxeo.ecm.core.api.blobholder.SimpleBlobHolderWithProperties;
026import org.nuxeo.ecm.core.api.impl.blob.FileBlob;
027import org.nuxeo.ecm.core.convert.api.ConversionException;
028import org.nuxeo.ecm.platform.commandline.executor.api.CmdParameters;
029import org.nuxeo.ecm.platform.commandline.executor.api.CommandException;
030import org.nuxeo.ecm.platform.commandline.executor.api.CommandLineExecutorService;
031import org.nuxeo.ecm.platform.commandline.executor.api.CommandNotAvailable;
032import org.nuxeo.ecm.platform.commandline.executor.api.ExecResult;
033import org.nuxeo.ecm.platform.convert.plugins.CommandLineBasedConverter;
034import org.nuxeo.runtime.api.Framework;
035
036import java.io.File;
037import java.io.IOException;
038import java.io.Serializable;
039import java.io.UncheckedIOException;
040import java.util.*;
041import java.util.stream.Collectors;
042
043import static org.nuxeo.ecm.platform.threed.convert.Constants.*;
044
045/**
046 * Conversion from Collada to glTF format
047 *
048 * @since 8.4
049 */
050public class Collada2glTFConverter extends CommandLineBasedConverter {
051
052    @Override
053    protected Map<String, Blob> getCmdBlobParameters(BlobHolder blobHolder, Map<String, Serializable> parameters)
054            throws ConversionException {
055        return null;
056    }
057
058    @Override
059    protected Map<String, String> getCmdStringParameters(BlobHolder blobHolder, Map<String, Serializable> parameters)
060            throws ConversionException {
061        return null;
062    }
063
064    // Let's override to make sure we keep the original blob names!
065    @Override
066    public BlobHolder convert(BlobHolder blobHolder, Map<String, Serializable> parameters) throws ConversionException {
067
068        String dataContainer = "data" + String.valueOf(Calendar.getInstance().getTime().getTime());
069        String convertContainer = "convert" + String.valueOf(Calendar.getInstance().getTime().getTime());
070        String commandName = getCommandName(blobHolder, parameters);
071        if (commandName == null) {
072            throw new ConversionException("Unable to determine target CommandLine name");
073        }
074
075        CmdParameters params = new CmdParameters();
076        List<String> filesToDelete = new ArrayList<>();
077
078        try {
079            Path directory = new Path(getTmpDirectory(parameters)).append(
080                    DAE2GLTF_PATH_PREFIX + UUID.randomUUID() + "_in");
081            boolean dirCreated = new File(directory.toString()).mkdirs();
082            if (!dirCreated) {
083                throw new ConversionException("Unable to create tmp dir: " + directory);
084            }
085
086            List<Blob> blobs = blobHolder.getBlobs();
087            // Add the main and assets as params
088            // The params are not used by the command but the blobs are extracted to files and managed
089            filesToDelete = blobs.stream().map(blob -> {
090                File file = new File(directory.append(blob.getFilename()).toString());
091                try {
092                    blob.transferTo(file);
093                } catch (IOException e) {
094                    throw new UncheckedIOException(e);
095                }
096                return file.getAbsolutePath();
097            }).collect(Collectors.toList());
098            Path inputFile = new Path(filesToDelete.get(0));
099            params.addNamedParameter(INPUT_FILE_PARAMETER, inputFile.lastSegment());
100
101            ExecResult createRes = DockerHelper.CreateContainer(dataContainer, "nuxeo/collada2gltf");
102            if (createRes == null || !createRes.isSuccessful()) {
103                throw new ConversionException("Unable to create data volume : " + dataContainer,
104                        (createRes != null) ? createRes.getError() : null);
105            }
106            ExecResult copyRes = DockerHelper.CopyData(
107                    inputFile.removeLastSegments(1).toString() + File.separatorChar + ".", dataContainer + ":/in/");
108            if (copyRes == null || !copyRes.isSuccessful()) {
109                throw new ConversionException("Unable to copy to data volume : " + dataContainer,
110                        (copyRes != null) ? copyRes.getError() : null);
111            }
112            params.addNamedParameter(NAME_PARAM, convertContainer);
113            params.addNamedParameter(DATA_PARAM, dataContainer);
114            filesToDelete.add(directory.toString());
115
116            String baseDir = getTmpDirectory(parameters);
117            Path outPath = new Path(baseDir).append(DAE2GLTF_OUTPUT_PATH_PREFIX + UUID.randomUUID() + "_out");
118            File outDir = new File(outPath.toString());
119            dirCreated = outDir.mkdir();
120            if (!dirCreated) {
121                throw new ConversionException("Unable to create tmp dir for transformer output: " + outDir);
122            }
123            params.addNamedParameter(OUTPUT_FILE_PARAMETER, inputFile.removeFileExtension().lastSegment() + ".gltf");
124            params.addNamedParameter(OUT_DIR_PARAMETER, outPath.toString());
125            // params.addNamedParameter(USER_ID_PARAMETER, UserIdHelper.getUid());
126
127            ExecResult result = Framework.getService(CommandLineExecutorService.class).execCommand(commandName, params);
128            if (!result.isSuccessful()) {
129                throw result.getError();
130            }
131
132            copyRes = DockerHelper.CopyData(dataContainer + ":/out/.", outPath.toString());
133            if (copyRes == null || !copyRes.isSuccessful()) {
134                throw new ConversionException("Unable to copy from data volume : " + dataContainer,
135                        (copyRes != null) ? copyRes.getError() : null);
136            }
137            return buildResult(result.getOutput(), params);
138        } catch (CommandNotAvailable e) {
139            // XXX bubble installation instructions
140            throw new ConversionException("Unable to find targetCommand", e);
141        } catch (CommandException e) {
142            throw new ConversionException("Error while converting via CommandLineService", e);
143        } finally {
144            for (String fileToDelete : filesToDelete) {
145                FileUtils.deleteQuietly(new File(fileToDelete));
146            }
147            DockerHelper.RemoveContainer(dataContainer);
148            DockerHelper.RemoveContainer(convertContainer);
149        }
150    }
151
152    @Override
153    protected BlobHolder buildResult(List<String> cmdOutput, CmdParameters cmdParameters) throws ConversionException {
154        File outputFile = new File(cmdParameters.getParameter(OUT_DIR_PARAMETER) + File.separator
155                + cmdParameters.getParameter(OUTPUT_FILE_PARAMETER));
156        List<Blob> blobs = new ArrayList<>();
157        blobs.add(new FileBlob(outputFile));
158        Map<String, Serializable> properties = new HashMap<>();
159        properties.put("cmdOutput", (Serializable) cmdOutput);
160        return new SimpleBlobHolderWithProperties(blobs, properties);
161    }
162
163}