285 lines
10 KiB
Java
285 lines
10 KiB
Java
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you 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 .
|
|
*/
|
|
|
|
package org.openoffice.test;
|
|
|
|
import com.sun.star.bridge.UnoUrlResolver;
|
|
import com.sun.star.bridge.XUnoUrlResolver;
|
|
import com.sun.star.comp.helper.Bootstrap;
|
|
import com.sun.star.connection.NoConnectException;
|
|
import com.sun.star.frame.XDesktop;
|
|
import com.sun.star.lang.DisposedException;
|
|
import com.sun.star.lang.XMultiComponentFactory;
|
|
import com.sun.star.uno.UnoRuntime;
|
|
import com.sun.star.uno.XComponentContext;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.PrintStream;
|
|
import java.util.Map;
|
|
import java.util.UUID;
|
|
import static org.junit.Assert.*;
|
|
|
|
/** Start up and shut down an OOo instance.
|
|
|
|
Details about the OOo instance are tunneled in via
|
|
org.openoffice.test.arg... system properties.
|
|
*/
|
|
|
|
|
|
public final class OfficeConnection {
|
|
/** Start up an OOo instance.
|
|
*/
|
|
public void setUp() throws Exception {
|
|
String sofficeArg = Argument.get("soffice");
|
|
if (sofficeArg.startsWith("path:")) {
|
|
description = "pipe,name=oootest" + UUID.randomUUID();
|
|
ProcessBuilder pb = new ProcessBuilder(
|
|
sofficeArg.substring("path:".length()), "--quickstart=no",
|
|
"--norestore", "--nologo", "--headless",
|
|
"--accept=" + description + ";urp",
|
|
"-env:UserInstallation=" + Argument.get("user"),
|
|
"-env:UNO_JAVA_JFW_ENV_JREHOME=true");
|
|
String workdirArg = Argument.get("workdir");
|
|
if (workdirArg != null) {
|
|
pb.directory(new File(workdirArg));
|
|
}
|
|
String envArg = Argument.get("env");
|
|
if (envArg != null) {
|
|
Map<String, String> env = pb.environment();
|
|
int i = envArg.indexOf('=');
|
|
if (i == -1) {
|
|
env.remove(envArg);
|
|
} else {
|
|
env.put(envArg.substring(0, i), envArg.substring(i + 1));
|
|
}
|
|
}
|
|
process = pb.start();
|
|
outForward = new Forward(process.getInputStream(), System.out);
|
|
outForward.start();
|
|
errForward = new Forward(process.getErrorStream(), System.err);
|
|
errForward.start();
|
|
} else if (sofficeArg.startsWith("connect:")) {
|
|
description = sofficeArg.substring("connect:".length());
|
|
} else {
|
|
fail(
|
|
"\"soffice\" argument \"" + sofficeArg +
|
|
" starts with neither \"path:\" nor \"connect:\"");
|
|
}
|
|
XUnoUrlResolver resolver = UnoUrlResolver.create(
|
|
Bootstrap.createInitialComponentContext(null));
|
|
for (;;) {
|
|
try {
|
|
context = UnoRuntime.queryInterface(
|
|
XComponentContext.class,
|
|
resolver.resolve(
|
|
"uno:" + description +
|
|
";urp;StarOffice.ComponentContext"));
|
|
break;
|
|
} catch (NoConnectException e) {}
|
|
if (process != null) {
|
|
assertNull(waitForProcess(process, 1000)); // 1 sec
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Shut down the OOo instance.
|
|
*/
|
|
public void tearDown()
|
|
throws InterruptedException, com.sun.star.uno.Exception
|
|
{
|
|
boolean cleanTermination = false;
|
|
int code = 0;
|
|
try {
|
|
if (process != null) {
|
|
if (context != null) {
|
|
XDesktop desktop = null;
|
|
try {
|
|
XMultiComponentFactory factory =
|
|
context.getServiceManager();
|
|
assertNotNull(factory);
|
|
desktop = UnoRuntime.queryInterface(XDesktop.class,
|
|
factory.createInstanceWithContext(
|
|
"com.sun.star.frame.Desktop", context));
|
|
} catch (DisposedException e) {
|
|
// it can happen that the Java bridge was disposed
|
|
// already, we want to ensure soffice.bin is killed
|
|
process.destroy();
|
|
}
|
|
context = null;
|
|
if (desktop != null) {
|
|
try {
|
|
boolean desktopTerminated = desktop.terminate();
|
|
if (!desktopTerminated) {
|
|
// in case terminate() fails we would wait
|
|
// forever for the process to die, so kill it
|
|
process.destroy();
|
|
}
|
|
assertTrue(desktopTerminated);
|
|
} catch (DisposedException e) {}
|
|
// it appears that DisposedExceptions can already happen
|
|
// while receiving the response of the terminate call
|
|
}
|
|
desktop = null;
|
|
} else {
|
|
process.destroy();
|
|
}
|
|
}
|
|
if (process != null) {
|
|
code = process.waitFor();
|
|
}
|
|
boolean outTerminated = outForward == null
|
|
|| outForward.terminated();
|
|
boolean errTerminated = errForward == null
|
|
|| errForward.terminated();
|
|
assertEquals(0, code);
|
|
cleanTermination = true;
|
|
assertTrue(outTerminated);
|
|
assertTrue(errTerminated);
|
|
} finally {
|
|
if (!cleanTermination) {
|
|
try {
|
|
String sofficeArg = Argument.get("soffice");
|
|
String workdir = Argument.get("workdir");
|
|
String postprocesscommand = Argument.get(
|
|
"postprocesscommand");
|
|
if (sofficeArg.startsWith("path:") && workdir != null
|
|
&& postprocesscommand != null)
|
|
{
|
|
ProcessBuilder pb = new ProcessBuilder(
|
|
postprocesscommand,
|
|
sofficeArg.substring("path:".length()) + ".bin",
|
|
workdir, String.valueOf(code));
|
|
Process postprocess = pb.start();
|
|
Forward ppoutForward = new Forward(
|
|
postprocess.getInputStream(), System.out);
|
|
ppoutForward.start();
|
|
Forward pperrForward = new Forward(
|
|
postprocess.getErrorStream(), System.err);
|
|
pperrForward.start();
|
|
code = postprocess.waitFor();
|
|
if (code != 0) {
|
|
throw new PostprocessFailedException(code);
|
|
}
|
|
}
|
|
}
|
|
catch (IOException e) {
|
|
throw new PostprocessFailedException(e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Obtain the component context of the running OOo instance.
|
|
*/
|
|
public XComponentContext getComponentContext() {
|
|
return context;
|
|
}
|
|
|
|
//TODO: get rid of this hack for legacy qa/unoapi tests
|
|
public String getDescription() {
|
|
return description;
|
|
}
|
|
|
|
private static Integer waitForProcess(Process process, final int millis)
|
|
throws InterruptedException
|
|
{
|
|
final Thread t1 = Thread.currentThread();
|
|
Thread t2 = new Thread("waitForProcess") {
|
|
@Override
|
|
public void run() {
|
|
util.utils.pause(millis);
|
|
t1.interrupt();
|
|
}
|
|
};
|
|
boolean old = Thread.interrupted();
|
|
// clear interrupted status, get old status
|
|
t2.start();
|
|
int n = 0;
|
|
boolean done = false;
|
|
try {
|
|
n = process.waitFor();
|
|
done = true;
|
|
} catch (InterruptedException e) {}
|
|
t2.interrupt();
|
|
try {
|
|
t2.join();
|
|
} catch (InterruptedException e) {
|
|
t2.join();
|
|
}
|
|
Thread.interrupted(); // clear interrupted status
|
|
if (old) {
|
|
t1.interrupt(); // reset old status
|
|
}
|
|
return done ? Integer.valueOf(n) : null;
|
|
}
|
|
|
|
private static final class Forward extends Thread {
|
|
public Forward(InputStream in, PrintStream out) {
|
|
super("process output forwarder");
|
|
this.in = in;
|
|
this.out = out;
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
for (;;) {
|
|
byte[] buf = new byte[1024];
|
|
int n;
|
|
try {
|
|
n = in.read(buf);
|
|
} catch (IOException e) {
|
|
throw new RuntimeException("wrapping", e);
|
|
}
|
|
if (n == -1) {
|
|
break;
|
|
}
|
|
out.write(buf, 0, n);
|
|
}
|
|
done = true;
|
|
}
|
|
|
|
public boolean terminated() throws InterruptedException {
|
|
join();
|
|
return done;
|
|
}
|
|
|
|
private final InputStream in;
|
|
private final PrintStream out;
|
|
private boolean done = false;
|
|
}
|
|
|
|
private static final class PostprocessFailedException
|
|
extends RuntimeException
|
|
{
|
|
PostprocessFailedException(int exitCode) {
|
|
super("postprocessing failed with exit code " + exitCode);
|
|
}
|
|
|
|
PostprocessFailedException(IOException cause) {
|
|
super("postprocessing failed with IOException " + cause, cause);
|
|
}
|
|
}
|
|
|
|
private String description;
|
|
private Process process = null;
|
|
private Forward outForward = null;
|
|
private Forward errForward = null;
|
|
private XComponentContext context = null;
|
|
}
|
|
// vim:set et sw=4 sts=4:
|