|
| 1 | +// -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| 2 | + |
| 3 | +/* |
| 4 | + * This file is part of the LibreOffice project. |
| 5 | + * |
| 6 | + * This Source Code Form is subject to the terms of the Mozilla Public |
| 7 | + * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 8 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 9 | + * |
| 10 | + * This file incorporates work covered by the following license notice: |
| 11 | + * |
| 12 | + * Licensed to the Apache Software Foundation (ASF) under one or more |
| 13 | + * contributor license agreements. See the NOTICE file distributed |
| 14 | + * with this work for additional information regarding copyright |
| 15 | + * ownership. The ASF licenses this file to you under the Apache |
| 16 | + * License, Version 2.0 (the "License"); you may not use this file |
| 17 | + * except in compliance with the License. You may obtain a copy of |
| 18 | + * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
| 19 | + */ |
| 20 | + |
| 21 | +package org.jabref.gui.openoffice; |
| 22 | + |
| 23 | +import java.io.BufferedReader; |
| 24 | +import java.io.File; |
| 25 | +import java.io.InputStream; |
| 26 | +import java.io.InputStreamReader; |
| 27 | +import java.io.PrintStream; |
| 28 | +import java.io.UnsupportedEncodingException; |
| 29 | +import java.net.URLClassLoader; |
| 30 | +import java.util.HashMap; |
| 31 | +import java.util.Hashtable; |
| 32 | +import java.util.Map; |
| 33 | +import java.util.Random; |
| 34 | + |
| 35 | +import com.sun.star.bridge.UnoUrlResolver; |
| 36 | +import com.sun.star.bridge.XUnoUrlResolver; |
| 37 | +import com.sun.star.comp.helper.BootstrapException; |
| 38 | +import com.sun.star.comp.helper.ComponentContext; |
| 39 | +import com.sun.star.comp.helper.ComponentContextEntry; |
| 40 | +import com.sun.star.comp.loader.JavaLoader; |
| 41 | +import com.sun.star.comp.servicemanager.ServiceManager; |
| 42 | +import com.sun.star.container.XSet; |
| 43 | +import com.sun.star.lang.XInitialization; |
| 44 | +import com.sun.star.lang.XMultiComponentFactory; |
| 45 | +import com.sun.star.lang.XMultiServiceFactory; |
| 46 | +import com.sun.star.lib.util.NativeLibraryLoader; |
| 47 | +import com.sun.star.loader.XImplementationLoader; |
| 48 | +import com.sun.star.uno.UnoRuntime; |
| 49 | +import com.sun.star.uno.XComponentContext; |
| 50 | + |
| 51 | +/** Bootstrap offers functionality to obtain a context or simply |
| 52 | + a service manager. |
| 53 | + The service manager can create a few basic services, whose implementations are: |
| 54 | + <ul> |
| 55 | + <li>com.sun.star.comp.loader.JavaLoader</li> |
| 56 | + <li>com.sun.star.comp.urlresolver.UrlResolver</li> |
| 57 | + <li>com.sun.star.comp.bridgefactory.BridgeFactory</li> |
| 58 | + <li>com.sun.star.comp.connections.Connector</li> |
| 59 | + <li>com.sun.star.comp.connections.Acceptor</li> |
| 60 | + <li>com.sun.star.comp.servicemanager.ServiceManager</li> |
| 61 | + </ul> |
| 62 | +
|
| 63 | + Other services can be inserted into the service manager by |
| 64 | + using its XSet interface: |
| 65 | + <pre> |
| 66 | + XSet xSet = UnoRuntime.queryInterface( XSet.class, aMultiComponentFactory ); |
| 67 | + // insert the service manager |
| 68 | + xSet.insert( aSingleComponentFactory ); |
| 69 | + </pre> |
| 70 | +*/ |
| 71 | +public class Bootstrap { |
| 72 | + |
| 73 | + private static final Random RANDOM_PIPE_NAME = new Random(); |
| 74 | + private static boolean M_LOADED_JUH = false; |
| 75 | + |
| 76 | + private static void insertBasicFactories(XSet xSet, XImplementationLoader xImpLoader) throws Exception { |
| 77 | + // insert the factory of the loader |
| 78 | + xSet.insert(xImpLoader.activate("com.sun.star.comp.loader.JavaLoader", null, null, null)); |
| 79 | + |
| 80 | + // insert the factory of the URLResolver |
| 81 | + xSet.insert(xImpLoader.activate("com.sun.star.comp.urlresolver.UrlResolver", null, null, null)); |
| 82 | + |
| 83 | + // insert the bridgefactory |
| 84 | + xSet.insert(xImpLoader.activate("com.sun.star.comp.bridgefactory.BridgeFactory", null, null, null)); |
| 85 | + |
| 86 | + // insert the connector |
| 87 | + xSet.insert(xImpLoader.activate("com.sun.star.comp.connections.Connector", null, null, null)); |
| 88 | + |
| 89 | + // insert the acceptor |
| 90 | + xSet.insert(xImpLoader.activate("com.sun.star.comp.connections.Acceptor", null, null, null)); |
| 91 | + } |
| 92 | + |
| 93 | + /** |
| 94 | + * Returns an array of default commandline options to start bootstrapped |
| 95 | + * instance of soffice with. You may use it in connection with bootstrap |
| 96 | + * method for example like this: |
| 97 | + * <pre> |
| 98 | + * List list = Arrays.asList( Bootstrap.getDefaultOptions() ); |
| 99 | + * list.remove("--nologo"); |
| 100 | + * list.remove("--nodefault"); |
| 101 | + * list.add("--invisible"); |
| 102 | + * |
| 103 | + * Bootstrap.bootstrap( list.toArray( new String[list.size()] ); |
| 104 | + * </pre> |
| 105 | + * |
| 106 | + * @return an array of default commandline options |
| 107 | + * @see #bootstrap( String[] ) |
| 108 | + * @since LibreOffice 5.1 |
| 109 | + */ |
| 110 | + public static final String[] getDefaultOptions() { |
| 111 | + return new String[] {"--nologo", "--nodefault", "--norestore", "--nolockcheck"}; |
| 112 | + } |
| 113 | + |
| 114 | + /** |
| 115 | + backwards compatibility stub. |
| 116 | + @param context_entries the hash table contains mappings of entry names (type string) to |
| 117 | + context entries (type class ComponentContextEntry). |
| 118 | + @throws Exception if things go awry. |
| 119 | + @return a new context. |
| 120 | + */ |
| 121 | + public static XComponentContext createInitialComponentContext(Hashtable<String, Object> context_entries) throws Exception { |
| 122 | + return createInitialComponentContext((Map<String, Object>) context_entries); |
| 123 | + } |
| 124 | + |
| 125 | + /** Bootstraps an initial component context with service manager and basic |
| 126 | + jurt components inserted. |
| 127 | + @param context_entries the hash table contains mappings of entry names (type string) to |
| 128 | + context entries (type class ComponentContextEntry). |
| 129 | + @throws Exception if things go awry. |
| 130 | + @return a new context. |
| 131 | + */ |
| 132 | + public static XComponentContext createInitialComponentContext(Map<String, Object> context_entries) throws Exception { |
| 133 | + ServiceManager xSMgr = new ServiceManager(); |
| 134 | + |
| 135 | + XImplementationLoader xImpLoader = UnoRuntime.queryInterface(XImplementationLoader.class, new JavaLoader()); |
| 136 | + XInitialization xInit = UnoRuntime.queryInterface(XInitialization.class, xImpLoader); |
| 137 | + Object[] args = new Object[] {xSMgr}; |
| 138 | + xInit.initialize(args); |
| 139 | + |
| 140 | + // initial component context |
| 141 | + if (context_entries == null) { |
| 142 | + context_entries = new HashMap<>(1); |
| 143 | + } |
| 144 | + // add smgr |
| 145 | + context_entries.put("/singletons/com.sun.star.lang.theServiceManager", new ComponentContextEntry(null, xSMgr)); |
| 146 | + // ... xxx todo: add standard entries |
| 147 | + XComponentContext xContext = new ComponentContext(context_entries, null); |
| 148 | + |
| 149 | + xSMgr.setDefaultContext(xContext); |
| 150 | + |
| 151 | + XSet xSet = UnoRuntime.queryInterface(XSet.class, xSMgr); |
| 152 | + // insert basic jurt factories |
| 153 | + insertBasicFactories(xSet, xImpLoader); |
| 154 | + |
| 155 | + return xContext; |
| 156 | + } |
| 157 | + |
| 158 | + /** |
| 159 | + * Bootstraps a servicemanager with the jurt base components registered. |
| 160 | + * |
| 161 | + * See also UNOIDL <code>com.sun.star.lang.ServiceManager</code>. |
| 162 | + * |
| 163 | + * @throws Exception if things go awry. |
| 164 | + * @return a freshly bootstrapped service manager |
| 165 | + */ |
| 166 | + public static XMultiServiceFactory createSimpleServiceManager() throws Exception { |
| 167 | + return UnoRuntime.queryInterface(XMultiServiceFactory.class, createInitialComponentContext((Map<String, Object>) null).getServiceManager()); |
| 168 | + } |
| 169 | + |
| 170 | + /** Bootstraps the initial component context from a native UNO installation. |
| 171 | +
|
| 172 | + @throws Exception if things go awry. |
| 173 | + @return a freshly bootstrapped component context. |
| 174 | +
|
| 175 | + See also |
| 176 | + <code>cppuhelper/defaultBootstrap_InitialComponentContext()</code>. |
| 177 | + */ |
| 178 | + public static final XComponentContext defaultBootstrap_InitialComponentContext() throws Exception { |
| 179 | + return defaultBootstrap_InitialComponentContext((String) null, (Map<String, String>) null); |
| 180 | + } |
| 181 | + |
| 182 | + /** |
| 183 | + * Backwards compatibility stub. |
| 184 | + * |
| 185 | + * @param ini_file |
| 186 | + * ini_file (may be null: uno.rc besides cppuhelper lib) |
| 187 | + * @param bootstrap_parameters |
| 188 | + * bootstrap parameters (maybe null) |
| 189 | + * |
| 190 | + * @throws Exception if things go awry. |
| 191 | + * @return a freshly bootstrapped component context. |
| 192 | + */ |
| 193 | + public static final XComponentContext defaultBootstrap_InitialComponentContext(String ini_file, Hashtable<String, String> bootstrap_parameters) throws Exception { |
| 194 | + return defaultBootstrap_InitialComponentContext(ini_file, (Map<String, String>) bootstrap_parameters); |
| 195 | + } |
| 196 | + |
| 197 | + /** Bootstraps the initial component context from a native UNO installation. |
| 198 | +
|
| 199 | + See also |
| 200 | + <code>cppuhelper/defaultBootstrap_InitialComponentContext()</code>. |
| 201 | +
|
| 202 | + @param ini_file |
| 203 | + ini_file (may be null: uno.rc besides cppuhelper lib) |
| 204 | + @param bootstrap_parameters |
| 205 | + bootstrap parameters (maybe null) |
| 206 | +
|
| 207 | + @throws Exception if things go awry. |
| 208 | + @return a freshly bootstrapped component context. |
| 209 | + */ |
| 210 | + public static final XComponentContext defaultBootstrap_InitialComponentContext(String ini_file, Map<String, String> bootstrap_parameters) throws Exception { |
| 211 | + // jni convenience: easier to iterate over array than calling Hashtable |
| 212 | + String pairs[] = null; |
| 213 | + if (null != bootstrap_parameters) { |
| 214 | + pairs = new String[2 * bootstrap_parameters.size()]; |
| 215 | + int n = 0; |
| 216 | + for (Map.Entry<String, String> bootstrap_parameter : bootstrap_parameters.entrySet()) { |
| 217 | + pairs[n++] = bootstrap_parameter.getKey(); |
| 218 | + pairs[n++] = bootstrap_parameter.getValue(); |
| 219 | + } |
| 220 | + } |
| 221 | + |
| 222 | + if (!M_LOADED_JUH) { |
| 223 | + if ("The Android Project".equals(System.getProperty("java.vendor"))) { |
| 224 | + // Find out if we are configured with DISABLE_DYNLOADING or |
| 225 | + // not. Try to load the lo-bootstrap shared library which |
| 226 | + // won't exist in the DISABLE_DYNLOADING case. (And which will |
| 227 | + // be already loaded otherwise, so nothing unexpected happens |
| 228 | + // that case.) Yeah, this would be simpler if I just could be |
| 229 | + // bothered to keep a separate branch for DISABLE_DYNLOADING |
| 230 | + // on Android, merging in master periodically, until I know |
| 231 | + // for sure whether it is what I want, or not. |
| 232 | + |
| 233 | + boolean disable_dynloading = false; |
| 234 | + try { |
| 235 | + System.loadLibrary("lo-bootstrap"); |
| 236 | + } catch (UnsatisfiedLinkError e) { |
| 237 | + disable_dynloading = true; |
| 238 | + } |
| 239 | + |
| 240 | + if (!disable_dynloading) { |
| 241 | + NativeLibraryLoader.loadLibrary(Bootstrap.class.getClassLoader(), "juh"); |
| 242 | + } |
| 243 | + } else { |
| 244 | + NativeLibraryLoader.loadLibrary(Bootstrap.class.getClassLoader(), "juh"); |
| 245 | + } |
| 246 | + M_LOADED_JUH = true; |
| 247 | + } |
| 248 | + return UnoRuntime.queryInterface(XComponentContext.class, cppuhelper_bootstrap(ini_file, pairs, Bootstrap.class.getClassLoader())); |
| 249 | + } |
| 250 | + |
| 251 | + private static native Object cppuhelper_bootstrap(String ini_file, String bootstrap_parameters[], ClassLoader loader) throws Exception; |
| 252 | + |
| 253 | + /** |
| 254 | + * Bootstraps the component context from a UNO installation. |
| 255 | + * |
| 256 | + * @throws BootstrapException if things go awry. |
| 257 | + * |
| 258 | + * @return a bootstrapped component context. |
| 259 | + * |
| 260 | + * @since UDK 3.1.0 |
| 261 | + */ |
| 262 | + public static final XComponentContext bootstrap(URLClassLoader loader) throws BootstrapException { |
| 263 | + |
| 264 | + String[] defaultArgArray = getDefaultOptions(); |
| 265 | + return bootstrap(defaultArgArray, loader); |
| 266 | + } |
| 267 | + |
| 268 | + /** |
| 269 | + * Bootstraps the component context from a UNO installation. |
| 270 | + * |
| 271 | + * @param argArray |
| 272 | + * an array of strings - commandline options to start instance of |
| 273 | + * soffice with |
| 274 | + * @see #getDefaultOptions() |
| 275 | + * |
| 276 | + * @throws BootstrapException if things go awry. |
| 277 | + * |
| 278 | + * @return a bootstrapped component context. |
| 279 | + * |
| 280 | + * @since LibreOffice 5.1 |
| 281 | + */ |
| 282 | + public static final XComponentContext bootstrap(String[] argArray, URLClassLoader loader) throws BootstrapException { |
| 283 | + |
| 284 | + XComponentContext xContext = null; |
| 285 | + |
| 286 | + try { |
| 287 | + // create default local component context |
| 288 | + XComponentContext xLocalContext = createInitialComponentContext((Map<String, Object>) null); |
| 289 | + if (xLocalContext == null) { |
| 290 | + throw new BootstrapException("no local component context!"); |
| 291 | + } |
| 292 | + |
| 293 | + // find office executable relative to this class's class loader |
| 294 | + String sOffice = System.getProperty("os.name").startsWith("Windows") ? "soffice.exe" : "soffice"; |
| 295 | + |
| 296 | + File fOffice = NativeLibraryLoader.getResource(loader, sOffice); |
| 297 | + if (fOffice == null) { |
| 298 | + throw new BootstrapException("no office executable found!"); |
| 299 | + } |
| 300 | + |
| 301 | + // create call with arguments |
| 302 | + //We need a socket, pipe does not work. https://api.libreoffice.org/examples/examples.html |
| 303 | + String[] cmdArray = new String[argArray.length + 2]; |
| 304 | + cmdArray[0] = fOffice.getPath(); |
| 305 | + cmdArray[1] = ("--accept=socket,host=localhost,port=2083" + ";urp;"); |
| 306 | + |
| 307 | + System.arraycopy(argArray, 0, cmdArray, 2, argArray.length); |
| 308 | + |
| 309 | + // start office process |
| 310 | + Process p = Runtime.getRuntime().exec(cmdArray); |
| 311 | + pipe(p.getInputStream(), System.out, "CO> "); |
| 312 | + pipe(p.getErrorStream(), System.err, "CE> "); |
| 313 | + |
| 314 | + // initial service manager |
| 315 | + XMultiComponentFactory xLocalServiceManager = xLocalContext.getServiceManager(); |
| 316 | + if (xLocalServiceManager == null) { |
| 317 | + throw new BootstrapException("no initial service manager!"); |
| 318 | + } |
| 319 | + |
| 320 | + // create a URL resolver |
| 321 | + XUnoUrlResolver xUrlResolver = UnoUrlResolver.create(xLocalContext); |
| 322 | + |
| 323 | + // connection string |
| 324 | + String sConnect = "uno:socket,host=localhost,port=2083" + ";urp;StarOffice.ComponentContext"; |
| 325 | + |
| 326 | + // wait until office is started |
| 327 | + for (int i = 0;; ++i) { |
| 328 | + try { |
| 329 | + // try to connect to office |
| 330 | + Object context = xUrlResolver.resolve(sConnect); |
| 331 | + xContext = UnoRuntime.queryInterface(XComponentContext.class, context); |
| 332 | + if (xContext == null) { |
| 333 | + throw new BootstrapException("no component context!"); |
| 334 | + } |
| 335 | + break; |
| 336 | + } catch (com.sun.star.connection.NoConnectException ex) { |
| 337 | + // Wait 500 ms, then try to connect again, but do not wait |
| 338 | + // longer than 5 min (= 600 * 500 ms) total: |
| 339 | + if (i == 600) { |
| 340 | + throw new BootstrapException(ex); |
| 341 | + } |
| 342 | + Thread.sleep(500); |
| 343 | + } |
| 344 | + } |
| 345 | + } catch (BootstrapException e) { |
| 346 | + throw e; |
| 347 | + } catch (java.lang.RuntimeException e) { |
| 348 | + throw e; |
| 349 | + } catch (java.lang.Exception e) { |
| 350 | + throw new BootstrapException(e); |
| 351 | + } |
| 352 | + |
| 353 | + return xContext; |
| 354 | + } |
| 355 | + |
| 356 | + private static void pipe(final InputStream in, final PrintStream out, final String prefix) { |
| 357 | + |
| 358 | + new Thread("Pipe: " + prefix) { |
| 359 | + |
| 360 | + @Override |
| 361 | + public void run() { |
| 362 | + try { |
| 363 | + BufferedReader r = new BufferedReader(new InputStreamReader(in, "UTF-8")); |
| 364 | + |
| 365 | + for (;;) { |
| 366 | + String s = r.readLine(); |
| 367 | + if (s == null) { |
| 368 | + break; |
| 369 | + } |
| 370 | + out.println(prefix + s); |
| 371 | + } |
| 372 | + } catch (UnsupportedEncodingException e) { |
| 373 | + e.printStackTrace(System.err); |
| 374 | + } catch (java.io.IOException e) { |
| 375 | + e.printStackTrace(System.err); |
| 376 | + } |
| 377 | + } |
| 378 | + }.start(); |
| 379 | + } |
| 380 | +} |
| 381 | + |
| 382 | +// vim:set shiftwidth=4 softtabstop=4 expandtab: |
0 commit comments