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[,<params>];urp;StarOffice.ServiceManager 98 * params := <path>[,<pipe>] 99 * path := path=<pathv> 100 * pipe := pipe=<pipev> 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[,<params>];urp;StarOffice.NamingService 355 * params := <path>[,<pipe>] 356 * path := path=<pathv> 357 * pipe := pipe=<pipev> 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