xref: /AOO41X/main/bean/com/sun/star/beans/LocalOfficeConnection.java (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
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.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.XEventListener;
38 import com.sun.star.bridge.XUnoUrlResolver;
39 import com.sun.star.uno.XComponentContext;
40 import com.sun.star.uno.UnoRuntime;
41 import com.sun.star.lib.uno.helper.UnoUrl;
42 import com.sun.star.lib.util.NativeLibraryLoader;
43 
44 /**
45  * This class reprecents a connection to the local office application.
46  * @deprecated
47  */
48 public class LocalOfficeConnection
49 	implements OfficeConnection
50 {
51 	public static final String		OFFICE_APP_NAME		= "soffice";
52 	public static final String		OFFICE_LIB_NAME		= "officebean";
53 	public static final String		OFFICE_ID_SUFFIX	= "_Office";
54 
55 	private Process				mProcess;
56 	private ContainerFactory		mContainerFactory;
57 	private XComponentContext		mContext;
58 
59 	private String				mURL;
60 	private String				mProgramPath;
61 	private String				mConnType;
62 	private String				mPipe;
63 	private String				mPort;
64 	private String				mProtocol;
65 	private String				mInitialObject;
66 
67 	private List				mComponents		= new Vector();
68 
69 	/**
70 	 * Constructor.
71 	 * Sets up paths to the office application and native libraries if
72 	 * values are available in <code>OFFICE_PROP_FILE</code> in the user
73 	 * home directory.<br />
74 	 * "com.sun.star.beans.path" - the office application directory;<br/>
75 	 * "com.sun.star.beans.libpath" - native libraries directory.
76 	 */
77 	public LocalOfficeConnection()
78 	{
79 		// init member vars
80 		try
81 		{
82 			setUnoUrl( "uno:pipe,name=" + getPipeName() + ";urp;StarOffice.ServiceManager" );
83 		}
84 		catch ( java.net.MalformedURLException e )
85 		{}
86 
87 		// load libofficebean.so/officebean.dll
88 		String aSharedLibName = getProgramPath() + java.io.File.separator +
89 			System.mapLibraryName(OFFICE_LIB_NAME);
90 		System.load( aSharedLibName );
91 	}
92 
93 	/**
94 	 * Sets a connection URL.
95 	 * This implementation accepts a UNO URL with following format:<br />
96 	 * <pre>
97 	 * url    := uno:localoffice[,&lt;params&gt;];urp;StarOffice.ServiceManager
98 	 * params := &lt;path&gt;[,&lt;pipe&gt;]
99 	 * path   := path=&lt;pathv&gt;
100 	 * pipe   := pipe=&lt;pipev&gt;
101 	 * pathv  := platform_specific_path_to_the_local_office_distribution
102 	 * pipev  := local_office_connection_pipe_name
103 	 * </pre>
104 	 *
105 	 * @param url This is UNO URL which discribes the type of a connection.
106 	 */
107 	public void setUnoUrl(String url)
108 		throws java.net.MalformedURLException
109 	{
110 		mURL	= null;
111 
112 		String prefix = "uno:localoffice";
113 		if ( url.startsWith(prefix) )
114 			parseUnoUrlWithOfficePath( url, prefix );
115 		else
116 		{
117 			try
118 			{
119 				UnoUrl aURL = UnoUrl.parseUnoUrl( url );
120 				mProgramPath = null;
121 				mConnType = aURL.getConnection();
122 				mPipe = (String) aURL.getConnectionParameters().get( "pipe" );
123 				mPort = (String) aURL.getConnectionParameters().get( "port" );
124 				mProtocol = aURL.getProtocol();
125 				mInitialObject = aURL.getRootOid();
126 			}
127 			catch ( com.sun.star.lang.IllegalArgumentException eIll )
128 			{
129 				throw new java.net.MalformedURLException(
130 					"Invalid UNO connection URL.");
131 			}
132 		}
133 		mURL	= url;
134 	}
135 
136 	/**
137 	 * Sets an AWT container catory.
138 	 *
139 	 * @param containerFactory This is a application provided AWT container
140 	 *	factory.
141 	 */
142 	public void setContainerFactory(ContainerFactory containerFactory)
143 	{
144 		mContainerFactory	= containerFactory;
145 	}
146 
147 	/**
148 	 * Retrives the UNO component context.
149 	 * Establishes a connection if necessary and initialises the
150 	 * UNO service manager if it has not already been initialised.
151 	 * This method can return <code>null</code> if it fails to connect
152 	 * to the office application.
153 	 *
154 	 * @return The office UNO component context.
155 	 */
156 	public XComponentContext getComponentContext()
157 	{
158 		if ( mContext == null )
159 			mContext = connect();
160 		return mContext;
161 	}
162 
163 	/**
164 	 * Creates an office window.
165 	 * The window is either a sub-class of java.awt.Canvas (local) or
166 	 * java.awt.Container (RVP).
167 	 *
168 	 * @param container This is an AWT container.
169 	 * @return The office window instance.
170 	 */
171 	public OfficeWindow createOfficeWindow(Container container)
172 	{
173 		return new LocalOfficeWindow(this);
174 	}
175 
176 	/**
177 	 * Closes the connection.
178 	 */
179 	public void dispose()
180 	{
181 		Iterator itr = mComponents.iterator();
182 		while (itr.hasNext() == true) {
183 			// ignore runtime exceptions in dispose
184 			try { ((XEventListener)itr.next()).disposing(null); }
185 			catch ( RuntimeException aExc ) {}
186 		}
187 		mComponents.clear();
188 
189 		mContainerFactory = null;
190 		mContext = null;
191 	}
192 
193 	/**
194 	 * Adds an event listener to the object.
195 	 *
196 	 * @param listener is a listener object.
197 	 */
198 	public void addEventListener(XEventListener listener)
199 	{
200 		mComponents.add(listener);
201 	}
202 
203 	/**
204 	 * Removes an event listener from the listener list.
205 	 *
206 	 * @param listener is a listener object.
207 	 */
208 	public void removeEventListener(XEventListener listener)
209 	{
210 		mComponents.remove(listener);
211 	}
212 
213 	/**
214 	 * Establishes the connection to the office.
215 	 */
216 	private XComponentContext connect()
217 	{
218 		try
219 		{
220 			// create default local component context
221 			XComponentContext xLocalContext =
222 			    com.sun.star.comp.helper.Bootstrap.createInitialComponentContext(null);
223 
224 			// initial serviceManager
225 			XMultiComponentFactory xLocalServiceManager = xLocalContext.getServiceManager();
226 
227 			// create a urlresolver
228 			Object urlResolver  = xLocalServiceManager.createInstanceWithContext(
229 			    "com.sun.star.bridge.UnoUrlResolver", xLocalContext );
230 
231 			// query for the XUnoUrlResolver interface
232 			XUnoUrlResolver xUrlResolver =
233 			    (XUnoUrlResolver) UnoRuntime.queryInterface( XUnoUrlResolver.class, urlResolver );
234 
235 			// try to connect to soffice
236 			Object aInitialObject = null;
237 			try
238 			{
239 				aInitialObject = xUrlResolver.resolve( mURL );
240 			}
241 			catch( com.sun.star.connection.NoConnectException e )
242 			{
243 				// launch soffice
244 				OfficeService aSOffice = new OfficeService();
245 				aSOffice.startupService();
246 
247 				// wait until soffice is started
248 				long nMaxMillis = System.currentTimeMillis() + 1000*aSOffice.getStartupTime();
249 				while ( aInitialObject == null )
250 				{
251 					try
252 					{
253 						// try to connect to soffice
254 						Thread.currentThread().sleep( 500 );
255 						aInitialObject = xUrlResolver.resolve( mURL );
256 					}
257 					catch( com.sun.star.connection.NoConnectException aEx )
258 					{
259 						// soffice did not start in time
260 						if ( System.currentTimeMillis() > nMaxMillis )
261 							throw aEx;
262 
263 					}
264 				}
265 			}
266 			finally
267 			{
268 			}
269 
270 			// XComponentContext
271 			if( null != aInitialObject )
272 			{
273 				XPropertySet xPropertySet = (XPropertySet)
274 					UnoRuntime.queryInterface( XPropertySet.class, aInitialObject);
275             			Object xContext = xPropertySet.getPropertyValue("DefaultContext");
276             			XComponentContext xComponentContext = (XComponentContext) UnoRuntime.queryInterface(
277 					XComponentContext.class, xContext);
278 				return xComponentContext;
279 			}
280 		}
281 		catch( com.sun.star.connection.NoConnectException e )
282 		{
283 			System.out.println( "Couldn't connect to remote server" );
284 			System.out.println( e.getMessage() );
285 		}
286 		catch( com.sun.star.connection.ConnectionSetupException e )
287 		{
288 			System.out.println( "Couldn't access necessary local resource to establish the interprocess connection" );
289 			System.out.println( e.getMessage() );
290 		}
291 		catch( com.sun.star.lang.IllegalArgumentException e )
292 		{
293 			System.out.println( "uno-url is syntactical illegal ( " + mURL + " )" );
294 			System.out.println( e.getMessage() );
295 		}
296 		catch( com.sun.star.uno.RuntimeException e )
297 		{
298 			System.out.println( "--- RuntimeException:" );
299 			System.out.println( e.getMessage() );
300 			e.printStackTrace();
301 			System.out.println( "--- end." );
302 			throw e;
303 		}
304 		catch( java.lang.Exception e )
305 		{
306 			System.out.println( "java.lang.Exception: " );
307 			System.out.println( e );
308 			e.printStackTrace();
309 			System.out.println( "--- end." );
310 			throw new com.sun.star.uno.RuntimeException( e.toString() );
311 		}
312 
313 		return null;
314 	}
315 
316 	/**
317 	 * Retrives a path to the office program folder.
318 	 *
319 	 * @return The path to the office program folder.
320 	 */
321 	private String getProgramPath()
322 	{
323 		if (mProgramPath == null)
324 		{
325 			// determine name of executable soffice
326 			String aExec = OFFICE_APP_NAME; // default for UNIX
327 			String aOS = System.getProperty("os.name");
328 
329 			// running on Windows?
330 			if (aOS.startsWith("Windows"))
331 				aExec = OFFICE_APP_NAME + ".exe";
332 
333 			// add other non-UNIX operating systems here
334 			// ...
335 
336 			// find soffice executable relative to this class's class loader:
337 			File path = NativeLibraryLoader.getResource(
338 				this.getClass().getClassLoader(), aExec);
339 			if (path != null) {
340 				mProgramPath = path.getParent();
341 		}
342 
343 			// default is ""
344 			if ( mProgramPath == null )
345 				mProgramPath = "";
346 		}
347 		return mProgramPath;
348 	}
349 
350 	/**
351 	 * Parses a connection URL.
352 	 * This method accepts a UNO URL with following format:<br />
353 	 * <pre>
354 	 * url    := uno:localoffice[,&lt;params&gt;];urp;StarOffice.NamingService
355 	 * params := &lt;path&gt;[,&lt;pipe&gt;]
356 	 * path   := path=&lt;pathv&gt;
357 	 * pipe   := pipe=&lt;pipev&gt;
358 	 * pathv  := platform_specific_path_to_the_local_office_distribution
359 	 * pipev  := local_office_connection_pipe_name
360 	 * </pre>
361 	 *
362 	 * <h4>Examples</h4>
363 	 * <ul>
364 	 * 	<li>"uno:localoffice,pipe=xyz_Office,path=/opt/openoffice11/program;urp;StarOffice.ServiceManager";
365 	 * 	<li>"uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager";
366 	 * </ul>
367 	 *
368 	 * @param url This is UNO URL which describes the type of a connection.
369 	 * @exception java.net.MalformedURLException when inappropreate URL was
370 	 *	provided.
371 	 */
372 	private void parseUnoUrlWithOfficePath(String url, String prefix)
373 		throws java.net.MalformedURLException
374 	{
375 		// Extruct parameters.
376 		int	idx	= url.indexOf(";urp;StarOffice.NamingService");
377 		if (idx < 0)
378 			throw new java.net.MalformedURLException(
379 				"Invalid UNO connection URL.");
380 		String	params	= url.substring(prefix.length(), idx + 1);
381 
382 		// Parse parameters.
383 		String	name	= null;
384 		String	path	= null;
385 		String	pipe	= null;
386 		char	ch;
387 		int		state	= 0;
388 		StringBuffer	buffer	= new StringBuffer();
389 		for(idx = 0; idx < params.length(); idx += 1) {
390 			ch	= params.charAt(idx);
391 			switch (state) {
392 			case 0:	// initial state
393 				switch(ch) {
394 				case ',':
395 					buffer.delete(0, buffer.length());
396 					state	= 1;
397 					break;
398 
399 				case ';':
400 					state	= 7;
401 					break;
402 
403 				default:
404 					buffer.delete(0, buffer.length());
405 					buffer.append(ch);
406 					state	= 1;
407 					break;
408 				}
409 				break;
410 
411 			case 1:	// parameter name
412 				switch(ch) {
413 				case ' ':
414 				case '=':
415 					name	= buffer.toString();
416 					state	= (ch == ' ')? 2: 3;
417 					break;
418 
419 				case ',':
420 				case ';':
421 					state	= -6;			// error: invalid name
422 					break;
423 
424 				default:
425 					buffer.append(ch);
426 					break;
427 				}
428 				break;
429 
430 			case 2:	// equal between the name and the value
431 				switch(ch) {
432 				case '=':
433 					state	= 3;
434 					break;
435 
436 				case ' ':
437 					break;
438 
439 				default:
440 					state	= -1;			// error: missing '='
441 					break;
442 				}
443 				break;
444 
445 			case 3:	// value leading spaces
446 				switch(ch) {
447 				case ' ':
448 					break;
449 
450 				default:
451 					buffer.delete(0, buffer.length());
452 					buffer.append(ch);
453 					state	= 4;
454 					break;
455 				}
456 				break;
457 
458 			case 4:	// value
459 				switch(ch) {
460 				case ' ':
461 				case ',':
462 				case ';':
463 					idx 	-= 1;			// put back the last read character
464 					state	= 5;
465 					if (name.equals("path")) {
466 						if (path == null)
467 							path	= buffer.toString();
468 						else
469 							state	= -3;	// error: more then one 'path'
470 					} else if (name.equals("pipe")) {
471 						if (pipe == null)
472 							pipe	= buffer.toString();
473 						else
474 							state	= -4;	// error: more then one 'pipe'
475 					} else
476 						state	= -2;		// error: unknown parameter
477 					buffer.delete(0, buffer.length());
478 					break;
479 
480 				default:
481 					buffer.append(ch);
482 					break;
483 				}
484 				break;
485 
486 			case 5:	// a delimeter after the value
487 				switch(ch) {
488 				case ' ':
489 					break;
490 
491 				case ',':
492 					state	= 6;
493 					break;
494 
495 				case ';':
496 					state	= 7;
497 					break;
498 
499 				default:
500 					state	= -5;			// error: ' ' inside the value
501 					break;
502 				}
503 				break;
504 
505 			case 6:	// leading spaces before next parameter name
506 				switch(ch) {
507 				case ' ':
508 					break;
509 
510 				default:
511 					buffer.delete(0, buffer.length());
512 					buffer.append(ch);
513 					state	= 1;
514 					break;
515 				}
516 				break;
517 
518 			default:
519 				throw new java.net.MalformedURLException(
520 					"Invalid UNO connection URL.");
521 			}
522 		}
523 		if (state != 7)
524 			throw new java.net.MalformedURLException(
525 				"Invalid UNO connection URL.");
526 
527 		// Set up the connection parameters.
528 		if (path != null)
529 			mProgramPath = path;
530 		if (pipe != null)
531 			mPipe = pipe;
532 	}
533 
534 	/* replaces each substring aSearch in aString by aReplace.
535 
536 		StringBuffer.replaceAll() is not avaialable in Java 1.3.x.
537 	 */
538     private static String replaceAll(String aString, String aSearch, String aReplace )
539     {
540         StringBuffer aBuffer = new StringBuffer(aString);
541 
542         int nPos = aString.length();
543         int nOfs = aSearch.length();
544 
545         while ( ( nPos = aString.lastIndexOf( aSearch, nPos - 1 ) ) > -1 )
546             aBuffer.replace( nPos, nPos+nOfs, aReplace );
547 
548         return aBuffer.toString();
549     }
550 
551 
552 	/** creates a unique pipe name.
553 	*/
554 	static String getPipeName()
555 	{
556 		// turn user name into a URL and file system safe name (% chars will not work)
557 		String aPipeName = System.getProperty("user.name") + OFFICE_ID_SUFFIX;
558 		aPipeName = replaceAll( aPipeName, "_", "%B7" );
559 		return replaceAll( replaceAll( java.net.URLEncoder.encode(aPipeName), "\\+", "%20" ), "%", "_" );
560 	}
561 
562 	/**
563 	 * @para This is an implementation of the native office service.
564      * @deprecated
565 	 */
566 	private class OfficeService
567 		implements NativeService
568 	{
569 		/**
570 		 * Retrive the office service identifier.
571 		 *
572 		 * @return The identifier of the office service.
573 		 */
574 		public String getIdentifier()
575 		{
576 			if ( mPipe == null)
577 				return getPipeName();
578 			else
579 				return mPipe;
580 		}
581 
582 		/**
583 		 * Starts the office process.
584 		 */
585 		public void startupService()
586 			throws java.io.IOException
587 		{
588            // create call with arguments
589 			String[] cmdArray = new String[4];
590 			cmdArray[0] = (new File(getProgramPath(), OFFICE_APP_NAME)).getPath();
591 			cmdArray[1] = "-nologo";
592 			cmdArray[2] = "-nodefault";
593 			if ( mConnType.equals( "pipe" ) )
594 				cmdArray[3] = "-accept=pipe,name=" + getIdentifier() + ";" +
595 						  mProtocol + ";" + mInitialObject;
596 			else if ( mConnType.equals( "socket" ) )
597 				cmdArray[3] = "-accept=socket,port=" + mPort + ";urp";
598 			else
599 				throw new java.io.IOException( "not connection specified" );
600 
601 			// start process
602 			mProcess = Runtime.getRuntime().exec(cmdArray);
603 			if ( mProcess == null )
604 				throw new RuntimeException( "cannot start soffice: " + cmdArray );
605 		}
606 
607 		/**
608 		 * Retrives the ammount of time to wait for the startup.
609 		 *
610 		 * @return The ammount of time to wait in seconds(?).
611 		 */
612 		public int getStartupTime()
613 		{
614 			return 60;
615 		}
616 	}
617 }
618