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[,<params>];urp;StarOffice.ServiceManager 94 * params := <path>[,<pipe>] 95 * path := path=<pathv> 96 * pipe := pipe=<pipev> 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[,<params>];urp;StarOffice.NamingService 351 * params := <path>[,<pipe>] 352 * path := path=<pathv> 353 * pipe := pipe=<pipev> 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