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 package org.openoffice.test;
23 
24 import java.io.File;
25 import java.io.IOException;
26 import java.net.MalformedURLException;
27 import java.net.URL;
28 import java.text.SimpleDateFormat;
29 import java.util.ArrayList;
30 import java.util.Date;
31 import java.util.HashMap;
32 import java.util.Map.Entry;
33 import java.util.Properties;
34 import java.util.Set;
35 import java.util.UUID;
36 import java.util.logging.Logger;
37 
38 import org.openoffice.test.common.FileUtil;
39 import org.openoffice.test.common.SystemUtil;
40 
41 /**
42  * It represents one OpenOffice instance.
43  * By default, the instance will be started with automation and socket UNO connection enabled.
44  *
45  */
46 public class OpenOffice {
47 
48 	private static Logger LOG = Logger.getLogger(OpenOffice.class.getName());
49 
50 	private static final String[] DEFAULT_HOME = new String[] {
51 		"C:/Program Files/OpenOffice.org 3",
52 		"C:/Program Files (x86)/OpenOffice.org 3",
53 		"/Applications/OpenOffice.org.app/Contents",
54 		"/opt/openoffice.org3",
55 	};
56 
57 	private static final String USERHOME = System.getProperty("user.home");
58 	private static final String BIN = SystemUtil.isWindows() ? "program/soffice.exe" : SystemUtil.isMac() ? "MacOS/soffice": "program/soffice";
59 	private static final String SYSUSERCONFIG = SystemUtil.isWindows()? System.getenv("APPDATA") : SystemUtil.isMac() ?  USERHOME + "/Library/Application Support" : USERHOME;
60 	public static final String DEFAULT_UNO_URL = "socket,host=127.0.0.1,port=2002;urp";
61 	public static final int DEFAULT_AUTOMATION_PORT = 12479;
62 
63 	private static OpenOffice defaultInstance = null;
64 
65 	private File userInstallation = null;
66 
67 	private File defaultUserInstallation = null;
68 
69 	private File home = null;
70 
71 	private File bin = null;
72 
73 	private String binPath = null;
74 
75 	private ArrayList<String> args = new ArrayList<String>();
76 
77 	private ArrayList<String> registryModifications = new ArrayList<String>();
78 
79 	private int automationPort = 0;
80 
81 	private Process process = null;
82 
83 	private String unoUrl = null;
84 
85 	private Properties versionProps = null;
86 
87 	private String id = UUID.randomUUID().toString().replace("-", "");
88 
89 	private String processPattern = SystemUtil.isMac() ? ".*soffice .*" + id + ".*" : ".*soffice\\.bin.*" + id + ".*|.*soffice\\.exe.*" + id + ".*-env.*";
90 
OpenOffice()91 	public OpenOffice() {
92 		this(null);
93 	}
94 
95 	/**
96 	 * Construct Process with the home path of OpenOffice.
97 	 *
98 	 * @param appHome
99 	 */
OpenOffice(String appHome)100 	public OpenOffice(String appHome) {
101 		if (appHome == null)
102 			appHome = System.getProperty("openoffice.home");
103 		if (appHome == null)
104 			appHome = System.getenv("OPENOFFICE_HOME");
105 		if (appHome == null) {
106 			// Search in the classpath
107 			try {
108 				URL url = getClass().getClassLoader().getResource(BIN);
109 				File file = new File(url.toURI());
110 				if (file.exists())
111 					appHome = file.getParentFile().getParentFile().getAbsolutePath();
112 			} catch (Exception e) {
113 				// ignore
114 			}
115 		}
116 
117 		if (appHome == null) {
118 			for (int i = 0; i < DEFAULT_HOME.length; i++)
119 				if (new File(DEFAULT_HOME[i]).exists())
120 					appHome = DEFAULT_HOME[i];
121 		}
122 
123 		home = new File(appHome);
124 		bin = new File(appHome, BIN);
125 		try {
126 			binPath = bin.getCanonicalPath();
127 		} catch (IOException e1) {
128 		}
129 		File binParent = bin.getParentFile();
130 		File bootstrapFile = new File(binParent, "bootstraprc");
131 		if (!bootstrapFile.exists())
132 			bootstrapFile = new File(binParent, "bootstrap.ini");
133 		if (!bootstrapFile.exists())
134 			throw new Error("OpenOffice can not be found or it's broken. Testing can not be performed. " +
135 					"Use system property openoffice.home to specify the correct location of OpenOffice.");
136 
137 		Properties props = FileUtil.loadProperties(bootstrapFile);
138 		String defaultUserInstallationPath = props.getProperty("UserInstallation").replace("$ORIGIN", binParent.getAbsolutePath()).replace("$SYSUSERCONFIG", SYSUSERCONFIG);
139 		defaultUserInstallation = new File(defaultUserInstallationPath);
140 
141 		File versionFile = new File(binParent, "versionrc");
142 		if (!versionFile.exists())
143 			versionFile = new File(binParent, "version.ini");
144 		versionProps = FileUtil.loadProperties(versionFile);
145 		Set<Entry<Object, Object>> entries = versionProps.entrySet();
146 		for (Entry<Object, Object> e : entries) {
147 			System.setProperty("info.app." + e.getKey(), (String)e.getValue());
148 		}
149 		//
150 		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
151 		System.setProperty("info.app.date", dateFormat.format(new Date(versionFile.lastModified())));
152 		addArgs("-" + id);
153 	}
154 
getDefault()155 	public static OpenOffice getDefault() {
156 		if (defaultInstance == null) {
157 			defaultInstance = new OpenOffice();
158 			defaultInstance.setAutomationPort(DEFAULT_AUTOMATION_PORT);
159 			defaultInstance.setUnoUrl(DEFAULT_UNO_URL);
160 			defaultInstance.addArgs("-nofirststartwizard", "-norestore", "-quickstart=no");
161 			defaultInstance.addRegistryModifications("<item oor:path=\"/org.openoffice.Office.Common/Misc\"><prop oor:name=\"UseSystemFileDialog\" oor:op=\"fuse\"><value>false</value></prop></item>",
162 					"<item oor:path=\"/org.openoffice.Office.Common/Security/Scripting\"><prop oor:name=\"MacroSecurityLevel\" oor:op=\"fuse\"><value>0</value></prop></item>");
163 		}
164 
165 		return defaultInstance;
166 	}
167 
restoreDefault()168 	public static void restoreDefault() {
169 		if (defaultInstance != null) {
170 
171 		}
172 	}
173 
getVersionProps()174 	public Properties getVersionProps() {
175 		return versionProps;
176 	}
177 
178 	/**
179 	 * Set UserInstallation directory. When openoffice is launched, the argument
180 	 * "-env:UserInstallation" will be enabled.
181 	 *
182 	 * @param dir
183 	 *            user installation directory. If null is given, the default
184 	 *            will be used.
185 	 */
setUserInstallation(File dir)186 	public void setUserInstallation(File dir) {
187 		userInstallation = dir;
188 	}
189 
190 	/**
191 	 * Get UserInstallation directory
192 	 *
193 	 * @return
194 	 */
getUserInstallation()195 	public File getUserInstallation() {
196 		return userInstallation == null ? defaultUserInstallation : userInstallation;
197 	}
198 
199 	/**
200 	 * Get default UserInstallation directory
201 	 *
202 	 * @return
203 	 */
getDefaultUserInstallation()204 	public File getDefaultUserInstallation() {
205 		return defaultUserInstallation;
206 	}
207 
208 	/**
209 	 * Clean the user installation
210 	 */
cleanUserInstallation()211 	public void cleanUserInstallation() {
212 		FileUtil.deleteFile(getUserInstallation());
213 	}
214 
215 	/**
216 	 * Get installation directory of OpenOffice.
217 	 *
218 	 * @return
219 	 */
getHome()220 	public File getHome() {
221 		return home;
222 	}
223 
224 	/**
225 	 * Set other command line arguments
226 	 *
227 	 * @param args
228 	 */
addArgs(String... arguments)229 	public void addArgs(String... arguments) {
230 		for (String a : arguments)
231 			args.add(a);
232 	}
233 
addRegistryModifications(String... items)234 	public void addRegistryModifications(String... items) {
235 		for (String i : items)
236 			registryModifications.add(i);
237 	}
238 
getAutomationPort()239 	public int getAutomationPort() {
240 		return automationPort;
241 	}
242 
243 	/**
244 	 * Set automation server listening port
245 	 * @param automationPort If 0, automation server will be disabled.
246 	 */
setAutomationPort(int automationPort)247 	public void setAutomationPort(int automationPort) {
248 		this.automationPort = automationPort;
249 	}
250 
getUnoUrl()251 	public String getUnoUrl() {
252 		return unoUrl;
253 	}
254 
255 	/**
256 	 * Set UNO connection listening url
257 	 * @param unoUrl If null, UNO connection will be disabled.
258 	 */
setUnoUrl(String unoUrl)259 	public void setUnoUrl(String unoUrl) {
260 		this.unoUrl = unoUrl;
261 	}
262 
263 	/**
264 	 * destroy OpenOffice
265 	 */
kill()266 	public void kill() {
267 		SystemUtil.killProcess(".*soffice.*" + id + ".*");
268 		SystemUtil.sleep(1);
269 		process = null;
270 	}
271 
272 	/**
273 	 * Kill all openoffice instances
274 	 */
killAll()275 	public static void killAll() {
276 		SystemUtil.killProcess(".*soffice.*");
277 		SystemUtil.sleep(1);
278 	}
279 
isRunning()280 	public boolean isRunning() {
281 		if (process == null)
282 			return false;
283 
284 		try {
285 			process.exitValue();
286 		} catch (Exception e1) {
287 			return true;
288 		}
289 
290 		return false;
291 	}
292 
293 	/**
294 	 * Start OpenOffice
295 	 *
296 	 * @return return true when the process is created.
297 	 */
298 	@SuppressWarnings("all")
start()299 	public boolean start() {
300 		if (isRunning())
301 			return false;
302 
303 		ArrayList<String> cmds = new ArrayList<String>();
304 		cmds.add(binPath);
305 		if (automationPort > 0) {
306 			cmds.add("-automationport=" + automationPort);
307 			cmds.add("-enableautomation");
308 		}
309 
310 		if (unoUrl != null)
311 			cmds.add("-accept=" + unoUrl);
312 
313 		if (userInstallation != null) {
314 			try {
315 				String url = userInstallation.toURL().toString();
316 				url = url.replace("file:/", "file:///");
317 				cmds.add("-env:UserInstallation=" + url);
318 			} catch (MalformedURLException e) {
319 				// ignore never to occur
320 			}
321 		}
322 
323 		if (args != null)
324 			cmds.addAll(args);
325 
326 		if (registryModifications.size() > 0) {
327 			File registry = new File(getUserInstallation(), "user/registrymodifications.xcu");
328 			String content = FileUtil.readFileAsString(registry);
329 			String newContent = "";
330 			for (String item : registryModifications) {
331 				if (!content.contains(item)) {
332 					newContent += item;
333 				}
334 			}
335 			content.replace("</oor:items>", "");
336 			if (content.length() == 0)
337 				content += "<?xml version=\"1.0\" encoding=\"UTF-8\"?><oor:items xmlns:oor=\"http://openoffice.org/2001/registry\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">";
338 			content += newContent + "</oor:items>";
339 			FileUtil.writeStringToFile(registry.getAbsolutePath(), content);
340 		}
341 
342 		process = SystemUtil.backgroundExec(cmds.toArray(new String[]{}), null, null, null, null);
343 
344 		String cmdLine="";
345 		for (String s : cmds)
346 			cmdLine += "\""+ s + "\" ";
347 
348 		LOG.info(cmdLine);
349 		if (process == null)
350 			throw new RuntimeException("OpenOffice can't be started!");
351 		return true;
352 	}
353 
getPerfData()354 	public HashMap<String, Object> getPerfData() {
355 		HashMap<String, Object> proccessInfo = SystemUtil.findProcess(processPattern);
356 		String pid = (String) proccessInfo.get("pid");
357 		if (pid == null)
358 			throw new RuntimeException("Can not find performance data");
359 		return SystemUtil.getProcessPerfData(pid);
360 	}
361 }
362