1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 package org.openoffice.test;
25 
26 import com.sun.star.bridge.UnoUrlResolver;
27 import com.sun.star.bridge.XUnoUrlResolver;
28 import com.sun.star.comp.helper.Bootstrap;
29 import com.sun.star.connection.NoConnectException;
30 import com.sun.star.frame.XDesktop;
31 import com.sun.star.lang.DisposedException;
32 import com.sun.star.lang.XMultiComponentFactory;
33 import com.sun.star.uno.UnoRuntime;
34 import com.sun.star.uno.XComponentContext;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.io.PrintStream;
38 import java.util.Map;
39 import java.util.UUID;
40 import static org.junit.Assert.*;
41 
42 /** Start up and shut down an OOo instance.
43 
44     Details about the OOo instance are tunneled in via
45     org.openoffice.test.arg.... system properties.
46 */
47 public final class OfficeConnection {
48     /** Start up an OOo instance.
49     */
setUp()50     public void setUp() throws Exception {
51         String sofficeArg = Argument.get("soffice");
52         if (sofficeArg.startsWith("path:")) {
53             description = "pipe,name=oootest" + UUID.randomUUID();
54             ProcessBuilder pb = new ProcessBuilder(
55                 sofficeArg.substring("path:".length()), "-quickstart=no",
56                 "-nofirststartwizard", "-norestore",
57                 "-accept=" + description + ";urp",
58                 "-env:UserInstallation=" + Argument.get("user"),
59                 "-env:UNO_JAVA_JFW_ENV_JREHOME=true");
60             String envArg = Argument.get("env");
61             if (envArg != null) {
62                 Map<String, String> env = pb.environment();
63                 int i = envArg.indexOf("=");
64                 if (i == -1) {
65                     env.remove(envArg);
66                 } else {
67                     env.put(envArg.substring(0, i), envArg.substring(i + 1));
68                 }
69             }
70             process = pb.start();
71             outForward = new Forward(process.getInputStream(), System.out);
72             outForward.start();
73             errForward = new Forward(process.getErrorStream(), System.err);
74             errForward.start();
75         } else if (sofficeArg.startsWith("connect:")) {
76             description = sofficeArg.substring("connect:".length());
77         } else {
78             fail(
79                 "\"soffice\" argument \"" + sofficeArg +
80                 " starts with neither \"path:\" nor \"connect:\"");
81         }
82         XUnoUrlResolver resolver = UnoUrlResolver.create(
83             Bootstrap.createInitialComponentContext(null));
84         for (;;) {
85             try {
86                 context = UnoRuntime.queryInterface(
87                     XComponentContext.class,
88                     resolver.resolve(
89                         "uno:" + description +
90                         ";urp;StarOffice.ComponentContext"));
91                 break;
92             } catch (NoConnectException e) {}
93             if (process != null) {
94                 assertNull(waitForProcess(process, 1000)); // 1 sec
95             }
96         }
97     }
98 
99     /** Shut down the OOo instance.
100     */
tearDown()101     public void tearDown()
102         throws InterruptedException, com.sun.star.uno.Exception
103     {
104         boolean desktopTerminated = true;
105         if (process != null) {
106             if (context != null) {
107                 XMultiComponentFactory factory = context.getServiceManager();
108                 assertNotNull(factory);
109                 XDesktop desktop = UnoRuntime.queryInterface(
110                     XDesktop.class,
111                     factory.createInstanceWithContext(
112                         "com.sun.star.frame.Desktop", context));
113                 context = null;
114                 try {
115                     desktopTerminated = desktop.terminate();
116                 } catch (DisposedException e) {}
117                     // it appears that DisposedExceptions can already happen
118                     // while receiving the response of the terminate call
119                 desktop = null;
120             } else {
121                 process.destroy();
122             }
123         }
124         int code = 0;
125         if (process != null) {
126             code = process.waitFor();
127         }
128         boolean outTerminated = outForward == null || outForward.terminated();
129         boolean errTerminated = errForward == null || errForward.terminated();
130         assertTrue(desktopTerminated);
131         assertEquals(0, code);
132         assertTrue(outTerminated);
133         assertTrue(errTerminated);
134     }
135 
136     /** Obtain the component context of the running OOo instance.
137     */
getComponentContext()138     public XComponentContext getComponentContext() {
139         return context;
140     }
141 
142     //TODO: get rid of this hack for legacy qa/unoapi tests
getDescription()143     public String getDescription() {
144         return description;
145     }
146 
waitForProcess(Process process, final long millis)147     private static Integer waitForProcess(Process process, final long millis)
148         throws InterruptedException
149     {
150         final Thread t1 = Thread.currentThread();
151         Thread t2 = new Thread("waitForProcess") {
152                 public void run() {
153                     try {
154                         Thread.currentThread().sleep(millis);
155                     } catch (InterruptedException e) {}
156                     t1.interrupt();
157                 }
158             };
159         boolean old = Thread.interrupted();
160             // clear interrupted status, get old status
161         t2.start();
162         int n = 0;
163         boolean done = false;
164         try {
165             n = process.waitFor();
166             done = true;
167         } catch (InterruptedException e) {}
168         t2.interrupt();
169         try {
170             t2.join();
171         } catch (InterruptedException e) {
172             t2.join();
173         }
174         Thread.interrupted(); // clear interrupted status
175         if (old) {
176             t1.interrupt(); // reset old status
177         }
178         return done ? new Integer(n) : null;
179     }
180 
181     private static final class Forward extends Thread {
Forward(InputStream in, PrintStream out)182         public Forward(InputStream in, PrintStream out) {
183             super("process output forwarder");
184             this.in = in;
185             this.out = out;
186         }
187 
run()188         public void run() {
189             for (;;) {
190                 byte[] buf = new byte[1024];
191                 int n;
192                 try {
193                     n = in.read(buf);
194                 } catch (IOException e) {
195                     throw new RuntimeException("wrapping", e);
196                 }
197                 if (n == -1) {
198                     break;
199                 }
200                 out.write(buf, 0, n);
201             }
202             done = true;
203         }
204 
terminated()205         public boolean terminated() throws InterruptedException {
206             join();
207             return done;
208         }
209 
210         private final InputStream in;
211         private final PrintStream out;
212         private boolean done = false;
213     }
214 
215     private String description;
216     private Process process = null;
217     private Forward outForward = null;
218     private Forward errForward = null;
219     private XComponentContext context = null;
220 }
221