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