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 package helper; 28 29 //import com.sun.star.bridge.UnoUrlResolver; 30 import com.sun.star.beans.XFastPropertySet; 31 import com.sun.star.bridge.XUnoUrlResolver; 32 import com.sun.star.container.XEnumeration; 33 import com.sun.star.container.XEnumerationAccess; 34 import com.sun.star.frame.XDesktop; 35 import com.sun.star.lang.XMultiComponentFactory; 36 import com.sun.star.lang.XMultiServiceFactory; 37 import com.sun.star.uno.UnoRuntime; 38 import com.sun.star.uno.XComponentContext; 39 import com.sun.star.util.XCloseable; 40 import com.sun.star.util.XStringSubstitution; 41 42 import java.io.File; 43 import java.io.PrintWriter; 44 import java.util.StringTokenizer; 45 46 import lib.TestParameters; 47 48 import share.DescEntry; 49 import share.LogWriter; 50 51 import util.DynamicClassLoader; 52 import util.PropertyName; 53 import util.utils; 54 55 /** 56 * This class will connect the office and start it if possible 57 * 58 */ 59 public class OfficeProvider implements AppProvider 60 { 61 62 private static boolean debug = false; 63 64 /** 65 * copy the user layer to a safe place, usualy to $TMP/user_backup$USER 66 * @param param 67 * @param msf 68 */ 69 public void backupUserLayer(TestParameters param, XMultiServiceFactory msf) 70 { 71 try 72 { 73 final XStringSubstitution sts = createStringSubstitution(msf); 74 debug = param.getBool(PropertyName.DEBUG_IS_ACTIVE); 75 76 String userLayer = sts.getSubstituteVariableValue("$(user)"); 77 userLayer = getDirSys(userLayer); 78 param.put("userLayer", userLayer); 79 80 final String copyLayer = util.utils.getUsersTempDir() + System.getProperty("file.separator") + 81 "user_backup" + 82 System.getProperty("user.name"); 83 param.put("copyLayer", copyLayer); 84 85 86 dbg(" copy '" + userLayer + "' ->" + copyLayer + "'"); 87 // Slow machines the copy job could spend some time. To avoid activating of OfficeWatcher it must be pinged 88 OfficeWatcherPing owp = new OfficeWatcherPing((OfficeWatcher) param.get(PropertyName.OFFICE_WATCHER)); 89 owp.start(); 90 91 deleteFilesAndDirector (new File(copyLayer)); 92 FileTools.copyDirectory(new File(userLayer), new File(copyLayer), new String[] 93 { 94 "temp" 95 }); 96 97 owp.finish(); 98 99 } 100 catch (com.sun.star.container.NoSuchElementException e) 101 { 102 System.out.println("User Variable '$(user)' not defined."); 103 } 104 catch (java.io.IOException e) 105 { 106 System.out.println("Couldn't backup user layer"); 107 e.printStackTrace(); 108 } 109 } 110 111 /** 112 * Dispose the office. 113 * This method can only be used, if the office was connected in the first 114 * place: getManager() was called first. 115 * @param param 116 * @return return true if desktop is terminates, else false 117 */ 118 public boolean disposeManager(lib.TestParameters param) 119 { 120 121 XMultiServiceFactory msf = (XMultiServiceFactory) param.getMSF(); 122 123 if (msf == null) 124 { 125 return true; 126 } 127 else 128 { 129 XDesktop desk = null; 130 131 try 132 { 133 desk = UnoRuntime.queryInterface(XDesktop.class, msf.createInstance("com.sun.star.frame.Desktop")); 134 } 135 catch (com.sun.star.uno.Exception ue) 136 { 137 return false; 138 } 139 140 msf = null; 141 142 if (desk != null) 143 { 144 desk.terminate(); 145 146 return true; 147 } 148 else 149 { 150 return false; 151 } 152 } 153 } 154 155 /** 156 * Method to get the ServiceManager of an Office 157 * @param param 158 * @return 159 */ 160 public Object getManager(lib.TestParameters param) 161 { 162 String errorMessage = null; 163 boolean bAppExecutionHasWarning = false; 164 debug = param.getBool(PropertyName.DEBUG_IS_ACTIVE); 165 166 String additionalArgs = (String) param.get( 167 "AdditionalConnectionArguments"); 168 169 if (additionalArgs == null) 170 { 171 additionalArgs = ";"; 172 } 173 else 174 { 175 additionalArgs = "," + additionalArgs + ";"; 176 } 177 178 final String cncstr = "uno:" + param.get("ConnectionString") + ";urp" + 179 additionalArgs + "StarOffice.ServiceManager"; 180 181 System.out.println("Connecting the Office with " + cncstr); 182 183 XMultiServiceFactory msf = connectOffice(cncstr); 184 185 // if the office is running and the office crashes while testing it could 186 // be usesfull to restart the office if possible and continuing the tests. 187 // Example: The UNO-API-Tests in the projects will be executed by calling 188 // 'damke'. This connects to an existing office. If the office crashes 189 // it is usefull to restart the office and continuing the tests. 190 if ((param.getBool(util.PropertyName.AUTO_RESTART)) && (msf != null)) 191 { 192 makeAppExecCommand(msf, param); 193 } 194 195 if (msf == null) 196 { 197 String exc = ""; 198 Exception exConnectFailed = null; 199 boolean isExecutable = false; 200 boolean isAppKnown = ((cncstr.indexOf("host=localhost") > 0) || (cncstr.indexOf("pipe,name=") > 0)); 201 isAppKnown &= !((String) param.get("AppExecutionCommand")).equals(""); 202 203 if (isAppKnown) 204 { 205 dbg("Local Connection trying to start the Office"); 206 207 //ensure that a pending officewatcher gets finished before a new 208 //office is started 209 final OfficeWatcher ow_old = (OfficeWatcher) param.get("Watcher"); 210 211 if (ow_old != null) 212 { 213 ow_old.finish = true; 214 } 215 216 final String cmd = (String) param.get("AppExecutionCommand"); 217 dbg("AppExecutionCommand: " + cmd); 218 // validate the AppExecutionCommand, but try it out anyway. 219 // keep the error message for later. 220 errorMessage = 221 util.utils.validateAppExecutionCommand(cmd, (String) param.get("OperatingSystem")); 222 if (errorMessage.startsWith("Error")) 223 { 224 System.out.println(errorMessage); 225 return null; 226 } 227 bAppExecutionHasWarning = !errorMessage.equals("OK"); 228 229 final DynamicClassLoader dcl = new DynamicClassLoader(); 230 final LogWriter log = (LogWriter) dcl.getInstance( 231 (String) param.get("LogWriter")); 232 233 //create empty entry 234 final DescEntry Entry = new DescEntry(); 235 Entry.entryName = "office"; 236 Entry.longName = "office"; 237 Entry.EntryType = "placebo"; 238 Entry.isOptional = false; 239 Entry.isToTest = false; 240 Entry.SubEntryCount = 0; 241 Entry.hasErrorMsg = false; 242 Entry.State = "non possible"; 243 Entry.UserDefinedParams = param; 244 245 log.initialize(Entry, debug); 246 247 final ProcessHandler ph = new ProcessHandler(cmd, (PrintWriter) log); 248 isExecutable = ph.executeAsynchronously(); 249 250 if (isExecutable) 251 { 252 param.put("AppProvider", ph); 253 final OfficeWatcher ow = new OfficeWatcher(param); 254 param.put("Watcher", ow); 255 ow.start(); 256 ow.ping(); 257 } 258 259 int k = 0; 260 261 // wait up to 21 seconds to get an office connection 262 while ((k < 42) && (msf == null)) 263 { 264 try 265 { 266 msf = connect(cncstr); 267 } 268 catch (com.sun.star.uno.Exception ue) 269 { 270 exConnectFailed = ue; 271 exc = ue.getMessage(); 272 } 273 catch (java.lang.Exception je) 274 { 275 exConnectFailed = je; 276 exc = je.getMessage(); 277 } 278 if (msf == null) 279 { 280 try 281 { 282 Thread.sleep(k * 500); 283 } 284 catch (InterruptedException ex) 285 { 286 } 287 } 288 k++; 289 } 290 291 if (msf == null) 292 { 293 System.out.println("Exception while connecting.\n" + exConnectFailed); 294 if (exc != null) 295 { 296 System.out.println(exc); 297 } 298 if (bAppExecutionHasWarning) 299 { 300 System.out.println(errorMessage); 301 } 302 } 303 else if (isExecutable) 304 { 305 if (!param.getBool(util.PropertyName.DONT_BACKUP_USERLAYER)) 306 { 307 backupUserLayer(param, msf); 308 } 309 } 310 } 311 else 312 { 313 System.out.println("Could not connect an Office and cannot start one.\n".concat("please start an office with following parameter:\n"). 314 concat("\nsoffice -accept=").concat((String) param.get("ConnectionString")).concat(";urp;\n")); 315 if (bAppExecutionHasWarning) 316 { 317 System.out.println(errorMessage); 318 } 319 } 320 } 321 322 return msf; 323 } 324 325 /** 326 * Connect an Office 327 * @param connectStr 328 * @return 329 * @throws com.sun.star.uno.Exception 330 * @throws com.sun.star.uno.RuntimeException 331 * @throws com.sun.star.connection.NoConnectException 332 * @throws Exception 333 */ 334 protected static XMultiServiceFactory connect(String connectStr) 335 throws com.sun.star.uno.Exception, 336 com.sun.star.uno.RuntimeException, 337 com.sun.star.connection.NoConnectException, 338 Exception 339 { 340 341 // Get component context 342 final XComponentContext xcomponentcontext = com.sun.star.comp.helper.Bootstrap.createInitialComponentContext(null); 343 344 // initial serviceManager 345 final XMultiComponentFactory xLocalServiceManager = xcomponentcontext.getServiceManager(); 346 347 // create a connector, so that it can contact the office 348 // XUnoUrlResolver urlResolver = UnoUrlResolver.create(xcomponentcontext); 349 final Object xUrlResolver = xLocalServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", xcomponentcontext); 350 final XUnoUrlResolver urlResolver = UnoRuntime.queryInterface(XUnoUrlResolver.class, xUrlResolver); 351 352 final Object rInitialObject = urlResolver.resolve(connectStr); 353 354 XMultiServiceFactory xMSF = null; 355 356 if (rInitialObject != null) 357 { 358 // debug = true; 359 dbg("resolved url"); 360 361 xMSF = UnoRuntime.queryInterface(XMultiServiceFactory.class, rInitialObject); 362 } 363 364 return xMSF; 365 } 366 367 /** 368 * Close an office. 369 * @param param The test parameters. 370 * @param closeIfPossible If true, close even if 371 * it was running before the test 372 */ 373 public boolean closeExistingOffice(lib.TestParameters param, boolean closeIfPossible) 374 { 375 376 XMultiServiceFactory msf = (XMultiServiceFactory) param.getMSF(); 377 final boolean alreadyConnected = (msf != null); 378 debug = param.getBool(PropertyName.DEBUG_IS_ACTIVE); 379 380 if (alreadyConnected) 381 { 382 dbg("try to get ProcessHandler"); 383 384 final ProcessHandler ph = (ProcessHandler) param.get("AppProvider"); 385 386 if (ph != null) 387 { 388 dbg("ProcessHandler != null"); 389 390 disposeOffice(msf, param); 391 392 // dispose watcher in case it's still running. 393 dbg("try to get OfficeWatcher"); 394 395 final OfficeWatcher ow = (OfficeWatcher) param.get("Watcher"); 396 397 if ((ow != null) && ow.isAlive()) 398 { 399 dbg("OfficeWatcher will be finished"); 400 ow.finish = true; 401 } 402 else 403 { 404 dbg("OfficeWatcher seems to be finished"); 405 } 406 407 return true; 408 } 409 else 410 { 411 if (closeIfPossible) 412 { 413 return disposeOffice(msf, param); 414 } 415 } 416 } 417 else 418 { 419 final String cncstr = "uno:" + param.get("ConnectionString") + 420 ";urp;StarOffice.ServiceManager"; 421 dbg("try to connect office"); 422 msf = connectOffice(cncstr); 423 424 if (closeIfPossible) 425 { 426 return disposeOffice(msf, param); 427 } 428 } 429 dbg("closeExistingOffice finished"); 430 return true; 431 } 432 433 private XMultiServiceFactory connectOffice(String cncstr) 434 { 435 XMultiServiceFactory msf = null; 436 String exc = ""; 437 // debug = true; 438 439 dbg("trying to connect to " + cncstr); 440 441 try 442 { 443 msf = connect(cncstr); 444 } 445 catch (com.sun.star.uno.Exception ue) 446 { 447 exc = ue.getMessage(); 448 } 449 catch (java.lang.Exception je) 450 { 451 exc = je.getMessage(); 452 } 453 454 if (debug && exc != null && exc.length() != 0) 455 { 456 if (exc == null) 457 { 458 exc = ""; 459 } 460 dbg("Could not connect an Office. " + exc); 461 } 462 463 return msf; 464 } 465 466 private synchronized boolean disposeOffice(XMultiServiceFactory msf, 467 TestParameters param) 468 { 469 XDesktop desk = null; 470 471 debug = param.getBool(PropertyName.DEBUG_IS_ACTIVE); 472 473 boolean result = true; 474 475 if (msf != null) 476 { 477 478 // disable QuickStarter 479 try 480 { 481 Object quickStarter = msf.createInstance("com.sun.star.office.Quickstart"); 482 XFastPropertySet fps = UnoRuntime.queryInterface(XFastPropertySet.class, quickStarter); 483 fps.setFastPropertyValue(0, false); 484 } 485 catch (com.sun.star.uno.Exception ex) 486 { 487 dbg("ERROR: Could not disable QuickStarter: " + ex.toString()); 488 } 489 490 try 491 { 492 desk = UnoRuntime.queryInterface(XDesktop.class, msf.createInstance("com.sun.star.frame.Desktop")); 493 msf = null; 494 495 if (desk != null) 496 { 497 final boolean allClosed = closeAllWindows(desk); 498 499 if (!allClosed) 500 { 501 dbg("Couldn't close all office windows!"); 502 } 503 504 dbg("Trying to terminate the desktop"); 505 506 desk.terminate(); 507 dbg("Desktop terminated"); 508 509 try 510 { 511 final int closeTime = param.getInt(util.PropertyName.OFFICE_CLOSE_TIME_OUT); 512 dbg("the Office has " + closeTime / 1000 + " seconds for closing..."); 513 Thread.sleep(closeTime); 514 } 515 catch (java.lang.InterruptedException e) 516 { 517 } 518 } 519 } 520 catch (com.sun.star.uno.Exception ue) 521 { 522 result = false; 523 } 524 catch (com.sun.star.lang.DisposedException ue) 525 { 526 result = false; 527 } 528 } 529 530 final String AppKillCommand = (String) param.get(util.PropertyName.APP_KILL_COMMAND); 531 if (AppKillCommand != null) 532 { 533 String sAppKillCommand = StringHelper.removeSurroundQuoteIfExists(AppKillCommand); 534 final StringTokenizer aKillCommandToken = new StringTokenizer(sAppKillCommand, ";"); 535 while (aKillCommandToken.hasMoreTokens()) 536 { 537 final String sKillCommand = aKillCommandToken.nextToken(); 538 dbg("User defined an application to destroy the started process. Trying to execute: " + sKillCommand); 539 540 final ProcessHandler pHdl = new ProcessHandler(sKillCommand, 1000); // 3000 seems to be too long 541 pHdl.runCommand(); 542 543 pHdl.kill(); 544 } 545 } 546 547 final ProcessHandler ph = (ProcessHandler) param.get("AppProvider"); 548 549 if (ph != null) 550 { 551 // dispose watcher in case it's still running. 552 final OfficeWatcher ow = (OfficeWatcher) param.get("Watcher"); 553 554 if ((ow != null) && ow.isAlive()) 555 { 556 ow.finish = true; 557 } 558 559 ph.kill(); 560 } 561 562 param.remove("AppProvider"); 563 param.remove("ServiceFactory"); 564 565 if (!param.getBool(util.PropertyName.DONT_BACKUP_USERLAYER)) 566 { 567 //copy user_backup into user layer 568 try 569 { 570 final String userLayer = (String) param.get("userLayer"); 571 final String copyLayer = (String) param.get("copyLayer"); 572 if (userLayer != null && copyLayer != null) 573 { 574 deleteFilesAndDirector(new File(userLayer)); 575 final File copyFile = new File(copyLayer); 576 dbg("copy '" + copyFile + "' -> '" + userLayer + "'"); 577 FileTools.copyDirectory(copyFile, new File(userLayer), new String[] 578 { 579 "temp" 580 }); 581 dbg("copy '" + copyFile + "' -> '" + userLayer + "' finished"); 582 583 // remove all user_backup folder in temp dir 584 // this is for the case the runner was killed and some old backup folder still stay in temp dir 585 586 587 } 588 else 589 { 590 System.out.println("Cannot copy layer: '" + copyLayer + "' back to user layer: '" + userLayer + "'"); 591 } 592 } 593 catch (java.io.IOException e) 594 { 595 dbg("Couldn't recover from backup\n" + e.getMessage()); 596 } 597 } 598 return result; 599 } 600 601 protected boolean closeAllWindows(XDesktop desk) 602 { 603 final XEnumerationAccess compEnumAccess = desk.getComponents(); 604 final XEnumeration compEnum = compEnumAccess.createEnumeration(); 605 boolean res = true; 606 607 try 608 { 609 while (compEnum.hasMoreElements()) 610 { 611 final XCloseable closer = UnoRuntime.queryInterface(XCloseable.class, compEnum.nextElement()); 612 613 if (closer != null) 614 { 615 closer.close(true); 616 } 617 } 618 } 619 catch (com.sun.star.util.CloseVetoException cve) 620 { 621 res = false; 622 } 623 catch (com.sun.star.container.NoSuchElementException nsee) 624 { 625 res = false; 626 } 627 catch (com.sun.star.lang.WrappedTargetException wte) 628 { 629 res = false; 630 } 631 632 return res; 633 } 634 635 public static XStringSubstitution createStringSubstitution(XMultiServiceFactory xMSF) 636 { 637 Object xPathSubst = null; 638 639 try 640 { 641 xPathSubst = xMSF.createInstance( 642 "com.sun.star.util.PathSubstitution"); 643 } 644 catch (com.sun.star.uno.Exception e) 645 { 646 e.printStackTrace(); 647 } 648 649 if (xPathSubst != null) 650 { 651 return UnoRuntime.queryInterface(XStringSubstitution.class, xPathSubst); 652 } 653 else 654 { 655 return null; 656 } 657 } 658 659 /** 660 * converts directory without 'file:///' prefix. 661 * and System dependend file separator 662 * @param dir 663 * @return 664 */ 665 public static String getDirSys(String dir) 666 { 667 String sysDir = ""; 668 669 final int idx = dir.indexOf("file://"); 670 671 final int idx2 = dir.indexOf("file:///"); 672 673 // remove leading 'file://' 674 if (idx < 0) 675 { 676 sysDir = dir; 677 } 678 else 679 { 680 sysDir = dir.substring("file://".length()); 681 } 682 683 sysDir = utils.replaceAll13(sysDir, "%20", " "); 684 685 // append '/' if not there (e.g. linux) 686 if (sysDir.charAt(sysDir.length() - 1) != '/') 687 { 688 sysDir += "/"; 689 } 690 691 // remove leading '/' and replace others with '\' on windows machines 692 final String sep = System.getProperty("file.separator"); 693 694 if (sep.equalsIgnoreCase("\\")) 695 { 696 if (!(idx2 < 0)) 697 { 698 sysDir = sysDir.substring(1); 699 } 700 else 701 { 702 //network path 703 sysDir = "//" + sysDir; 704 } 705 sysDir = sysDir.replace('/', '\\'); 706 } 707 708 return sysDir; 709 } 710 711 /** 712 * If the office is connected but the <CODE>AppExecutionCommand</CODE> is not set, 713 * this function asks the office for its location and fill the 714 * <CODE>AppExecutionCommand</CODE> with valid contet. 715 * This function was only called if parameter <CODE>AutoRestart</CODE> is set. 716 * @param msf the <CODE>MultiServiceFactory</CODE> 717 * @param param the <CODE>TestParameters</CODE> 718 */ 719 private static void makeAppExecCommand(XMultiServiceFactory msf, TestParameters param) 720 { 721 debug = param.getBool(PropertyName.DEBUG_IS_ACTIVE); 722 723 // get existing AppExecutionCommand if available, else empty string 724 String command = (String) param.get(util.PropertyName.APP_EXECUTION_COMMAND); 725 726 String connectionString; 727 if (param.getBool(util.PropertyName.USE_PIPE_CONNECTION) == true) 728 { 729 // This is the default behaviour 730 connectionString = (String) param.get(util.PropertyName.PIPE_CONNECTION_STRING); 731 } 732 else 733 { 734 // is used if UsePipeConnection=false 735 connectionString = (String) param.get(util.PropertyName.CONNECTION_STRING); 736 } 737 738 String sysBinDir = ""; 739 740 try 741 { 742 sysBinDir = utils.getSystemURL(utils.expandMacro(msf, "$SYSBINDIR")); 743 } 744 catch (java.lang.Exception e) 745 { 746 dbg("could not get system binary directory"); 747 return; 748 } 749 750 // does the existing command show to the connected office? 751 if (command.indexOf(sysBinDir) == -1) 752 { 753 command = sysBinDir + System.getProperty("file.separator") + "soffice" + 754 " -norestore -accept=" + connectionString + ";urp;"; 755 } 756 757 dbg("update AppExecutionCommand: " + command); 758 759 param.put(util.PropertyName.APP_EXECUTION_COMMAND, command); 760 } 761 762 private static void dbg(String message) 763 { 764 if (debug) 765 { 766 System.out.println(utils.getDateTime() + "OfficeProvider: " + message); 767 } 768 769 } 770 771 private class OfficeWatcherPing extends Thread 772 { 773 774 private final OfficeWatcher ow; 775 private boolean bStop = false; 776 777 public OfficeWatcherPing(OfficeWatcher ow) 778 { 779 this.ow = ow; 780 } 781 782 @Override 783 public void run() 784 { 785 System.out.println(utils.getDateTime() + "OfficeProvider:Owp: start "); 786 787 while (!bStop) 788 { 789 System.out.println(utils.getDateTime() + "OfficeProvider:Owp: ping "); 790 ow.ping(); 791 try 792 { 793 System.out.println(utils.getDateTime() + "OfficeProvider:Owp: sleep "); 794 OfficeWatcherPing.sleep(1000); // 5000 795 } 796 catch (InterruptedException ex) 797 { 798 ex.printStackTrace(); 799 } 800 } 801 802 } 803 804 public void finish() 805 { 806 synchronized(this) 807 { 808 bStop = true; 809 System.out.println(utils.getDateTime() + "OfficeProvider:Owp: stop "); 810 811 notify(); 812 } 813 } 814 } 815 816 private void deleteFilesAndDirector(File file) 817 { 818 File f = file; 819 if(f.isDirectory()) 820 { 821 File files[] = f.listFiles(); 822 for(int i = 0; i < files.length; i++) 823 { 824 deleteFilesAndDirector(files[i]); 825 } 826 f.delete(); 827 } 828 else if (f.isFile()) 829 { 830 f.delete(); 831 } 832 } 833 } 834