bboss.org.artofsolving.jodconverter.office.OfficeProcess.java Source code

Java tutorial

Introduction

Here is the source code for bboss.org.artofsolving.jodconverter.office.OfficeProcess.java

Source

//
// JODConverter - Java OpenDocument Converter
// Copyright 2004-2011 Mirko Nasato and contributors
//
// JODConverter is free software: you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// JODConverter is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General
// Public License along with JODConverter.  If not, see
// <http://www.gnu.org/licenses/>.
//
package bboss.org.artofsolving.jodconverter.office;

import static bboss.org.artofsolving.jodconverter.process.ProcessManager.PID_UNKNOWN;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;

import bboss.org.artofsolving.jodconverter.process.ProcessManager;
import bboss.org.artofsolving.jodconverter.process.ProcessQuery;
import bboss.org.artofsolving.jodconverter.util.PlatformUtils;

class OfficeProcess {

    private final File officeHome;
    private final UnoUrl unoUrl;
    private final String[] runAsArgs;
    private final File templateProfileDir;
    private final File instanceProfileDir;
    private final ProcessManager processManager;

    private Process process;
    private long pid = PID_UNKNOWN;

    private final Logger logger = Logger.getLogger(OfficeProcess.class);

    public OfficeProcess(File officeHome, UnoUrl unoUrl, String[] runAsArgs, File templateProfileDir, File workDir,
            ProcessManager processManager) {
        this.officeHome = officeHome;
        this.unoUrl = unoUrl;
        this.runAsArgs = runAsArgs;
        this.templateProfileDir = templateProfileDir;
        this.instanceProfileDir = getInstanceProfileDir(workDir, unoUrl);
        this.processManager = processManager;
    }

    public void start() throws IOException {
        start(false);
    }

    public void start(boolean restart) throws IOException {
        ProcessQuery processQuery = new ProcessQuery("soffice.bin", unoUrl.getAcceptString());
        long existingPid = processManager.findPid(processQuery);
        if (existingPid != PID_UNKNOWN) {
            throw new IllegalStateException(
                    String.format("a process with acceptString '%s' is already running; pid %d",
                            unoUrl.getAcceptString(), existingPid));
        }
        if (!restart) {
            prepareInstanceProfileDir();
        }
        List<String> command = new ArrayList<String>();
        File executable = OfficeUtils.getOfficeExecutable(officeHome);
        if (runAsArgs != null) {
            command.addAll(Arrays.asList(runAsArgs));
        }
        command.add(executable.getAbsolutePath());
        if (!PlatformUtils.isWindows())
            command.add("-accept=" + unoUrl.getAcceptString() + ";urp;");
        else
            command.add("-accept=\"" + unoUrl.getAcceptString() + ";urp;\"");
        if (PlatformUtils.isWindows())
            command.add("-env:UserInstallation=\"" + OfficeUtils.toUrl(instanceProfileDir) + "\"");
        else
            command.add("-env:UserInstallation=" + OfficeUtils.toUrl(instanceProfileDir) + "");
        System.out.println("instanceProfileDir--------------:" + instanceProfileDir);
        command.add("-headless");
        command.add("-nocrashreport");
        command.add("-nodefault");
        command.add("-nofirststartwizard");
        command.add("-nolockcheck");
        command.add("-nologo");
        command.add("-norestore");
        ProcessBuilder processBuilder = new ProcessBuilder(command);
        if (PlatformUtils.isWindows()) {
            if (this.officeHome != null) {
                if (officeHome.getAbsolutePath().toLowerCase().contains("libre"))
                    addLiberofficeBasisAndUrePaths(processBuilder);
                else
                    addBasisAndUrePaths(processBuilder);
            } else
                addBasisAndUrePaths(processBuilder);

        }
        logger.info(String.format("starting process with acceptString '%s' and profileDir '%s'", unoUrl,
                instanceProfileDir));
        process = processBuilder.start();
        pid = processManager.findPid(processQuery);
        logger.info("started process" + (pid != PID_UNKNOWN ? "; pid = " + pid : ""));
    }

    private File getInstanceProfileDir(File workDir, UnoUrl unoUrl) {
        if (PlatformUtils.isWindows()) {
            String dirName = "libre.jodconverter_" + unoUrl.getAcceptString().replace(',', '_').replace('=', '-');
            return new File(workDir, dirName);
        } else {
            String dirName = ".jodconverter_" + unoUrl.getAcceptString().replace(',', '_').replace('=', '-');
            return new File(workDir, dirName);
        }
    }

    private void prepareInstanceProfileDir() throws OfficeException {
        if (instanceProfileDir.exists()) {
            logger.warn(String.format("profile dir '%s' already exists; deleting", instanceProfileDir));
            deleteProfileDir();
        }
        if (templateProfileDir != null) {
            try {
                FileUtils.copyDirectory(templateProfileDir, instanceProfileDir);
            } catch (IOException ioException) {
                throw new OfficeException("failed to create profileDir", ioException);
            }
        }
    }

    public void deleteProfileDir() {
        if (instanceProfileDir != null) {
            try {
                FileUtils.deleteDirectory(instanceProfileDir);
            } catch (IOException ioException) {
                File oldProfileDir = new File(instanceProfileDir.getParentFile(),
                        instanceProfileDir.getName() + ".old." + System.currentTimeMillis());
                if (instanceProfileDir.renameTo(oldProfileDir)) {
                    logger.warn("could not delete profileDir: " + ioException.getMessage() + "; renamed it to "
                            + oldProfileDir);
                } else {
                    logger.warn("could not delete profileDir: " + ioException.getMessage());
                }
            }
        }
    }

    private void addBasisAndUrePaths(ProcessBuilder processBuilder) throws IOException {
        // see http://wiki.services.openoffice.org/wiki/ODF_Toolkit/Efforts/Three-Layer_OOo
        File basisLink = new File(officeHome, "basis-link");
        if (!basisLink.isFile()) {
            logger.info(
                    "no %OFFICE_HOME%/basis-link found; assuming it's OOo 2.x and we don't need to append URE and Basic paths");
            return;
        }
        String basisLinkText = FileUtils.readFileToString(basisLink).trim();
        File basisHome = new File(officeHome, basisLinkText);
        File basisProgram = new File(basisHome, "program");
        File ureLink = new File(basisHome, "ure-link");
        String ureLinkText = FileUtils.readFileToString(ureLink).trim();
        File ureHome = new File(basisHome, ureLinkText);
        File ureBin = new File(ureHome, "bin");
        Map<String, String> environment = processBuilder.environment();
        // Windows environment variables are case insensitive but Java maps are not :-/
        // so let's make sure we modify the existing key
        String pathKey = "PATH";
        for (String key : environment.keySet()) {
            if ("PATH".equalsIgnoreCase(key)) {
                pathKey = key;
            }
        }
        String path = environment.get(pathKey) + ";" + ureBin.getAbsolutePath() + ";"
                + basisProgram.getAbsolutePath();
        logger.info(String.format("setting %s to \"%s\"", pathKey, path));
        environment.put(pathKey, path);
    }

    private void addLiberofficeBasisAndUrePaths(ProcessBuilder processBuilder) throws IOException {
        // see http://wiki.services.openoffice.org/wiki/ODF_Toolkit/Efforts/Three-Layer_OOo
        //        File basisLink = new File(officeHome, "basis-link");
        //        if (!basisLink.isFile()) {
        //            logger.fine("no %OFFICE_HOME%/basis-link found; assuming it's OOo 2.x and we don't need to append URE and Basic paths");
        //            return;
        //        }
        //        String basisLinkText = FileUtils.readFileToString(basisLink).trim();
        File basisHome = officeHome;
        File basisProgram = new File(basisHome, "program");
        File ureLink = new File(basisHome, "URE");
        //        String ureLinkText = FileUtils.readFileToString(ureLink).trim();
        //        File ureHome = new File(basisHome, ureLinkText);
        File ureBin = new File(ureLink, "bin");
        Map<String, String> environment = processBuilder.environment();
        // Windows environment variables are case insensitive but Java maps are not :-/
        // so let's make sure we modify the existing key
        String pathKey = "PATH";
        for (String key : environment.keySet()) {
            if ("PATH".equalsIgnoreCase(key)) {
                pathKey = key;
            }
        }
        String path = environment.get(pathKey) + ";" + ureBin.getAbsolutePath() + ";"
                + basisProgram.getAbsolutePath();
        logger.info(String.format("setting %s to \"%s\"", pathKey, path));
        environment.put(pathKey, path);
    }

    public boolean isRunning() {
        if (process == null) {
            return false;
        }
        return getExitCode() == null;
    }

    private class ExitCodeRetryable extends Retryable {

        private int exitCode;

        protected void attempt() throws TemporaryException, Exception {
            try {
                if (process == null)
                    return;
                exitCode = process.exitValue();
            } catch (IllegalThreadStateException illegalThreadStateException) {
                throw new TemporaryException(illegalThreadStateException);
            }
        }

        public int getExitCode() {
            return exitCode;
        }

    }

    public Integer getExitCode() {
        try {
            if (process == null)
                return ManagedOfficeProcess.EXIT_CODE_NEW_INSTALLATION;
            return process.exitValue();
        } catch (IllegalThreadStateException exception) {
            return null;
        }
    }

    public int getExitCode(long retryInterval, long retryTimeout) throws RetryTimeoutException {
        try {
            ExitCodeRetryable retryable = new ExitCodeRetryable();
            retryable.execute(retryInterval, retryTimeout);
            return retryable.getExitCode();
        } catch (RetryTimeoutException retryTimeoutException) {
            throw retryTimeoutException;
        } catch (Exception exception) {
            throw new OfficeException("could not get process exit code", exception);
        }
    }

    public int forciblyTerminate(long retryInterval, long retryTimeout) throws IOException, RetryTimeoutException {
        logger.info(String.format("trying to forcibly terminate process: '" + unoUrl + "'"
                + (pid != PID_UNKNOWN ? " (pid " + pid + ")" : "")));
        processManager.kill(process, pid);
        return getExitCode(retryInterval, retryTimeout);
    }

}