1
0
Fork 0
libreoffice/unotest/source/java/org/openoffice/test/OfficeConnection.java
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

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: