2 * This file is part of the LibreOffice project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 * This file incorporates work covered by the following license notice:
10 * Licensed to the Apache Software Foundation (ASF) under one or more
11 * contributor license agreements. See the NOTICE file distributed
12 * with this work for additional information regarding copyright
13 * ownership. The ASF licenses this file to you under the Apache
14 * License, Version 2.0 (the "License"); you may not use this file
15 * except in compliance with the License. You may obtain a copy of
16 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 package org
.openoffice
.test
;
21 import com
.sun
.star
.bridge
.UnoUrlResolver
;
22 import com
.sun
.star
.bridge
.XUnoUrlResolver
;
23 import com
.sun
.star
.comp
.helper
.Bootstrap
;
24 import com
.sun
.star
.connection
.NoConnectException
;
25 import com
.sun
.star
.frame
.XDesktop
;
26 import com
.sun
.star
.lang
.DisposedException
;
27 import com
.sun
.star
.lang
.XMultiComponentFactory
;
28 import com
.sun
.star
.uno
.UnoRuntime
;
29 import com
.sun
.star
.uno
.XComponentContext
;
31 import java
.io
.IOException
;
32 import java
.io
.InputStream
;
33 import java
.io
.PrintStream
;
35 import java
.util
.UUID
;
36 import static org
.junit
.Assert
.*;
38 /** Start up and shut down an OOo instance.
40 Details about the OOo instance are tunneled in via
41 org.openoffice.test.arg... system properties.
45 public final class OfficeConnection
{
46 /** Start up an OOo instance.
48 public void setUp() throws Exception
{
49 String sofficeArg
= Argument
.get("soffice");
50 if (sofficeArg
.startsWith("path:")) {
51 description
= "pipe,name=oootest" + UUID
.randomUUID();
52 ProcessBuilder pb
= new ProcessBuilder(
53 sofficeArg
.substring("path:".length()), "--quickstart=no",
54 "--norestore", "--nologo", "--headless",
55 "--accept=" + description
+ ";urp",
56 "-env:UserInstallation=" + Argument
.get("user"),
57 "-env:UNO_JAVA_JFW_ENV_JREHOME=true");
58 String workdirArg
= Argument
.get("workdir");
59 if (workdirArg
!= null) {
60 pb
.directory(new File(workdirArg
));
62 String envArg
= Argument
.get("env");
64 Map
<String
, String
> env
= pb
.environment();
65 int i
= envArg
.indexOf('=');
69 env
.put(envArg
.substring(0, i
), envArg
.substring(i
+ 1));
73 outForward
= new Forward(process
.getInputStream(), System
.out
);
75 errForward
= new Forward(process
.getErrorStream(), System
.err
);
77 } else if (sofficeArg
.startsWith("connect:")) {
78 description
= sofficeArg
.substring("connect:".length());
81 "\"soffice\" argument \"" + sofficeArg
+
82 " starts with neither \"path:\" nor \"connect:\"");
84 XUnoUrlResolver resolver
= UnoUrlResolver
.create(
85 Bootstrap
.createInitialComponentContext(null));
88 context
= UnoRuntime
.queryInterface(
89 XComponentContext
.class,
91 "uno:" + description
+
92 ";urp;StarOffice.ComponentContext"));
94 } catch (NoConnectException e
) {}
95 if (process
!= null) {
96 assertNull(waitForProcess(process
, 1000)); // 1 sec
101 /** Shut down the OOo instance.
103 public void tearDown()
104 throws InterruptedException
, com
.sun
.star
.uno
.Exception
106 boolean cleanTermination
= false;
109 if (process
!= null) {
110 if (context
!= null) {
111 XDesktop desktop
= null;
113 XMultiComponentFactory factory
=
114 context
.getServiceManager();
115 assertNotNull(factory
);
116 desktop
= UnoRuntime
.queryInterface(XDesktop
.class,
117 factory
.createInstanceWithContext(
118 "com.sun.star.frame.Desktop", context
));
119 } catch (DisposedException e
) {
120 // it can happen that the Java bridge was disposed
121 // already, we want to ensure soffice.bin is killed
125 if (desktop
!= null) {
127 boolean desktopTerminated
= desktop
.terminate();
128 if (!desktopTerminated
) {
129 // in case terminate() fails we would wait
130 // forever for the process to die, so kill it
133 assertTrue(desktopTerminated
);
134 } catch (DisposedException e
) {}
135 // it appears that DisposedExceptions can already happen
136 // while receiving the response of the terminate call
143 if (process
!= null) {
144 code
= process
.waitFor();
146 boolean outTerminated
= outForward
== null
147 || outForward
.terminated();
148 boolean errTerminated
= errForward
== null
149 || errForward
.terminated();
150 assertEquals(0, code
);
151 cleanTermination
= true;
152 assertTrue(outTerminated
);
153 assertTrue(errTerminated
);
155 if (!cleanTermination
) {
157 String sofficeArg
= Argument
.get("soffice");
158 String workdir
= Argument
.get("workdir");
159 String postprocesscommand
= Argument
.get(
160 "postprocesscommand");
161 if (sofficeArg
.startsWith("path:") && workdir
!= null
162 && postprocesscommand
!= null)
164 ProcessBuilder pb
= new ProcessBuilder(
166 sofficeArg
.substring("path:".length()) + ".bin",
167 workdir
, String
.valueOf(code
));
168 Process postprocess
= pb
.start();
169 Forward ppoutForward
= new Forward(
170 postprocess
.getInputStream(), System
.out
);
171 ppoutForward
.start();
172 Forward pperrForward
= new Forward(
173 postprocess
.getErrorStream(), System
.err
);
174 pperrForward
.start();
175 code
= postprocess
.waitFor();
177 throw new PostprocessFailedException(code
);
181 catch (IOException e
) {
182 throw new PostprocessFailedException(e
);
188 /** Obtain the component context of the running OOo instance.
190 public XComponentContext
getComponentContext() {
194 //TODO: get rid of this hack for legacy qa/unoapi tests
195 public String
getDescription() {
199 private static Integer
waitForProcess(Process process
, final int millis
)
200 throws InterruptedException
202 final Thread t1
= Thread
.currentThread();
203 Thread t2
= new Thread("waitForProcess") {
206 util
.utils
.pause(millis
);
210 boolean old
= Thread
.interrupted();
211 // clear interrupted status, get old status
214 boolean done
= false;
216 n
= process
.waitFor();
218 } catch (InterruptedException e
) {}
222 } catch (InterruptedException e
) {
225 Thread
.interrupted(); // clear interrupted status
227 t1
.interrupt(); // reset old status
229 return done ? Integer
.valueOf(n
) : null;
232 private static final class Forward
extends Thread
{
233 public Forward(InputStream in
, PrintStream out
) {
234 super("process output forwarder");
242 byte[] buf
= new byte[1024];
246 } catch (IOException e
) {
247 throw new RuntimeException("wrapping", e
);
252 out
.write(buf
, 0, n
);
257 public boolean terminated() throws InterruptedException
{
262 private final InputStream in
;
263 private final PrintStream out
;
264 private boolean done
= false;
267 private static final class PostprocessFailedException
268 extends RuntimeException
270 PostprocessFailedException(int exitCode
) {
271 super("postprocessing failed with exit code " + exitCode
);
274 PostprocessFailedException(IOException cause
) {
275 super("postprocessing failed with IOException " + cause
, cause
);
279 private String description
;
280 private Process process
= null;
281 private Forward outForward
= null;
282 private Forward errForward
= null;
283 private XComponentContext context
= null;
285 // vim:set et sw=4 sts=4: