1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
   5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   6  *
   7  * This code is free software; you can redistribute it and/or modify it
   8  * under the terms of the GNU General Public License version 2 only, as
   9  * published by the Free Software Foundation.  Oracle designates this
  10  * particular file as subject to the "Classpath" exception as provided
  11  * by Oracle in the LICENSE file that accompanied this code.
  12  *
  13  * This code is distributed in the hope that it will be useful, but WITHOUT
  14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16  * version 2 for more details (a copy is included in the LICENSE file that
  17  * accompanied this code).
  18  *
  19  * You should have received a copy of the GNU General Public License version
  20  * 2 along with this work; if not, write to the Free Software Foundation,
  21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  22  *
  23  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  24  * or visit www.oracle.com if you need additional information or have any
  25  * questions.
  26  */
  27 package com.sun.javatest.tool;
  28 
  29 import java.awt.EventQueue;
  30 import java.io.File;
  31 import java.io.IOException;
  32 import java.io.PrintWriter;
  33 import java.text.MessageFormat;
  34 import java.util.Map;
  35 import java.util.Set;
  36 
  37 import com.sun.javatest.Harness;
  38 import com.sun.javatest.InterviewParameters;
  39 import com.sun.javatest.JavaTestError;
  40 import com.sun.javatest.JavaTestSecurityManager;
  41 import com.sun.javatest.ProductInfo;
  42 import com.sun.javatest.Status;
  43 import com.sun.javatest.services.ServiceManager;
  44 import com.sun.javatest.util.Debug;
  45 import com.sun.javatest.util.ExitCount;
  46 import com.sun.javatest.util.I18NResourceBundle;
  47 
  48 /**
  49  * The main program class for JT Harness.
  50  */
  51 public class Main
  52 {
  53 
  54     /**
  55      * Thrown when a bad command line argument is encountered.
  56      */
  57     public static class Fault extends Exception
  58     {
  59         /**
  60          * Create a BadArgs exception.
  61          * @param i18n A resource bundle in which to find the detail message.
  62          * @param key The key for the detail message.
  63          */
  64         public Fault(I18NResourceBundle i18n, String key) {
  65             super(i18n.getString(key));
  66         }
  67 
  68         /**
  69          * Create a BadArgs exception.
  70          * @param i18n A resource bundle in which to find the detail message.
  71          * @param key The key for the detail message.
  72          * @param arg An argument to be formatted with the detail message by
  73          * {@link java.text.MessageFormat#format}
  74          */
  75         public Fault(I18NResourceBundle i18n, String key, Object arg) {
  76             super(i18n.getString(key, arg));
  77         }
  78 
  79         /**
  80          * Create a BadArgs exception.
  81          * @param i18n A resource bundle in which to find the detail message.
  82          * @param key The key for the detail message.
  83          * @param args An array of arguments to be formatted with the detail message by
  84          * {@link java.text.MessageFormat#format}
  85          */
  86         public Fault(I18NResourceBundle i18n, String key, Object[] args) {
  87             super(i18n.getString(key, args));
  88         }
  89     }
  90 
  91 
  92     /**
  93      * Run JT Harness with command-line args.
  94      * @param args Arguments, per the command-line spec
  95      */
  96     public static void main(String[] args) {
  97         tracing = Boolean.getBoolean("javatest.trace.startup");
  98 
  99         if (tracing)
 100             traceStartTime = System.currentTimeMillis();
 101 
 102         if (Boolean.getBoolean("javatest.trace.printargs")) {
 103             StringBuilder fullCmd = new StringBuilder();
 104             StringBuilder incrementalCmd = new StringBuilder();
 105 
 106             for (String a : args) {
 107                 fullCmd.append(a);
 108                 fullCmd.append(" ");
 109 
 110                 incrementalCmd.append("// ");
 111                 incrementalCmd.append(a);
 112                 incrementalCmd.append("\n");
 113             }   // for
 114 
 115             System.out.println(fullCmd.toString().trim());
 116             System.out.println(incrementalCmd.toString());
 117         }
 118 
 119         String javaVersion = System.getProperty("java.version");
 120         if (javaVersion != null) {
 121             String[] oldVersions = {"1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6"};
 122             for (int i = 0; i < oldVersions.length; i++) {
 123                 if (javaVersion.startsWith(oldVersions[i])) {
 124                     // I18N?
 125                     System.err.println("Please use Java(TM) Standard Edition, Version 7.0 or better to run the JT Harness(TM) harness.");
 126                     System.exit(1);
 127                 }
 128             }
 129         }
 130 
 131         main0(args);
 132     }
 133 
 134     private static void main0(String[] args) {
 135         Debug.setProperties(System.getProperties());
 136 
 137         PrintWriter out = new PrintWriter(System.err) {
 138                 public void close() {
 139                     flush();
 140                 }
 141             };
 142 
 143         if (tracing)
 144             traceOut = out;
 145 
 146         try {
 147             ExitCount.inc();
 148             Main m = new Main();
 149 
 150             CommandContext ctx = new CommandContext(out);
 151             m.run(args, ctx);
 152 
 153             if (tracing)
 154                 trace("Main.run complete");
 155 
 156             out.flush(); // flush now in case ExitCount exits
 157 
 158             int[] stats = ctx.getTestStats();
 159             int rc = (stats[Status.ERROR] > 0 ? RC_BATCH_TESTS_ERROR
 160                       : stats[Status.FAILED] > 0 ? RC_BATCH_TESTS_FAILED
 161                       : RC_OK);
 162 
 163             ExitCount.dec(true, rc);
 164 
 165             // all initialization is done; this thread has nothing left to do, so...
 166             boolean preload =
 167                 System.getProperty("javatest.preload.classes", "true").equals("true");
 168 
 169             if (preload) {
 170                 if (tracing)
 171                     trace("preloading classes...");
 172 
 173                 preloadUsefulClasses();
 174 
 175                 if (tracing)
 176                     trace("preloaded classes");
 177             }
 178 
 179             out.flush();
 180         }
 181         catch (Command.Fault e) {
 182             // occurs when executing default commands at end of command line
 183             out.println(CommandContext.TRACE_PREFIX + e.getCommand().toString());
 184             out.println(e.getMessage());
 185             out.flush();
 186             exit(RC_USER_ERROR);
 187         }
 188         catch (CommandContext.Fault e) {
 189             // occurs when executing commands on command line or in command file
 190             Throwable t = e.getCause();
 191             if (t instanceof Command.Fault) {
 192                 CommandContext ctx = e.getContext();
 193                 boolean verboseCommands =
 194                     ctx.getVerboseOptionValue(CommandContext.VERBOSE_COMMANDS);
 195                 if (!verboseCommands) {
 196                     Command.Fault ce = (Command.Fault) t;
 197                     Command c = ce.getCommand();
 198                     out.println(CommandContext.TRACE_PREFIX + c.toString());
 199                 }
 200             }
 201             out.println(e.getMessage());
 202             out.flush();
 203             exit(RC_USER_ERROR);
 204         }
 205         catch (CommandParser.Fault e) {
 206             // occurs when parsing commands on command line or in command file
 207             Throwable t = e.getCause();
 208             if (t instanceof Command.Fault) {
 209                 Command.Fault ce = (Command.Fault) t;
 210                 Command c = ce.getCommand();
 211                 out.println(CommandParser.TRACE_PREFIX + c.toString());
 212             }
 213             out.println(e.getMessage());
 214             out.flush();
 215             exit(RC_USER_ERROR);
 216         }
 217         catch (Fault e) {
 218             out.println(e.getMessage());
 219             out.flush();
 220             exit(RC_INTERNAL_ERROR);
 221         }
 222         catch (Error e) {
 223             e.printStackTrace(out);
 224             out.flush();
 225             exit(RC_INTERNAL_ERROR);
 226         }
 227         catch (RuntimeException e) {
 228             e.printStackTrace(out);
 229             out.flush();
 230             exit(RC_INTERNAL_ERROR);
 231         }
 232     }
 233 
 234     /**
 235      * The main routine to run JT Harness.
 236      * @param args Arguments for JT Harness, per the command-line spec.
 237      * @param out  A stream to which to write standard messages, such as
 238      *   command-line help, version info etc. Some error messages will
 239      *   still be sent to System.err.
 240      * @throws Main.Fault if there is a problem initializing the harness
 241      * @throws Command.Fault if there is a problem with a command's arguments
 242      * @throws CommandContext.Fault if there is a problem executing a command
 243      * @throws CommandParser.Fault if there is a problem parsing the args
 244      */
 245     public final void run(String[] args, PrintWriter out)
 246         throws Fault, Command.Fault, CommandContext.Fault, CommandParser.Fault
 247     {
 248         run(args, new CommandContext(out));
 249     }
 250 
 251     /**
 252      * A routine to run JT Harness.
 253      * @param args Arguments for JT Harness, per the command-line spec.
 254      * @param ctx A context to use to execute the commands in the args
 255      * @throws Main.Fault if there is a problem initializing the harness
 256      * @throws Command.Fault if there is a problem with a command's arguments
 257      * @throws CommandContext.Fault if there is a problem executing a command
 258      * @throws CommandParser.Fault if there is a problem parsing the args
 259      */
 260     public final void run(String[] args, final CommandContext ctx)
 261         throws Fault, Command.Fault, CommandContext.Fault, CommandParser.Fault
 262     {
 263         if (commandManagers == null) {
 264             desktopManager = new DesktopManager();
 265             helpManager = new HelpManager();
 266             serviceManager = new ServiceManager.ServiceCommandManager();
 267             try {
 268                 ManagerLoader ml = new ManagerLoader(CommandManager.class, System.err);
 269                 Set<Object> mgrs = ml.loadManagers(CMDMGRLIST);
 270                 mgrs.add(desktopManager);
 271                 mgrs.add(helpManager);
 272                 mgrs.add(serviceManager);
 273                 commandManagers = mgrs.toArray(new CommandManager[mgrs.size()]);
 274                 helpManager.setCommandManagers(commandManagers);
 275             }
 276             catch (IOException e) {
 277                 throw new Fault(i18n, "main.cantAccessResource", new Object[] { CMDMGRLIST, e });
 278             }
 279         }
 280 
 281         CommandParser p = new CommandParser(commandManagers);
 282         boolean urlEncoded = Boolean.getBoolean("javatest.command.urlEncoded");
 283         p.parse(args, urlEncoded, ctx);
 284 
 285         if (!initialized) {
 286             File classDir = ProductInfo.getJavaTestClassDir();
 287             Harness.setClassDir(classDir);
 288 
 289             // Install our own security manager. This is primarily for self-defense
 290             // against sameJVM tests, and not to prevent access outside the sandbox.
 291             // Moan to stderr if it can't be installed.
 292             JavaTestSecurityManager.install();
 293 
 294             // mark initialization done
 295             initialized = true;
 296         }
 297 
 298         final Command[] cmds = ctx.getCommands();
 299 
 300         // special case, when user requested info
 301         boolean helpInfoRequired = helpManager.isInfoRequired();
 302         if (helpInfoRequired) {
 303             helpManager.showRequiredInfo(ctx.getLogWriter(), ctx);
 304             if (cmds.length == 0)
 305                 return;
 306         }
 307 
 308         final Desktop desktop;
 309         boolean needDesktop = ctx.isDesktopRequired();
 310 
 311         if (needDesktop) {
 312             if (tracing)
 313                 trace("creating desktop...");
 314 
 315             // should really be on event thread
 316             desktop = desktopManager.createDesktop(ctx);
 317 
 318             /*
 319             // show splash screen, using event thread
 320             Runnable task = new Runnable() {
 321                 public void run() {
 322                     if (tracing)
 323                         trace("creating splash screen...");
 324                     Startup su = null;
 325                     // lots of GUI construction caused by the few lines below
 326                     for (int i = 0; i < cmds.length; i++) {
 327                         URL ss = cmds[i].getCustomSplash();
 328                         if (ss != null) {
 329                             su = new Startup(ss);
 330                             break;
 331                         }
 332                     }   // for
 333 
 334                     if (su == null)
 335                         su = new Startup();
 336 
 337                     final Startup splashScreen = su;
 338 
 339                     Thread splashTimer = new Thread() {
 340                             public void run() {
 341                                 // pause for 15 seconds before displaying desktop and
 342                                 // hiding splash screen; if the desktop is made ready sooner,
 343                                 // it will be displayed sooner
 344                                 int splashSecs =
 345                                     Integer.getInteger("javatest.splashScreen.time", 15).intValue();
 346                                 try {
 347                                     Thread.currentThread().sleep(splashSecs*1000);
 348                                 }
 349                                 catch (InterruptedException e) {
 350                                 }
 351                                 if (tracing)
 352                                     trace("splash timer done: showing desktop...");
 353 
 354                                 Runnable task2 = new Runnable() {
 355                                     public void run() {
 356                                         desktop.setVisible(true);
 357                                         splashScreen.disposeLater();
 358                                     }
 359                                 };
 360 
 361                                 try {
 362                                     EventQueue.invokeAndWait(task2);
 363                                 } catch (InterruptedException e) {
 364                                     if (tracing)
 365                                         e.printStackTrace();
 366                                 } catch (java.lang.reflect.InvocationTargetException e) {
 367                                     if (tracing)
 368                                         e.printStackTrace();
 369                                 }
 370 
 371                             }
 372                         };
 373                     splashTimer.setDaemon(true);
 374                     splashTimer.start();
 375                 }   // run()
 376             };  // runnable
 377 
 378             try {
 379                 EventQueue.invokeAndWait(task);
 380             } catch (InterruptedException e) {
 381                 if (tracing)
 382                     e.printStackTrace();
 383             } catch (java.lang.reflect.InvocationTargetException e) {
 384                 if (tracing)
 385                     e.printStackTrace();
 386             }
 387             */
 388 
 389             desktop.setVisible(true);
 390             ctx.setDesktop(desktop);
 391             // set context log to display in a log tool
 392         }
 393         else
 394             desktop = null;
 395 
 396         // execute the commands on the command line
 397         if (tracing)
 398             trace("executing command line...");
 399 
 400         if (desktop != null) {
 401             desktop.setVisible(true);
 402         }
 403 
 404         ctx.runCommands();
 405 
 406         if (tracing)
 407             trace("command line done");
 408 
 409         if (desktop != null) {
 410             if (ctx.isCloseDesktopWhenDoneEnabled()
 411                 && desktop.isOKToAutoExit()) {
 412                 Runnable task = new Runnable() {
 413                     public void run() {
 414                         desktop.setVisible(false);
 415                         desktop.dispose();
 416                     }
 417                 };
 418                 try {
 419                     EventQueue.invokeAndWait(task);
 420                 } catch (InterruptedException e) {
 421                     if (tracing)
 422                         e.printStackTrace();
 423                 } catch (java.lang.reflect.InvocationTargetException e) {
 424                     if (tracing)
 425                         e.printStackTrace();
 426                 }
 427             }
 428             else {
 429                 InterviewParameters ip_tmp = null;
 430                 if (desktop.isEmpty() && ctx.hasConfig()) {
 431                     try {
 432                         ip_tmp = ctx.getConfig();
 433                     } catch (CommandContext.Fault e) {
 434                         System.err.println(i18n.getString("main.cantGetConfig", e.getMessage()));
 435                     }
 436                 }
 437                 final InterviewParameters ip = ip_tmp;
 438 
 439                 Runnable task = new Runnable() {
 440                     public void run() {
 441                         // if a desktop has been started, make sure it is not empty
 442                         if (desktop.isEmpty()) {
 443                             if (ctx.hasConfig() && ip != null) {
 444                                 if (tracing) {
 445                                     trace("show specified test suite");
 446                                 }
 447                                 desktop.restoreHistory();
 448                                 Tool tool = desktop.addDefaultTool(ip);
 449                                 Map<String, String> data = ctx.getDesktopData();
 450                                 if (data != null) {
 451                                     tool.restore(data);
 452                                 }
 453                             }
 454                             else if (desktop.isFirstTime()) {
 455                                 if (tracing)
 456                                     trace("show default");
 457                                 desktop.addDefaultTool();
 458                             }
 459                             else {
 460                                 if (tracing)
 461                                     trace("restore desktop");
 462                                 desktop.restore();
 463                             }
 464                         }
 465                         if (tracing)
 466                             trace("set desktop visible");
 467                         desktop.setVisible(true);
 468                     }   // run()
 469                 };   // Runnable
 470 
 471                 try {
 472                     EventQueue.invokeAndWait(task);
 473                 } catch (InterruptedException e) {
 474                     if (tracing)
 475                         e.printStackTrace();
 476                 } catch (java.lang.reflect.InvocationTargetException e) {
 477                     if (tracing)
 478                         e.printStackTrace();
 479                 }
 480 
 481             }
 482         }
 483         ctx.dispose();
 484     }
 485 
 486     private CommandManager[] commandManagers;
 487     private HelpManager helpManager;
 488     private DesktopManager desktopManager;
 489     private ServiceManager.ServiceCommandManager serviceManager;
 490 
 491     private static void preloadUsefulClasses() {
 492         //System.err.println("\n\n\n>>>>> preloading classes\n\n\n");
 493         new javax.swing.text.html.HTMLEditorKit().createDefaultDocument();
 494         com.sun.interview.Interview i = new com.sun.interview.Interview("dummy") {
 495                 com.sun.interview.Question qEnd = new com.sun.interview.FinalQuestion(this);
 496                 { setFirstQuestion(qEnd); }
 497             };
 498         new com.sun.interview.wizard.WizPane(i);
 499         //System.err.println("\n\n\n>>>>> preloading classes done\n\n\n");
 500     }
 501 
 502     private static void trace(String msg) {
 503         long now = System.currentTimeMillis();
 504         traceOut.println(MessageFormat.format("{0,number,[##0.0]} {1}",
 505                 new Float((now - traceStartTime)/1000f), msg));
 506         traceOut.flush();
 507     }
 508 
 509 
 510     /**
 511      * Call System.exit, taking care to get permission from the
 512      * JavaTestSecurityManager, if it is installed.
 513      * @param exitCode an exit code to be passed to System.exit
 514      */
 515     private static final void exit(int exitCode) {
 516         // If our security manager is installed, it won't allow a call of
 517         // System.exit unless we ask it nicely, pretty please, thank you.
 518         SecurityManager sc = System.getSecurityManager();
 519         if (sc instanceof JavaTestSecurityManager)
 520             ((JavaTestSecurityManager) sc).setAllowExit(true);
 521         System.exit(exitCode);
 522         throw new JavaTestError(i18n, "main.cannotExit.err");
 523     }
 524 
 525     private static boolean tracing;
 526     private static long traceStartTime;
 527     private static PrintWriter traceOut;
 528     private static boolean initialized = false;
 529     private static final String CMDMGRLIST = "META-INF/services/com.sun.javatest.tool.CommandManager.lst";
 530 
 531     private static I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(Main.class);
 532 
 533     private static final int RC_GUI_ACTIVE = -1;
 534     private static final int RC_OK = 0;
 535     private static final int RC_BATCH_TESTS_FAILED = 1;
 536     private static final int RC_BATCH_TESTS_ERROR = 2;
 537     private static final int RC_USER_ERROR = 3;
 538     private static final int RC_INTERNAL_ERROR = 4;
 539 }