xref: /AOO41X/main/bean/com/sun/star/beans/LocalOfficeConnection.java (revision d4cc1e8c350bb591a80bbabe126ff6af34c125a2)
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