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