public void executeTask() throws Exception {

    final PooledOfficeManagerSettings settings = new PooledOfficeManagerSettings(CONNECTION_MODE);
    settings.setProcessManager(OfficeUtils.findBestProcessManager());
    final PooledOfficeManager officeManager = new PooledOfficeManager(settings);
    ManagedOfficeProcess managedOfficeProcess =
        (ManagedOfficeProcess)
            FieldUtils.readDeclaredField(officeManager, "managedOfficeProcess", true);
    OfficeProcess process =
        (OfficeProcess) FieldUtils.readDeclaredField(managedOfficeProcess, "process", true);
    OfficeConnection connection =
        (OfficeConnection) FieldUtils.readDeclaredField(managedOfficeProcess, "connection", true);

    try {
      officeManager.start();
      assertTrue(process.isRunning());
      assertTrue(connection.isConnected());

      MockOfficeTask task = new MockOfficeTask();
      officeManager.execute(task);
      assertTrue(task.isCompleted());

    } finally {
      officeManager.stop();
      assertFalse(connection.isConnected());
      assertFalse(process.isRunning());
      assertEquals(process.getExitCode(0, 0), 0);
    }
  }
 public void start(boolean restart) throws IOException {
   ProcessQuery processQuery = new ProcessQuery("soffice.bin", unoUrl.getAcceptString());
   long existingPid = processManager.findPid(processQuery);
   if (!(existingPid == PID_NOT_FOUND || 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());
   command.add("-accept=" + unoUrl.getAcceptString() + ";urp;");
   command.add("-env:UserInstallation=" + OfficeUtils.toUrl(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()) {
     addBasisAndUrePaths(processBuilder);
   }
   logger.info(
       String.format(
           "starting process with acceptString '%s' and profileDir '%s'",
           unoUrl, instanceProfileDir));
   process = processBuilder.start();
   pid = processManager.findPid(processQuery);
   if (pid == PID_NOT_FOUND) {
     throw new IllegalStateException(
         String.format(
             "process with acceptString '%s' started but its pid could not be found",
             unoUrl.getAcceptString()));
   }
   logger.info("started process" + (pid != PID_UNKNOWN ? "; pid = " + pid : ""));
 }
  public void restartAfterCrash() throws Exception {

    final PooledOfficeManagerSettings settings = new PooledOfficeManagerSettings(CONNECTION_MODE);
    settings.setProcessManager(OfficeUtils.findBestProcessManager());
    final PooledOfficeManager officeManager = new PooledOfficeManager(settings);
    ManagedOfficeProcess managedOfficeProcess =
        (ManagedOfficeProcess)
            FieldUtils.readDeclaredField(officeManager, "managedOfficeProcess", true);
    OfficeProcess process =
        (OfficeProcess) FieldUtils.readDeclaredField(managedOfficeProcess, "process", true);
    OfficeConnection connection =
        (OfficeConnection) FieldUtils.readDeclaredField(managedOfficeProcess, "connection", true);
    assertNotNull(connection);

    try {
      officeManager.start();
      assertTrue(process.isRunning());
      assertTrue(connection.isConnected());

      new Thread() {
        public void run() {
          MockOfficeTask badTask = new MockOfficeTask(10 * 1000);
          try {
            officeManager.execute(badTask);
            fail("task should be cancelled");
            // FIXME being in a separate thread the test won't actually fail
          } catch (OfficeException officeEx) {
            assertTrue(officeEx.getCause() instanceof CancellationException);
          }
        }
      }.start();
      Thread.sleep(500);
      Process underlyingProcess = (Process) FieldUtils.readDeclaredField(process, "process", true);
      assertNotNull(underlyingProcess);
      logger.debug("Simulating the crash");
      underlyingProcess.destroy(); // simulate crash

      Thread.sleep(RESTART_WAIT_TIME);
      assertTrue(process.isRunning());
      assertTrue(connection.isConnected());

      MockOfficeTask goodTask = new MockOfficeTask();
      officeManager.execute(goodTask);
      assertTrue(goodTask.isCompleted());

    } finally {
      officeManager.stop();
      assertFalse(connection.isConnected());
      assertFalse(process.isRunning());
      assertEquals(process.getExitCode(0, 0), 0);
    }
  }
  public void restartAfterTaskTimeout() throws Exception {
    final PooledOfficeManagerSettings settings = new PooledOfficeManagerSettings(CONNECTION_MODE);
    settings.setProcessManager(OfficeUtils.findBestProcessManager());
    settings.setTaskExecutionTimeout(1500L);
    final PooledOfficeManager officeManager = new PooledOfficeManager(settings);

    ManagedOfficeProcess managedOfficeProcess =
        (ManagedOfficeProcess)
            FieldUtils.readDeclaredField(officeManager, "managedOfficeProcess", true);
    OfficeProcess process =
        (OfficeProcess) FieldUtils.readDeclaredField(managedOfficeProcess, "process", true);
    OfficeConnection connection =
        (OfficeConnection) FieldUtils.readDeclaredField(managedOfficeProcess, "connection", true);
    assertNotNull(connection);

    try {
      officeManager.start();
      assertTrue(process.isRunning());
      assertTrue(connection.isConnected());

      MockOfficeTask task = new MockOfficeTask(2000);
      try {
        officeManager.execute(task);
        fail("task should be timed out");
      } catch (OfficeException officeEx) {
        assertTrue(officeEx.getCause() instanceof TimeoutException);
      }

      Thread.sleep(RESTART_WAIT_TIME);

      assertTrue(process.isRunning());
      assertTrue(connection.isConnected());

      MockOfficeTask goodTask = new MockOfficeTask();
      officeManager.execute(goodTask);
      assertTrue(goodTask.isCompleted());
    } finally {

      officeManager.stop();
      assertFalse(connection.isConnected());
      assertFalse(process.isRunning());
      assertEquals(process.getExitCode(0, 0), 0);
    }
  }
  public OfficeManager buildOfficeManager() throws IllegalStateException {
    if (officeHome == null)
      throw new IllegalStateException("officeHome not set and could not be auto-detected");
    else if (!officeHome.isDirectory())
      throw new IllegalStateException(
          "officeHome doesn't exist or is not a directory: " + officeHome);
    else if (!OfficeUtils.getOfficeExecutable(officeHome).isFile())
      throw new IllegalStateException(
          "invalid officeHome: it doesn't contain soffice.bin: " + officeHome);
    if (templateProfileDir != null && !isValidProfileDir(templateProfileDir))
      throw new IllegalStateException(
          "templateProfileDir doesn't appear to contain a user profile: " + templateProfileDir);
    if (!workDir.isDirectory())
      throw new IllegalStateException("workDir doesn't exist or is not a directory: " + workDir);

    if (processManager == null) processManager = findBestProcessManager();

    final int numInstances =
        connectionProtocol == OfficeConnectionProtocol.PIPE ? pipeNames.length : portNumbers.length;
    final UnoUrl[] unoUrls = new UnoUrl[numInstances];
    for (int i = 0; i < numInstances; i++)
      unoUrls[i] =
          connectionProtocol == OfficeConnectionProtocol.PIPE
              ? UnoUrl.pipe(pipeNames[i])
              : UnoUrl.socket(portNumbers[i]);
    return new ProcessPoolOfficeManager(
        officeHome,
        unoUrls,
        runAsArgs,
        templateProfileDir,
        workDir,
        retryTimeout,
        taskQueueTimeout,
        taskExecutionTimeout,
        maxTasksPerProcess,
        processManager,
        manageExternalInstances);
  }
public class DefaultOfficeManagerConfiguration {

  public static final long DEFAULT_RETRY_TIMEOUT = 120000L;

  private File officeHome = OfficeUtils.getDefaultOfficeHome();
  private OfficeConnectionProtocol connectionProtocol = OfficeConnectionProtocol.SOCKET;
  private int[] portNumbers = new int[] {2002};
  private String[] pipeNames = new String[] {"office"};
  private String[] runAsArgs = null;
  private File templateProfileDir = null;
  private File workDir = new File(System.getProperty("java.io.tmpdir"));
  private long taskQueueTimeout = 30000L; // 30 seconds
  private long taskExecutionTimeout = 120000L; // 2 minutes
  private int maxTasksPerProcess = 200;
  private long retryTimeout = DEFAULT_RETRY_TIMEOUT;

  private ProcessManager processManager = null; // lazily initialised

  private boolean manageExternalInstances;

  public DefaultOfficeManagerConfiguration setOfficeHome(final String officeHome)
      throws NullPointerException, IllegalArgumentException {
    checkArgumentNotNull("officeHome", officeHome);
    return setOfficeHome(new File(officeHome));
  }

  public DefaultOfficeManagerConfiguration setOfficeHome(final File officeHome)
      throws NullPointerException, IllegalArgumentException {
    checkArgumentNotNull("officeHome", officeHome);
    checkArgument("officeHome", officeHome.isDirectory(), "must exist and be a directory");
    this.officeHome = officeHome;
    return this;
  }

  public DefaultOfficeManagerConfiguration setConnectionProtocol(
      final OfficeConnectionProtocol connectionProtocol) throws NullPointerException {
    checkArgumentNotNull("connectionProtocol", connectionProtocol);
    this.connectionProtocol = connectionProtocol;
    return this;
  }

  public DefaultOfficeManagerConfiguration setPortNumber(final int portNumber) {
    portNumbers = new int[] {portNumber};
    return this;
  }

  public DefaultOfficeManagerConfiguration setPortNumbers(final int... portNumbers)
      throws NullPointerException, IllegalArgumentException {
    checkArgumentNotNull("portNumbers", portNumbers);
    checkArgument("portNumbers", portNumbers.length > 0, "must not be empty");
    this.portNumbers = portNumbers;
    return this;
  }

  public DefaultOfficeManagerConfiguration setPipeName(final String pipeName)
      throws NullPointerException {
    checkArgumentNotNull("pipeName", pipeName);
    pipeNames = new String[] {pipeName};
    return this;
  }

  public DefaultOfficeManagerConfiguration setPipeNames(final String... pipeNames)
      throws NullPointerException, IllegalArgumentException {
    checkArgumentNotNull("pipeNames", pipeNames);
    checkArgument("pipeNames", pipeNames.length > 0, "must not be empty");
    this.pipeNames = pipeNames;
    return this;
  }

  public DefaultOfficeManagerConfiguration setRunAsArgs(final String... runAsArgs) {
    this.runAsArgs = runAsArgs;
    return this;
  }

  public DefaultOfficeManagerConfiguration setTemplateProfileDir(final File templateProfileDir)
      throws IllegalArgumentException {
    if (templateProfileDir != null)
      checkArgument(
          "templateProfileDir", templateProfileDir.isDirectory(), "must exist and be a directory");
    this.templateProfileDir = templateProfileDir;
    return this;
  }

  /**
   * Sets the directory where temporary office profiles will be created.
   *
   * <p>Defaults to the system temporary directory as specified by the <code>java.io.tmpdir</code>
   * system property.
   *
   * @param workDir
   * @return
   */
  public DefaultOfficeManagerConfiguration setWorkDir(final File workDir) {
    checkArgumentNotNull("workDir", workDir);
    this.workDir = workDir;
    return this;
  }

  public DefaultOfficeManagerConfiguration setTaskQueueTimeout(final long taskQueueTimeout) {
    this.taskQueueTimeout = taskQueueTimeout;
    return this;
  }

  public DefaultOfficeManagerConfiguration setTaskExecutionTimeout(
      final long taskExecutionTimeout) {
    this.taskExecutionTimeout = taskExecutionTimeout;
    return this;
  }

  public DefaultOfficeManagerConfiguration setMaxTasksPerProcess(final int maxTasksPerProcess) {
    this.maxTasksPerProcess = maxTasksPerProcess;
    return this;
  }

  /**
   * Provide a specific {@link ProcessManager} implementation
   *
   * <p>The default is to use {@link SigarProcessManager} if sigar.jar is available in the
   * classpath, otherwise {@link LinuxProcessManager} on Linux and {@link PureJavaProcessManager} on
   * other platforms.
   *
   * @param processManager
   * @return
   * @throws NullPointerException
   */
  public DefaultOfficeManagerConfiguration setProcessManager(final ProcessManager processManager)
      throws NullPointerException {
    checkArgumentNotNull("processManager", processManager);
    this.processManager = processManager;
    return this;
  }

  /**
   * Retry timeout set in milliseconds. Used for retrying office process calls. If not set, it
   * defaults to 2 minutes
   *
   * @param retryTimeout in milliseconds
   * @return
   */
  public DefaultOfficeManagerConfiguration setRetryTimeout(final long retryTimeout) {
    this.retryTimeout = retryTimeout;
    return this;
  }

  public void setManageExternalInstances(final boolean value) {
    manageExternalInstances = value;
  }

  public OfficeManager buildOfficeManager() throws IllegalStateException {
    if (officeHome == null)
      throw new IllegalStateException("officeHome not set and could not be auto-detected");
    else if (!officeHome.isDirectory())
      throw new IllegalStateException(
          "officeHome doesn't exist or is not a directory: " + officeHome);
    else if (!OfficeUtils.getOfficeExecutable(officeHome).isFile())
      throw new IllegalStateException(
          "invalid officeHome: it doesn't contain soffice.bin: " + officeHome);
    if (templateProfileDir != null && !isValidProfileDir(templateProfileDir))
      throw new IllegalStateException(
          "templateProfileDir doesn't appear to contain a user profile: " + templateProfileDir);
    if (!workDir.isDirectory())
      throw new IllegalStateException("workDir doesn't exist or is not a directory: " + workDir);

    if (processManager == null) processManager = findBestProcessManager();

    final int numInstances =
        connectionProtocol == OfficeConnectionProtocol.PIPE ? pipeNames.length : portNumbers.length;
    final UnoUrl[] unoUrls = new UnoUrl[numInstances];
    for (int i = 0; i < numInstances; i++)
      unoUrls[i] =
          connectionProtocol == OfficeConnectionProtocol.PIPE
              ? UnoUrl.pipe(pipeNames[i])
              : UnoUrl.socket(portNumbers[i]);
    return new ProcessPoolOfficeManager(
        officeHome,
        unoUrls,
        runAsArgs,
        templateProfileDir,
        workDir,
        retryTimeout,
        taskQueueTimeout,
        taskExecutionTimeout,
        maxTasksPerProcess,
        processManager,
        manageExternalInstances);
  }

  private ProcessManager findBestProcessManager() {
    if (isSigarAvailable()) return new SigarProcessManager();
    else if (PlatformUtils.isLinux()) {
      final LinuxProcessManager processManager = new LinuxProcessManager();
      if (runAsArgs != null) processManager.setRunAsArgs(runAsArgs);
      return processManager;
    } else
      // NOTE: UnixProcessManager can't be trusted to work on Solaris
      // because of the 80-char limit on ps output there
      return new PureJavaProcessManager();
  }

  private boolean isSigarAvailable() {
    try {
      Class.forName("org.hyperic.sigar.Sigar", false, getClass().getClassLoader());
      return true;
    } catch (final ClassNotFoundException classNotFoundException) {
      return false;
    }
  }

  private void checkArgumentNotNull(final String argName, final Object argValue)
      throws NullPointerException {
    if (argValue == null) throw new NullPointerException(argName + " must not be null");
  }

  private void checkArgument(final String argName, final boolean condition, final String message)
      throws IllegalArgumentException {
    if (!condition) throw new IllegalArgumentException(argName + " " + message);
  }

  private boolean isValidProfileDir(final File profileDir) {
    return new File(profileDir, "user").isDirectory();
  }
}