1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: LocalOfficeConnection.java,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 package com
.sun
.star
.beans
;
33 import java
.awt
.Container
;
35 import java
.util
.Iterator
;
36 import java
.util
.List
;
37 import java
.util
.Vector
;
39 import com
.sun
.star
.lang
.XMultiComponentFactory
;
40 import com
.sun
.star
.lang
.XEventListener
;
41 import com
.sun
.star
.bridge
.XUnoUrlResolver
;
42 import com
.sun
.star
.uno
.XComponentContext
;
43 import com
.sun
.star
.uno
.UnoRuntime
;
44 import com
.sun
.star
.lib
.uno
.helper
.UnoUrl
;
45 import com
.sun
.star
.lib
.util
.NativeLibraryLoader
;
48 * This class reprecents a connection to the local office application.
51 public class LocalOfficeConnection
52 implements OfficeConnection
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";
58 private Process mProcess
;
59 private ContainerFactory mContainerFactory
;
60 private XComponentContext mContext
;
63 private String mProgramPath
;
64 private String mConnType
;
67 private String mProtocol
;
68 private String mInitialObject
;
70 private List mComponents
= new Vector();
74 * Sets up paths to the office application and native libraries if
75 * values are available in <code>OFFICE_PROP_FILE</code> in the user
76 * home directory.<br />
77 * "com.sun.star.beans.path" - the office application directory;<br/>
78 * "com.sun.star.beans.libpath" - native libraries directory.
80 public LocalOfficeConnection()
85 setUnoUrl( "uno:pipe,name=" + getPipeName() + ";urp;StarOffice.ServiceManager" );
87 catch ( java
.net
.MalformedURLException e
)
90 // load libofficebean.so/officebean.dll
91 String aSharedLibName
= getProgramPath() + java
.io
.File
.separator
+
92 System
.mapLibraryName(OFFICE_LIB_NAME
);
93 System
.load( aSharedLibName
);
97 * Sets a connection URL.
98 * This implementation accepts a UNO URL with following format:<br />
100 * url := uno:localoffice[,<params>];urp;StarOffice.ServiceManager
101 * params := <path>[,<pipe>]
102 * path := path=<pathv>
103 * pipe := pipe=<pipev>
104 * pathv := platform_specific_path_to_the_local_office_distribution
105 * pipev := local_office_connection_pipe_name
108 * @param url This is UNO URL which discribes the type of a connection.
110 public void setUnoUrl(String url
)
111 throws java
.net
.MalformedURLException
115 String prefix
= "uno:localoffice";
116 if ( url
.startsWith(prefix
) )
117 parseUnoUrlWithOfficePath( url
, prefix
);
122 UnoUrl aURL
= UnoUrl
.parseUnoUrl( url
);
124 mConnType
= aURL
.getConnection();
125 mPipe
= (String
) aURL
.getConnectionParameters().get( "pipe" );
126 mPort
= (String
) aURL
.getConnectionParameters().get( "port" );
127 mProtocol
= aURL
.getProtocol();
128 mInitialObject
= aURL
.getRootOid();
130 catch ( com
.sun
.star
.lang
.IllegalArgumentException eIll
)
132 throw new java
.net
.MalformedURLException(
133 "Invalid UNO connection URL.");
140 * Sets an AWT container catory.
142 * @param containerFactory This is a application provided AWT container
145 public void setContainerFactory(ContainerFactory containerFactory
)
147 mContainerFactory
= containerFactory
;
151 * Retrives the UNO component context.
152 * Establishes a connection if necessary and initialises the
153 * UNO service manager if it has not already been initialised.
154 * This method can return <code>null</code> if it fails to connect
155 * to the office application.
157 * @return The office UNO component context.
159 public XComponentContext
getComponentContext()
161 if ( mContext
== null )
162 mContext
= connect();
167 * Creates an office window.
168 * The window is either a sub-class of java.awt.Canvas (local) or
169 * java.awt.Container (RVP).
171 * @param container This is an AWT container.
172 * @return The office window instance.
174 public OfficeWindow
createOfficeWindow(Container container
)
176 return new LocalOfficeWindow(this);
180 * Closes the connection.
182 public void dispose()
184 Iterator itr
= mComponents
.iterator();
185 while (itr
.hasNext() == true) {
186 // ignore runtime exceptions in dispose
187 try { ((XEventListener
)itr
.next()).disposing(null); }
188 catch ( RuntimeException aExc
) {}
192 mContainerFactory
= null;
197 * Adds an event listener to the object.
199 * @param listener is a listener object.
201 public void addEventListener(XEventListener listener
)
203 mComponents
.add(listener
);
207 * Removes an event listener from the listener list.
209 * @param listener is a listener object.
211 public void removeEventListener(XEventListener listener
)
213 mComponents
.remove(listener
);
217 * Establishes the connection to the office.
219 private XComponentContext
connect()
223 // create default local component context
224 XComponentContext xLocalContext
=
225 com
.sun
.star
.comp
.helper
.Bootstrap
.createInitialComponentContext(null);
227 // initial serviceManager
228 XMultiComponentFactory xLocalServiceManager
= xLocalContext
.getServiceManager();
230 // create a urlresolver
231 Object urlResolver
= xLocalServiceManager
.createInstanceWithContext(
232 "com.sun.star.bridge.UnoUrlResolver", xLocalContext
);
234 // query for the XUnoUrlResolver interface
235 XUnoUrlResolver xUrlResolver
=
236 (XUnoUrlResolver
) UnoRuntime
.queryInterface( XUnoUrlResolver
.class, urlResolver
);
238 // try to connect to soffice
239 Object aInitialObject
= null;
242 aInitialObject
= xUrlResolver
.resolve( mURL
);
244 catch( com
.sun
.star
.connection
.NoConnectException e
)
247 OfficeService aSOffice
= new OfficeService();
248 aSOffice
.startupService();
250 // wait until soffice is started
251 long nMaxMillis
= System
.currentTimeMillis() + 1000*aSOffice
.getStartupTime();
252 while ( aInitialObject
== null )
256 // try to connect to soffice
257 Thread
.currentThread().sleep( 500 );
258 aInitialObject
= xUrlResolver
.resolve( mURL
);
260 catch( com
.sun
.star
.connection
.NoConnectException aEx
)
262 // soffice did not start in time
263 if ( System
.currentTimeMillis() > nMaxMillis
)
274 if( null != aInitialObject
)
276 XPropertySet xPropertySet
= (XPropertySet
)
277 UnoRuntime
.queryInterface( XPropertySet
.class, aInitialObject
);
278 Object xContext
= xPropertySet
.getPropertyValue("DefaultContext");
279 XComponentContext xComponentContext
= (XComponentContext
) UnoRuntime
.queryInterface(
280 XComponentContext
.class, xContext
);
281 return xComponentContext
;
284 catch( com
.sun
.star
.connection
.NoConnectException e
)
286 System
.out
.println( "Couldn't connect to remote server" );
287 System
.out
.println( e
.getMessage() );
289 catch( com
.sun
.star
.connection
.ConnectionSetupException e
)
291 System
.out
.println( "Couldn't access necessary local resource to establish the interprocess connection" );
292 System
.out
.println( e
.getMessage() );
294 catch( com
.sun
.star
.lang
.IllegalArgumentException e
)
296 System
.out
.println( "uno-url is syntactical illegal ( " + mURL
+ " )" );
297 System
.out
.println( e
.getMessage() );
299 catch( com
.sun
.star
.uno
.RuntimeException e
)
301 System
.out
.println( "--- RuntimeException:" );
302 System
.out
.println( e
.getMessage() );
304 System
.out
.println( "--- end." );
307 catch( java
.lang
.Exception e
)
309 System
.out
.println( "java.lang.Exception: " );
310 System
.out
.println( e
);
312 System
.out
.println( "--- end." );
313 throw new com
.sun
.star
.uno
.RuntimeException( e
.toString() );
320 * Retrives a path to the office program folder.
322 * @return The path to the office program folder.
324 private String
getProgramPath()
326 if (mProgramPath
== null)
328 // determine name of executable soffice
329 String aExec
= OFFICE_APP_NAME
; // default for UNIX
330 String aOS
= System
.getProperty("os.name");
332 // running on Windows?
333 if (aOS
.startsWith("Windows"))
334 aExec
= OFFICE_APP_NAME
+ ".exe";
336 // add other non-UNIX operating systems here
339 // find soffice executable relative to this class's class loader:
340 File path
= NativeLibraryLoader
.getResource(
341 this.getClass().getClassLoader(), aExec
);
343 mProgramPath
= path
.getParent();
347 if ( mProgramPath
== null )
354 * Parses a connection URL.
355 * This method accepts a UNO URL with following format:<br />
357 * url := uno:localoffice[,<params>];urp;StarOffice.NamingService
358 * params := <path>[,<pipe>]
359 * path := path=<pathv>
360 * pipe := pipe=<pipev>
361 * pathv := platform_specific_path_to_the_local_office_distribution
362 * pipev := local_office_connection_pipe_name
367 * <li>"uno:localoffice,pipe=xyz_Office,path=/opt/openoffice11/program;urp;StarOffice.ServiceManager";
368 * <li>"uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager";
371 * @param url This is UNO URL which describes the type of a connection.
372 * @exception java.net.MalformedURLException when inappropreate URL was
375 private void parseUnoUrlWithOfficePath(String url
, String prefix
)
376 throws java
.net
.MalformedURLException
378 // Extruct parameters.
379 int idx
= url
.indexOf(";urp;StarOffice.NamingService");
381 throw new java
.net
.MalformedURLException(
382 "Invalid UNO connection URL.");
383 String params
= url
.substring(prefix
.length(), idx
+ 1);
391 StringBuffer buffer
= new StringBuffer();
392 for(idx
= 0; idx
< params
.length(); idx
+= 1) {
393 ch
= params
.charAt(idx
);
395 case 0: // initial state
398 buffer
.delete(0, buffer
.length());
407 buffer
.delete(0, buffer
.length());
414 case 1: // parameter name
418 name
= buffer
.toString();
419 state
= (ch
== ' ')?
2: 3;
424 state
= -6; // error: invalid name
433 case 2: // equal between the name and the value
443 state
= -1; // error: missing '='
448 case 3: // value leading spaces
454 buffer
.delete(0, buffer
.length());
466 idx
-= 1; // put back the last read character
468 if (name
.equals("path")) {
470 path
= buffer
.toString();
472 state
= -3; // error: more then one 'path'
473 } else if (name
.equals("pipe")) {
475 pipe
= buffer
.toString();
477 state
= -4; // error: more then one 'pipe'
479 state
= -2; // error: unknown parameter
480 buffer
.delete(0, buffer
.length());
489 case 5: // a delimeter after the value
503 state
= -5; // error: ' ' inside the value
508 case 6: // leading spaces before next parameter name
514 buffer
.delete(0, buffer
.length());
522 throw new java
.net
.MalformedURLException(
523 "Invalid UNO connection URL.");
527 throw new java
.net
.MalformedURLException(
528 "Invalid UNO connection URL.");
530 // Set up the connection parameters.
537 /* replaces each substring aSearch in aString by aReplace.
539 StringBuffer.replaceAll() is not avaialable in Java 1.3.x.
541 private static String
replaceAll(String aString
, String aSearch
, String aReplace
)
543 StringBuffer aBuffer
= new StringBuffer(aString
);
545 int nPos
= aString
.length();
546 int nOfs
= aSearch
.length();
548 while ( ( nPos
= aString
.lastIndexOf( aSearch
, nPos
- 1 ) ) > -1 )
549 aBuffer
.replace( nPos
, nPos
+nOfs
, aReplace
);
551 return aBuffer
.toString();
555 /** creates a unique pipe name.
557 static String
getPipeName()
559 // turn user name into a URL and file system safe name (% chars will not work)
560 String aPipeName
= System
.getProperty("user.name") + OFFICE_ID_SUFFIX
;
561 aPipeName
= replaceAll( aPipeName
, "_", "%B7" );
562 return replaceAll( replaceAll( java
.net
.URLEncoder
.encode(aPipeName
), "\\+", "%20" ), "%", "_" );
566 * @para This is an implementation of the native office service.
569 private class OfficeService
570 implements NativeService
573 * Retrive the office service identifier.
575 * @return The identifier of the office service.
577 public String
getIdentifier()
580 return getPipeName();
586 * Starts the office process.
588 public void startupService()
589 throws java
.io
.IOException
591 // create call with arguments
592 String
[] cmdArray
= new String
[4];
593 cmdArray
[0] = (new File(getProgramPath(), OFFICE_APP_NAME
)).getPath();
594 cmdArray
[1] = "-nologo";
595 cmdArray
[2] = "-nodefault";
596 if ( mConnType
.equals( "pipe" ) )
597 cmdArray
[3] = "-accept=pipe,name=" + getIdentifier() + ";" +
598 mProtocol
+ ";" + mInitialObject
;
599 else if ( mConnType
.equals( "socket" ) )
600 cmdArray
[3] = "-accept=socket,port=" + mPort
+ ";urp";
602 throw new java
.io
.IOException( "not connection specified" );
605 mProcess
= Runtime
.getRuntime().exec(cmdArray
);
606 if ( mProcess
== null )
607 throw new RuntimeException( "cannot start soffice: " + cmdArray
);
611 * Retrives the ammount of time to wait for the startup.
613 * @return The ammount of time to wait in seconds(?).
615 public int getStartupTime()