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 */
backupUserLayer(TestParameters param, XMultiServiceFactory msf)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 */
disposeManager(lib.TestParameters param)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 */
getManager(lib.TestParameters param)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 */
connect(String connectStr)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 */
closeExistingOffice(lib.TestParameters param, boolean closeIfPossible)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
connectOffice(String cncstr)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
disposeOffice(XMultiServiceFactory msf, TestParameters param)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
closeAllWindows(XDesktop desk)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
createStringSubstitution(XMultiServiceFactory xMSF)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 */
getDirSys(String dir)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 */
makeAppExecCommand(XMultiServiceFactory msf, TestParameters param)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
dbg(String message)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
OfficeWatcherPing(OfficeWatcher ow)773 public OfficeWatcherPing(OfficeWatcher ow)
774 {
775 this.ow = ow;
776 }
777
778 @Override
run()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
finish()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
deleteFilesAndDirector(File file)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