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

org.jodconverter.office.ManagedOfficeProcess Maven / Gradle / Ivy

Go to download

Core JODConverter abstractions, used by JODConverter implementations, such as JODConverter Local or JODConverter Remote, used to convert office documents using LibreOffice or Apache OpenOffice.

There is a newer version: 4.4.8
Show newest version
/*
 * Copyright 2004 - 2012 Mirko Nasato and contributors
 *           2016 - 2017 Simon Braconnier and contributors
 *
 * This file is part of JODConverter - Java OpenDocument Converter.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jodconverter.office;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.star.lang.DisposedException;

/**
 * A ManagedOfficeProcess is responsible to manage an office process and the connection (bridge) to
 * this office process.
 *
 * @see OfficeProcess
 * @see OfficeConnection
 */
class ManagedOfficeProcess {

  private static final Logger logger = LoggerFactory.getLogger(ManagedOfficeProcess.class);

  private final OfficeProcess process;
  private final OfficeConnection connection;
  private final ManagedOfficeProcessSettings settings;
  private final ExecutorService executor;

  /**
   * Creates a new instance of the class with the specified settings.
   *
   * @param settings the managed office process settings.
   */
  public ManagedOfficeProcess(final ManagedOfficeProcessSettings settings) {

    this.settings = settings;
    process =
        new OfficeProcess(
            settings.getOfficeHome(),
            settings.getUnoUrl(),
            settings.getRunAsArgs(),
            settings.getTemplateProfileDir(),
            settings.getWorkingDir(),
            settings.getProcessManager(),
            settings.isKillExistingProcess());
    connection = new OfficeConnection(settings.getUnoUrl());
    executor = Executors.newSingleThreadExecutor(new NamedThreadFactory("OfficeProcessThread"));
  }

  /**
   * Ensures that the process exited.
   *
   * @throws OfficeException if an exception occurs.
   */
  private void doEnsureProcessExited() throws OfficeException {
    logger.trace("> doEnsureProcessExited");

    try {
      final int exitCode =
          process.getExitCode(settings.getRetryInterval(), settings.getRetryTimeout());
      logger.info("process exited with code " + exitCode);

    } catch (RetryTimeoutException retryTimeoutEx) {

      logger.debug("doEnsureProcessExited times out", retryTimeoutEx);
      doTerminateProcess();

    } finally {
      process.deleteProfileDir();
      logger.trace("< doEnsureProcessExited");
    }
  }

  /** Starts the office process managed by this class and connect to the process. */
  private void doStartProcessAndConnect() throws OfficeException {
    logger.trace("> doStartProcessAndConnect");

    try {
      process.start();
      new ConnectRetryable(process, connection)
          .execute(settings.getRetryInterval(), settings.getRetryTimeout());

    } catch (Exception ex) {
      throw new OfficeException("Could not establish connection", ex);

    } finally {
      logger.trace("< doStartProcessAndConnect");
    }
  }

  /** Stops the office process managed by this class. */
  private void doStopProcess() throws OfficeException {
    logger.trace("> doStopProcess");

    try {
      boolean terminated = connection.getDesktop().terminate();

      // once more: try to terminate
      logger.debug(
          "The Office Process {}",
          terminated
              ? "has been terminated"
              : "is still running. Someone else prevents termination, e.g. the quickstarter");

    } catch (DisposedException disposedEx) {
      // expected so ignore it
      logger.debug("DisposedException catched and ignored in doStopProcess", disposedEx);

    } catch (Exception ex) {
      logger.debug("Exception catched in doStopProcess", ex);
      // in case we can't get hold of the desktop
      doTerminateProcess();

    } finally {
      doEnsureProcessExited();
      logger.trace("< doStopProcess");
    }
  }

  /**
   * Ensures that the process exited.
   *
   * @throws OfficeException if an exception occurs.
   */
  private void doTerminateProcess() throws OfficeException {
    logger.trace("> doTerminateProcess");

    try {
      final int exitCode =
          process.forciblyTerminate(settings.getRetryInterval(), settings.getRetryTimeout());
      logger.info("process forcibly terminated with code " + exitCode);

    } catch (Exception ex) {
      throw new OfficeException("Could not terminate process", ex);

    } finally {
      logger.trace("< doTerminateProcess");
    }
  }

  // Executes the specified task without waiting for the completion of the task
  private void execute(final String taskName, final Runnable task) {
    logger.trace("> execute - '{}'", taskName);

    logger.info("Executing task '{}'...", taskName);
    executor.execute(task);

    logger.trace("< execute - '{}'", taskName);
  }

  /**
   * Gets the connection of this ManagedOfficeProcess.
   *
   * @return the {@link OfficeConnection} of this ManagedOfficeProcess.
   */
  public OfficeConnection getConnection() {
    return connection;
  }

  /**
   * Gets the process of this ManagedOfficeProcess.
   *
   * @return the {@link OfficeProcess} of this ManagedOfficeProcess.
   */
  public OfficeProcess getOfficeProcess() {
    return process;
  }

  /** Gets whether the connection to the office instance is opened. */
  public boolean isConnected() {
    return connection.isConnected();
  }

  /**
   * Restarts an office process and wait until we are connected to the retarted process.
   *
   * @throws OfficeException if we are not able to restart the office process.
   */
  public void restartAndWait() throws OfficeException {

    // Create the restart task to be execute
    final Callable restartTask =
        new Callable() {

          @Override
          public Void call() throws Exception {
            doStopProcess();
            doStartProcessAndConnect();

            return null;
          }
        };

    // Submit the task to the executor and wait
    submitAndWait("Restart", restartTask);
  }

  /** Restarts the office process when the connection is lost. */
  public void restartDueToLostConnection() {

    // Create the restart task to be execute
    final Runnable restartTask =
        new Runnable() {

          @Override
          public void run() {

            try {
              doEnsureProcessExited();
              doStartProcessAndConnect();

            } catch (OfficeException officeEx) {
              logger.error("Could not restart process", officeEx);
            }
          }
        };

    // Execute the task
    execute("Restart After Lost Connection", restartTask);
  }

  /** Restarts the office process when there is a timeout while executing a task. */
  public void restartDueToTaskTimeout() {

    // Create the restart task to be execute
    final Runnable restartTask =
        new Runnable() {

          @Override
          public void run() {

            try {
              doTerminateProcess();
              // will cause unexpected disconnection and subsequent restart

            } catch (OfficeException officeException) {
              logger.error("Could not restart process", officeException);
            }
          }
        };

    // Execute the task
    execute("Restart After Timeout", restartTask);
  }

  /**
   * Starts an office process and wait until we are connected to the running process.
   *
   * @throws OfficeException if we are not able to start and connect to the office process.
   */
  public void startAndWait() throws OfficeException {

    // Create the start task to be execute
    final Callable startTask =
        new Callable() {

          @Override
          public Void call() throws Exception {
            doStartProcessAndConnect();

            return null;
          }
        };

    // Submit the task to the executor and wait
    submitAndWait("Start", startTask);
  }

  /**
   * Stop an office process and wait until the process is stopped.
   *
   * @throws OfficeException if we are not able to stop the office process.
   */
  public void stopAndWait() throws OfficeException {

    // Create the stop task to be execute
    final Callable stopTask =
        new Callable() {

          @Override
          public Void call() throws Exception {
            doStopProcess();

            return null;
          }
        };

    // Submit the task to the executor and wait
    submitAndWait("Stop", stopTask);
  }

  // Submits the specified task to the executor and waits for its completion
  private void submitAndWait(final String taskName, final Callable task)
      throws OfficeException {
    logger.trace("> submitAndWait - '{}'", taskName);

    logger.info("Submitting task '{}' and waiting...", taskName);
    final Future future = executor.submit(task);

    // Wait for completion of the restart task
    try {
      future.get();
      logger.debug("Task '{}' executed successfully", taskName);

    } catch (ExecutionException executionEx) {
      logger.debug("ExecutionException catched in submitAndWait", executionEx);

      // Rethrow the original (cause) exception
      if (executionEx.getCause() instanceof OfficeException) {
        throw (OfficeException) executionEx.getCause();
      }
      throw new OfficeException(
          "Failed to execute task '" + taskName + "'", executionEx.getCause());

    } catch (InterruptedException interruptedEx) {
      Thread.currentThread().interrupt(); // ignore/reset

    } finally {
      logger.trace("< submitAndWait - '{}'", taskName);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy