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