1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 package com.sun.star.comp.beans;
29 
30 import java.awt.Container;
31 import java.io.File;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.Vector;
35 
36 import com.sun.star.lang.XMultiComponentFactory;
37 import com.sun.star.lang.XComponent;
38 import com.sun.star.lang.XEventListener;
39 import com.sun.star.connection.XConnection;
40 import com.sun.star.connection.XConnector;
41 import com.sun.star.bridge.XBridge;
42 import com.sun.star.bridge.XBridgeFactory;
43 import com.sun.star.beans.XPropertySet;
44 import com.sun.star.uno.XComponentContext;
45 import com.sun.star.uno.UnoRuntime;
46 import com.sun.star.uno.Exception;
47 import com.sun.star.lib.uno.helper.UnoUrl;
48 import com.sun.star.lib.util.NativeLibraryLoader;
49 
50 /**
51  * This class reprecents a connection to the local office application.
52  *
53  * @since OOo 2.0.0
54  */
55 public class LocalOfficeConnection
56 	implements OfficeConnection
57 {
58 	public static final String		OFFICE_APP_NAME		= "soffice";
59 	public static final String		OFFICE_LIB_NAME		= "officebean";
60 	public static final String		OFFICE_ID_SUFFIX	= "_Office";
61 
62 	private static String			mProgramPath;
63 
64 	private Process				mProcess;
65 	private ContainerFactory		mContainerFactory;
66 	private XComponentContext		mContext;
67     private XBridge mBridge;
68 
69 	private String				mURL;
70 	private String				mConnType;
71 	private String				mPipe;
72 	private String				mPort;
73 	private String				mProtocol;
74 	private String				mInitialObject;
75 
76 	private List				mComponents		= new Vector();
77 
78     private static long m_nBridgeCounter = 0;
79 	//-------------------------------------------------------------------------
80 	static
81 	{
82 		// preload shared libraries whichs import lips are linked to officebean
83 		if ( System.getProperty( "os.name" ).startsWith( "Windows" ) )
84 		{
85 			try
86 			{
87 				NativeLibraryLoader.loadLibrary(LocalOfficeConnection.class.getClassLoader(), "msvcr70");
88 			}
89 			catch (Throwable e)
90 			{
91 				// loading twice would fail
92 				System.err.println( "cannot find msvcr70" );
93 			}
94 
95 			try
96 			{
97 				NativeLibraryLoader.loadLibrary(LocalOfficeConnection.class.getClassLoader(), "msvcr71");
98 			}
99 			catch (Throwable e)
100 			{
101 				// loading twice would fail
102 				System.err.println( "cannot find msvcr71" );
103 			}
104 
105 			try
106 			{
107 				NativeLibraryLoader.loadLibrary(LocalOfficeConnection.class.getClassLoader(), "uwinapi");
108 			}
109 			catch (Throwable e)
110 			{
111 				// loading twice would fail
112 				System.err.println( "cannot find uwinapi" );
113 			}
114 
115 			try
116 			{
117 				NativeLibraryLoader.loadLibrary(LocalOfficeConnection.class.getClassLoader(), "jawt");
118 			}
119 			catch (Throwable e)
120 			{
121 				// loading twice would fail
122 				System.err.println( "cannot find jawt" );
123 			}
124 		}
125 
126 		// load shared library for JNI code
127 		NativeLibraryLoader.loadLibrary( LocalOfficeConnection.class.getClassLoader(), "officebean" );
128 	}
129 
130 	//-------------------------------------------------------------------------
131 	// debugging method
132 	private void dbgPrint( String aMessage )
133 	{
134 		System.err.println( aMessage );
135 	}
136 
137 	/**
138 	 * Constructor.
139 	 * Sets up paths to the office application and native libraries if
140 	 * values are available in <code>OFFICE_PROP_FILE</code> in the user
141 	 * home directory.<br />
142 	 * "com.sun.star.beans.path" - the office application directory;<br/>
143 	 * "com.sun.star.beans.libpath" - native libraries directory.
144 	 */
145 	public LocalOfficeConnection()
146 	{
147 		// init member vars
148 		try
149 		{
150 			setUnoUrl( "uno:pipe,name=" + getPipeName() + ";urp;StarOffice.ServiceManager" );
151 		}
152 		catch ( java.net.MalformedURLException e )
153 		{}
154 	}
155 
156         /**
157          * protected Constructor
158          * Initialise a LocalOfficeConnection with an already running office.
159          * This C'Tor is only used in complex tests at the moment.
160          * @param xContext
161          */
162         protected LocalOfficeConnection(com.sun.star.uno.XComponentContext xContext)
163         {
164             this.mContext = xContext;
165         }
166 
167         /**
168 	 * Sets a connection URL.
169 	 * This implementation accepts a UNO URL with following format:<br />
170 	 * <pre>
171 	 * url    := uno:localoffice[,&lt;params&gt;];urp;StarOffice.ServiceManager
172 	 * params := &lt;path&gt;[,&lt;pipe&gt;]
173 	 * path   := path=&lt;pathv&gt;
174 	 * pipe   := pipe=&lt;pipev&gt;
175 	 * pathv  := platform_specific_path_to_the_local_office_distribution
176 	 * pipev  := local_office_connection_pipe_name
177 	 * </pre>
178 	 *
179 	 * @param url This is UNO URL which discribes the type of a connection.
180 	 */
181 	public void setUnoUrl(String url)
182 		throws java.net.MalformedURLException
183 	{
184 		mURL	= null;
185 
186 		String prefix = "uno:localoffice";
187 		if ( url.startsWith(prefix) )
188 			parseUnoUrlWithOfficePath( url, prefix );
189 		else
190 		{
191 			try
192 			{
193 				UnoUrl aURL = UnoUrl.parseUnoUrl( url );
194 				mProgramPath = null;
195 				mConnType = aURL.getConnection();
196 				mPipe = (String) aURL.getConnectionParameters().get( "pipe" );
197 				mPort = (String) aURL.getConnectionParameters().get( "port" );
198 				mProtocol = aURL.getProtocol();
199 				mInitialObject = aURL.getRootOid();
200 			}
201 			catch ( com.sun.star.lang.IllegalArgumentException eIll )
202 			{
203 				throw new java.net.MalformedURLException(
204 					"Invalid UNO connection URL.");
205 			}
206 		}
207 		mURL	= url;
208 	}
209 
210 	/**
211 	 * Sets an AWT container catory.
212 	 *
213 	 * @param containerFactory This is a application provided AWT container
214 	 *	factory.
215 	 */
216 	public void setContainerFactory(ContainerFactory containerFactory)
217 	{
218 		mContainerFactory	= containerFactory;
219 	}
220 
221 	/**
222 	 * Retrives the UNO component context.
223 	 * Establishes a connection if necessary and initialises the
224 	 * UNO service manager if it has not already been initialised.
225 	 * This method can return <code>null</code> if it fails to connect
226 	 * to the office application.
227 	 *
228 	 * @return The office UNO component context.
229 	 */
230 	synchronized public XComponentContext getComponentContext()
231 	{
232 		if ( mContext == null )
233 			mContext = connect();
234 		return mContext;
235 	}
236 
237 	/**
238 	 * Creates an office window.
239 	 * The window is either a sub-class of java.awt.Canvas (local) or
240 	 * java.awt.Container (RVP).
241 	 *
242 	 * @param container This is an AWT container.
243 	 * @return The office window instance.
244 	 */
245 	public OfficeWindow createOfficeWindow(Container container)
246 	{
247 		return new LocalOfficeWindow(this);
248 	}
249 
250 	/**
251 	 * Closes the connection.
252 	 */
253     public void dispose()
254 	{
255 		Iterator itr = mComponents.iterator();
256 		while (itr.hasNext() == true) {
257 			// ignore runtime exceptions in dispose
258 			try { ((XEventListener)itr.next()).disposing(null); }
259 			catch ( RuntimeException aExc ) {}
260 		}
261 		mComponents.clear();
262 
263         //Terminate the bridge. It turned out that this is necessary for the bean
264         //to work properly when displayed in an applet within Internet Explorer.
265         //When navigating off the page which is showing  the applet and then going
266         //back to it, then the Java remote bridge is damaged. That is the Java threads
267         //do not work properly anymore. Therefore when Applet.stop is called the connection
268         //to the office including the bridge needs to be terminated.
269         if (mBridge != null)
270         {
271             XComponent comp = (XComponent)UnoRuntime.queryInterface(
272                     XComponent.class, mBridge);
273             if (comp != null)
274                comp.dispose();
275             else
276                 System.err.println("LocalOfficeConnection: could not dispose bridge!");
277 
278             mBridge = null;
279         }
280 
281 		mContainerFactory = null;
282 		mContext = null;
283 	}
284 
285 	/**
286 	 * Adds an event listener to the object.
287 	 *
288 	 * @param listener is a listener object.
289 	 */
290 	public void addEventListener(XEventListener listener)
291 	{
292 		mComponents.add(listener);
293 	}
294 
295 	/**
296 	 * Removes an event listener from the listener list.
297 	 *
298 	 * @param listener is a listener object.
299 	 */
300 	public void removeEventListener(XEventListener listener)
301 	{
302 		mComponents.remove(listener);
303 	}
304 
305 	/**
306 	 * Establishes the connection to the office.
307 	 */
308 	private XComponentContext connect()
309 	{
310 		try
311 		{
312 			// create default local component context
313 			XComponentContext xLocalContext =
314 			    com.sun.star.comp.helper.Bootstrap.createInitialComponentContext(null);
315 
316 			// initial serviceManager
317 			XMultiComponentFactory xLocalServiceManager = xLocalContext.getServiceManager();
318 
319 			// try to connect to soffice
320 			Object aInitialObject = null;
321 			try
322 			{
323                 aInitialObject = resolve(xLocalContext, mURL);
324 			}
325 			catch( com.sun.star.connection.NoConnectException e )
326 			{
327 				// launch soffice
328 				OfficeService aSOffice = new OfficeService();
329 				aSOffice.startupService();
330 
331 				// wait until soffice is started
332 				long nMaxMillis = System.currentTimeMillis() + 1000*aSOffice.getStartupTime();
333 				while ( aInitialObject == null )
334 				{
335 					try
336 					{
337 						// try to connect to soffice
338 						Thread.currentThread().sleep( 500 );
339                         aInitialObject = resolve(xLocalContext, mURL);
340 					}
341 					catch( com.sun.star.connection.NoConnectException aEx )
342 					{
343 						// soffice did not start in time
344 						if ( System.currentTimeMillis() > nMaxMillis )
345 							throw aEx;
346 
347 					}
348 				}
349 			}
350 			finally
351 			{
352 			}
353 
354 			// XComponentContext
355 			if( null != aInitialObject )
356 			{
357 				XPropertySet xPropertySet = (XPropertySet)
358 					UnoRuntime.queryInterface( XPropertySet.class, aInitialObject);
359             			Object xContext = xPropertySet.getPropertyValue("DefaultContext");
360             			XComponentContext xComponentContext = (XComponentContext) UnoRuntime.queryInterface(
361 					XComponentContext.class, xContext);
362 				return xComponentContext;
363 			}
364 		}
365 		catch( com.sun.star.connection.NoConnectException e )
366 		{
367 			System.out.println( "Couldn't connect to remote server" );
368 			System.out.println( e.getMessage() );
369 		}
370 		catch( com.sun.star.connection.ConnectionSetupException e )
371 		{
372 			System.out.println( "Couldn't access necessary local resource to establish the interprocess connection" );
373 			System.out.println( e.getMessage() );
374 		}
375 		catch( com.sun.star.lang.IllegalArgumentException e )
376 		{
377 			System.out.println( "uno-url is syntactical illegal ( " + mURL + " )" );
378 			System.out.println( e.getMessage() );
379 		}
380 		catch( com.sun.star.uno.RuntimeException e )
381 		{
382 			System.out.println( "--- RuntimeException:" );
383 			System.out.println( e.getMessage() );
384 			e.printStackTrace();
385 			System.out.println( "--- end." );
386 			throw e;
387 		}
388 		catch( java.lang.Exception e )
389 		{
390 			System.out.println( "java.lang.Exception: " );
391 			System.out.println( e );
392 			e.printStackTrace();
393 			System.out.println( "--- end." );
394 			throw new com.sun.star.uno.RuntimeException( e.toString() );
395 		}
396 
397 		return null;
398 	}
399 
400 
401     //The function is copied and adapted from the UrlResolver.resolve.
402     //We cannot use the URLResolver because we need access to the bridge which has
403     //to be disposed when Applet.stop is called.
404     private Object resolve(XComponentContext xLocalContext, String dcp)
405         throws com.sun.star.connection.NoConnectException,
406             com.sun.star.connection.ConnectionSetupException,
407             com.sun.star.lang.IllegalArgumentException
408     {
409  		String conDcp = null;
410         String protDcp = null;
411         String rootOid = null;
412 
413         if(dcp.indexOf(';') == -1) {// use old style
414             conDcp = dcp;
415             protDcp = "iiop";
416             rootOid = "classic_uno";
417         }
418         else { // new style
419             int index = dcp.indexOf(':');
420             String url = dcp.substring(0, index).trim();
421             dcp = dcp.substring(index + 1).trim();
422 
423             index = dcp.indexOf(';');
424             conDcp = dcp.substring(0, index).trim();
425             dcp = dcp.substring(index + 1).trim();
426 
427             index = dcp.indexOf(';');
428             protDcp = dcp.substring(0, index).trim();
429             dcp = dcp.substring(index + 1).trim();
430 
431             rootOid = dcp.trim().trim();
432         }
433 
434         Object rootObject = null;
435         XBridgeFactory xBridgeFactory= null;
436 
437         XMultiComponentFactory xLocalServiceManager = xLocalContext.getServiceManager();
438         try {
439             xBridgeFactory = (XBridgeFactory)UnoRuntime.queryInterface(
440                     XBridgeFactory.class,
441                     xLocalServiceManager.createInstanceWithContext(
442                         "com.sun.star.bridge.BridgeFactory", xLocalContext));
443         } catch (com.sun.star.uno.Exception e) {
444             throw new com.sun.star.uno.RuntimeException(e.getMessage());
445         }
446         synchronized(this) {
447             if(mBridge == null) {
448                 Object connector= null;
449                 try {
450                     connector = xLocalServiceManager.createInstanceWithContext(
451                             "com.sun.star.connection.Connector", xLocalContext);
452                 } catch (com.sun.star.uno.Exception e) {
453                     throw new com.sun.star.uno.RuntimeException(e.getMessage());
454                 }
455                 XConnector connector_xConnector = (XConnector)UnoRuntime.queryInterface(XConnector.class, connector);
456                 // connect to the server
457                 XConnection xConnection = connector_xConnector.connect(conDcp);
458                 // create the bridge name. This should not be necessary if we pass an
459                 //empty string as bridge name into createBridge. Then we should always get
460                 //a new bridge. This does not work because of (i51323). Therefore we
461                 //create unique bridge names for the current process.
462                 String sBridgeName = "OOoBean_private_bridge_" + String.valueOf(m_nBridgeCounter++);
463                 try {
464                     mBridge = xBridgeFactory.createBridge(sBridgeName, protDcp, xConnection, null);
465                 } catch (com.sun.star.bridge.BridgeExistsException e) {
466                     throw new com.sun.star.uno.RuntimeException(e.getMessage());
467                 }
468             }
469             rootObject = mBridge.getInstance(rootOid);
470             return rootObject;
471         }
472     }
473 
474 
475 	/**
476 	 * Retrives a path to the office program folder.
477 	 *
478 	 * @return The path to the office program folder.
479 	 */
480 	static private String getProgramPath()
481 	{
482 		if (mProgramPath == null)
483 		{
484 			// determine name of executable soffice
485 			String aExec = OFFICE_APP_NAME; // default for UNIX
486 			String aOS = System.getProperty("os.name");
487 
488 			// running on Windows?
489 			if (aOS.startsWith("Windows"))
490 				aExec = OFFICE_APP_NAME + ".exe";
491 
492 			// add other non-UNIX operating systems here
493 			// ...
494 
495 			// find soffice executable relative to this class's class loader:
496 			File path = NativeLibraryLoader.getResource(
497 				LocalOfficeConnection.class.getClassLoader(), aExec);
498 			if (path != null)
499 				mProgramPath = path.getParent();
500 
501 			// default is ""
502 			if ( mProgramPath == null )
503 				mProgramPath = "";
504 		}
505 		return mProgramPath;
506 	}
507 
508 	/**
509 	 * Parses a connection URL.
510 	 * This method accepts a UNO URL with following format:<br />
511 	 * <pre>
512 	 * url    := uno:localoffice[,&lt;params&gt;];urp;StarOffice.NamingService
513 	 * params := &lt;path&gt;[,&lt;pipe&gt;]
514 	 * path   := path=&lt;pathv&gt;
515 	 * pipe   := pipe=&lt;pipev&gt;
516 	 * pathv  := platform_specific_path_to_the_local_office_distribution
517 	 * pipev  := local_office_connection_pipe_name
518 	 * </pre>
519 	 *
520 	 * <h4>Examples</h4>
521 	 * <ul>
522 	 * 	<li>"uno:localoffice,pipe=xyz_Office,path=/opt/openoffice11/program;urp;StarOffice.ServiceManager";
523 	 * 	<li>"uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager";
524 	 * </ul>
525 	 *
526 	 * @param url This is UNO URL which describes the type of a connection.
527 	 * @exception java.net.MalformedURLException when inappropreate URL was
528 	 *	provided.
529 	 */
530 	private void parseUnoUrlWithOfficePath(String url, String prefix)
531 		throws java.net.MalformedURLException
532 	{
533 		// Extruct parameters.
534 		int	idx	= url.indexOf(";urp;StarOffice.NamingService");
535 		if (idx < 0)
536 			throw new java.net.MalformedURLException(
537 				"Invalid UNO connection URL.");
538 		String	params	= url.substring(prefix.length(), idx + 1);
539 
540 		// Parse parameters.
541 		String	name	= null;
542 		String	path	= null;
543 		String	pipe	= null;
544 		char	ch;
545 		int		state	= 0;
546 		StringBuffer	buffer	= new StringBuffer();
547 		for(idx = 0; idx < params.length(); idx += 1) {
548 			ch	= params.charAt(idx);
549 			switch (state) {
550 			case 0:	// initial state
551 				switch(ch) {
552 				case ',':
553 					buffer.delete(0, buffer.length());
554 					state	= 1;
555 					break;
556 
557 				case ';':
558 					state	= 7;
559 					break;
560 
561 				default:
562 					buffer.delete(0, buffer.length());
563 					buffer.append(ch);
564 					state	= 1;
565 					break;
566 				}
567 				break;
568 
569 			case 1:	// parameter name
570 				switch(ch) {
571 				case ' ':
572 				case '=':
573 					name	= buffer.toString();
574 					state	= (ch == ' ')? 2: 3;
575 					break;
576 
577 				case ',':
578 				case ';':
579 					state	= -6;			// error: invalid name
580 					break;
581 
582 				default:
583 					buffer.append(ch);
584 					break;
585 				}
586 				break;
587 
588 			case 2:	// equal between the name and the value
589 				switch(ch) {
590 				case '=':
591 					state	= 3;
592 					break;
593 
594 				case ' ':
595 					break;
596 
597 				default:
598 					state	= -1;			// error: missing '='
599 					break;
600 				}
601 				break;
602 
603 			case 3:	// value leading spaces
604 				switch(ch) {
605 				case ' ':
606 					break;
607 
608 				default:
609 					buffer.delete(0, buffer.length());
610 					buffer.append(ch);
611 					state	= 4;
612 					break;
613 				}
614 				break;
615 
616 			case 4:	// value
617 				switch(ch) {
618 				case ' ':
619 				case ',':
620 				case ';':
621 					idx 	-= 1;			// put back the last read character
622 					state	= 5;
623 					if (name.equals("path")) {
624 						if (path == null)
625 							path	= buffer.toString();
626 						else
627 							state	= -3;	// error: more then one 'path'
628 					} else if (name.equals("pipe")) {
629 						if (pipe == null)
630 							pipe	= buffer.toString();
631 						else
632 							state	= -4;	// error: more then one 'pipe'
633 					} else
634 						state	= -2;		// error: unknown parameter
635 					buffer.delete(0, buffer.length());
636 					break;
637 
638 				default:
639 					buffer.append(ch);
640 					break;
641 				}
642 				break;
643 
644 			case 5:	// a delimeter after the value
645 				switch(ch) {
646 				case ' ':
647 					break;
648 
649 				case ',':
650 					state	= 6;
651 					break;
652 
653 				case ';':
654 					state	= 7;
655 					break;
656 
657 				default:
658 					state	= -5;			// error: ' ' inside the value
659 					break;
660 				}
661 				break;
662 
663 			case 6:	// leading spaces before next parameter name
664 				switch(ch) {
665 				case ' ':
666 					break;
667 
668 				default:
669 					buffer.delete(0, buffer.length());
670 					buffer.append(ch);
671 					state	= 1;
672 					break;
673 				}
674 				break;
675 
676 			default:
677 				throw new java.net.MalformedURLException(
678 					"Invalid UNO connection URL.");
679 			}
680 		}
681 		if (state != 7)
682 			throw new java.net.MalformedURLException(
683 				"Invalid UNO connection URL.");
684 
685 		// Set up the connection parameters.
686 		if (path != null)
687 			mProgramPath = path;
688 		if (pipe != null)
689 			mPipe = pipe;
690 	}
691 
692 	/* replaces each substring aSearch in aString by aReplace.
693 
694 		StringBuffer.replaceAll() is not avaialable in Java 1.3.x.
695 	 */
696     private static String replaceAll(String aString, String aSearch, String aReplace )
697     {
698         StringBuffer aBuffer = new StringBuffer(aString);
699 
700         int nPos = aString.length();
701         int nOfs = aSearch.length();
702 
703         while ( ( nPos = aString.lastIndexOf( aSearch, nPos - 1 ) ) > -1 )
704             aBuffer.replace( nPos, nPos+nOfs, aReplace );
705 
706         return aBuffer.toString();
707     }
708 
709 
710 	/** creates a unique pipe name.
711 	*/
712 	static String getPipeName()
713 	{
714 		// turn user name into a URL and file system safe name (% chars will not work)
715 		String aPipeName = System.getProperty("user.name") + OFFICE_ID_SUFFIX;
716 		aPipeName = replaceAll( aPipeName, "_", "%B7" );
717 		return replaceAll( replaceAll( java.net.URLEncoder.encode(aPipeName), "+", "%20" ), "%", "_" );
718 	}
719 
720 	/**
721 	 * @para This is an implementation of the native office service.
722 	 */
723 	private class OfficeService
724 		implements NativeService
725 	{
726 		/**
727 		 * Retrive the office service identifier.
728 		 *
729 		 * @return The identifier of the office service.
730 		 */
731 		public String getIdentifier()
732 		{
733 			if ( mPipe == null)
734 				return getPipeName();
735 			else
736 				return mPipe;
737 		}
738 
739 		/**
740 		 * Starts the office process.
741 		 */
742 		public void startupService()
743 			throws java.io.IOException
744 		{
745             int nSizeCmdArray = 4;
746             String sOption = null;
747             //examine if user specified command-line options in system properties.
748             //We may offer later a more sophisticated way of providing options if
749             //the need arises. Currently this is intended to ease the pain during
750             //development  with pre-release builds of OOo where one wants to start
751             //OOo with the -norestore options. The value of the property is simple
752             //passed on to the Runtime.exec call.
753             try {
754                 sOption = System.getProperty("com.sun.star.officebean.Options");
755                 if (sOption != null)
756                     nSizeCmdArray ++;
757             } catch (java.lang.SecurityException e)
758             {
759                 e.printStackTrace();
760             }
761            // create call with arguments
762 			String[] cmdArray = new String[nSizeCmdArray];
763 
764 			// read UNO_PATH environment variable to get path to soffice binary
765 			String unoPath = System.getenv("UNO_PATH");
766 			if (unoPath == null)
767                 throw new java.io.IOException( "UNO_PATH environment variable is not set (required system path to the office program directory)" );
768 
769 //			cmdArray[0] = (new File(getProgramPath(), OFFICE_APP_NAME)).getPath();
770 			cmdArray[0] = (new File(unoPath, OFFICE_APP_NAME)).getPath();
771 			cmdArray[1] = "-nologo";
772 			cmdArray[2] = "-nodefault";
773 			if ( mConnType.equals( "pipe" ) )
774 				cmdArray[3] = "-accept=pipe,name=" + getIdentifier() + ";" +
775 						  mProtocol + ";" + mInitialObject;
776 			else if ( mConnType.equals( "socket" ) )
777 				cmdArray[3] = "-accept=socket,port=" + mPort + ";urp";
778 			else
779 				throw new java.io.IOException( "not connection specified" );
780 
781             if (sOption != null)
782                 cmdArray[4] = sOption;
783 
784 			// start process
785 			mProcess = Runtime.getRuntime().exec(cmdArray);
786 			if ( mProcess == null )
787 				throw new RuntimeException( "cannot start soffice: " + cmdArray );
788             new StreamProcessor(mProcess.getInputStream(), System.out);
789             new StreamProcessor(mProcess.getErrorStream(), System.err);
790 		}
791 
792 		/**
793 		 * Retrives the ammount of time to wait for the startup.
794 		 *
795 		 * @return The ammount of time to wait in seconds(?).
796 		 */
797 		public int getStartupTime()
798 		{
799 			return 60;
800 		}
801 	}
802 
803 
804 
805     class StreamProcessor extends Thread
806     {
807         java.io.InputStream m_in;
808         java.io.PrintStream m_print;
809 
810         public StreamProcessor(final java.io.InputStream in, final java.io.PrintStream out)
811         {
812             m_in = in;
813             m_print = out;
814             start();
815         }
816 
817         public void run() {
818             java.io.BufferedReader r = new java.io.BufferedReader(
819                 new java.io.InputStreamReader(m_in) );
820             try {
821                 for ( ; ; ) {
822                     String s = r.readLine();
823                     if ( s == null ) {
824                         break;
825                     }
826                     m_print.println(s);
827                 }
828             } catch ( java.io.IOException e ) {
829                 e.printStackTrace( System.err );
830             }
831         }
832     }
833 
834 }
835