Home | History | Annotate | Download | only in emma
      1 /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
      2  *
      3  * This program and the accompanying materials are made available under
      4  * the terms of the Common Public License v1.0 which accompanies this distribution,
      5  * and is available at http://www.eclipse.org/legal/cpl-v10.html
      6  *
      7  * $Id: runCommand.java,v 1.1.1.1.2.1 2004/07/16 23:32:03 vlad_r Exp $
      8  */
      9 package com.vladium.emma;
     10 
     11 import java.io.File;
     12 import java.io.IOException;
     13 import java.util.jar.Attributes;
     14 import java.util.jar.JarFile;
     15 import java.util.jar.Manifest;
     16 
     17 import com.vladium.util.ClassLoaderResolver;
     18 import com.vladium.util.Strings;
     19 import com.vladium.util.args.IOptsParser;
     20 import com.vladium.util.asserts.$assert;
     21 import com.vladium.emma.rt.AppRunner;
     22 
     23 // ----------------------------------------------------------------------------
     24 /**
     25  * @author Vlad Roubtsov, (C) 2003
     26  */
     27 public
     28 final class runCommand extends Command
     29 {
     30     // public: ................................................................
     31 
     32 
     33     public synchronized void run ()
     34     {
     35         ClassLoader loader;
     36         try
     37         {
     38             loader = ClassLoaderResolver.getClassLoader ();
     39         }
     40         catch (Throwable t)
     41         {
     42             loader = getClass ().getClassLoader ();
     43         }
     44 
     45         try
     46         {
     47             // process 'args':
     48             {
     49                 final IOptsParser parser = getOptParser (loader);
     50                 final IOptsParser.IOpts parsedopts = parser.parse (m_args);
     51 
     52                 // check if usage is requested before checking args parse errors etc:
     53                 {
     54                     final int usageRequestLevel = parsedopts.usageRequestLevel ();
     55 
     56                     if (usageRequestLevel > 0)
     57                     {
     58                         usageexit (parser, usageRequestLevel, null);
     59                         return;
     60                     }
     61                 }
     62 
     63                 final IOptsParser.IOpt [] opts = parsedopts.getOpts ();
     64 
     65                 if (opts == null) // this means there were args parsing errors
     66                 {
     67                     parsedopts.error (m_out, STDOUT_WIDTH);
     68                     usageexit (parser, IOptsParser.SHORT_USAGE, null);
     69                     return;
     70                 }
     71 
     72                 // process parsed args:
     73                 try
     74                 {
     75                     for (int o = 0; o < opts.length; ++ o)
     76                     {
     77                         final IOptsParser.IOpt opt = opts [o];
     78                         final String on = opt.getCanonicalName ();
     79 
     80                         if (! processOpt (opt))
     81                         {
     82                             if ("cp".equals (on))
     83                             {
     84                                 m_classpath = getListOptValue (opt, PATH_DELIMITERS, true);
     85                             }
     86                             else if ("jar".equals (on))
     87                             {
     88                                 m_jarMode = true;
     89                             }
     90                             else if ("f".equals (on))
     91                             {
     92                                 m_scanCoveragePath = getOptionalBooleanOptValue (opt);
     93                             }
     94                             else if ("sp".equals (on))
     95                             {
     96                                 m_srcpath = getListOptValue (opt, PATH_DELIMITERS, true);
     97                             }
     98                             else if ("raw".equals (on))
     99                             {
    100                                 m_dumpRawData = getOptionalBooleanOptValue (opt);
    101                             }
    102                             else if ("out".equals (on))
    103                             {
    104                                 m_outFileName = opt.getFirstValue ();
    105                             }
    106                             else if ("merge".equals (on))
    107                             {
    108                                 m_outDataMerge = getOptionalBooleanOptValue (opt) ? Boolean.TRUE : Boolean.FALSE;
    109                             }
    110                             else if ("r".equals (on))
    111                             {
    112                                 m_reportTypes = Strings.merge (opt.getValues (), COMMA_DELIMITERS, true);
    113                             }
    114                             else if ("ix".equals (on))
    115                             {
    116                                 m_ixpath = getListOptValue (opt, COMMA_DELIMITERS, true);
    117                             }
    118                         }
    119                     }
    120 
    121                     // user '-props' file property overrides:
    122 
    123                     if (! processFilePropertyOverrides ()) return;
    124 
    125                     // process prefixed opts:
    126 
    127                     processCmdPropertyOverrides (parsedopts);
    128                 }
    129                 catch (IOException ioe)
    130                 {
    131                     throw new EMMARuntimeException (IAppErrorCodes.ARGS_IO_FAILURE, ioe);
    132                 }
    133 
    134 
    135                 // process free args:
    136                 {
    137                     final String [] freeArgs = parsedopts.getFreeArgs ();
    138 
    139                     if (m_jarMode)
    140                     {
    141                         if ((freeArgs == null) || (freeArgs.length == 0))
    142                         {
    143                             usageexit (parser, IOptsParser.SHORT_USAGE, "missing jar file name");
    144                             return;
    145                         }
    146 
    147                         if ($assert.ENABLED) $assert.ASSERT (freeArgs != null && freeArgs.length > 0, "invalid freeArgs");
    148 
    149                         final File jarfile = new File (freeArgs [0]);
    150                         final String jarMainClass;
    151                         try
    152                         {
    153                             jarMainClass = openJarFile (jarfile); // the rest of free args are *not* ignored
    154                         }
    155                         catch (IOException ioe)
    156                         {
    157                             // TODO: is the right error code?
    158                             throw new EMMARuntimeException (IAppErrorCodes.ARGS_IO_FAILURE, ioe);
    159                         }
    160 
    161                         if (jarMainClass == null)
    162                         {
    163                             exit (true, "failed to load Main-Class manifest attribute from [" + jarfile.getAbsolutePath () + "]", null, RC_UNEXPECTED);
    164                             return;
    165                         }
    166 
    167                         if ($assert.ENABLED) $assert.ASSERT (jarMainClass != null, "invalid jarMainClass");
    168 
    169                         m_appArgs = new String [freeArgs.length];
    170                         System.arraycopy (freeArgs, 1, m_appArgs, 1, freeArgs.length - 1);
    171                         m_appArgs [0] = jarMainClass;
    172 
    173                         m_classpath = new String [] { jarfile.getPath () };
    174                     }
    175                     else
    176                     {
    177                         if ((freeArgs == null) || (freeArgs.length == 0))
    178                         {
    179                             usageexit (parser, IOptsParser.SHORT_USAGE, "missing application class name");
    180                             return;
    181                         }
    182 
    183                         m_appArgs = freeArgs;
    184                     }
    185                 }
    186                 // [m_appArgs: { mainclass, arg1, arg2, ... }]
    187 
    188 
    189                 // handle cmd line-level defaults and option interaction
    190                 {
    191                     if (DEFAULT_TO_SYSTEM_CLASSPATH)
    192                     {
    193                         if (m_classpath == null)
    194                         {
    195                             // TODO" this is not guaranteed to work (in WebStart etc), so double check if I should remove this
    196 
    197                             final String systemClasspath = System.getProperty ("java.class.path", "");
    198                             if (systemClasspath.length () == 0)
    199                             {
    200                                 // TODO: assume "." just like Sun JVMs in this case?
    201                                 usageexit (parser, IOptsParser.SHORT_USAGE, "could not infer coverage classpath from 'java.class.path'; use an explicit -cp option");
    202                                 return;
    203                             }
    204 
    205                             m_classpath = new String [] {systemClasspath};
    206                         }
    207                     }
    208                     else
    209                     {
    210                         if (m_classpath == null)
    211                         {
    212                             usageexit (parser, IOptsParser.SHORT_USAGE, "either '-cp' or '-jar' option is required");
    213                             return;
    214                         }
    215                     }
    216 
    217                     // TODO: who owns setting this 'txt' default? most likely AppRunner
    218                     if (m_reportTypes == null)
    219                     {
    220                         m_reportTypes = new String [] {"txt"};
    221                     }
    222                 }
    223             }
    224 
    225             // run the app:
    226             {
    227                 if ($assert.ENABLED) $assert.ASSERT (m_appArgs != null && m_appArgs.length > 0, "invalid m_appArgs");
    228 
    229                 final String [] appargs = new String [m_appArgs.length - 1];
    230                 System.arraycopy (m_appArgs, 1, appargs, 0, appargs.length);
    231 
    232                 final AppRunner processor = AppRunner.create (loader);
    233                 processor.setAppName (IAppConstants.APP_NAME); // for log prefixing
    234 
    235                 processor.setAppClass (m_appArgs [0], appargs);
    236                 processor.setCoveragePath (m_classpath, true); // TODO: an option to set 'canonical'?
    237                 processor.setScanCoveragePath (m_scanCoveragePath);
    238                 processor.setSourcePath (m_srcpath);
    239                 processor.setInclExclFilter (m_ixpath);
    240                 processor.setDumpSessionData (m_dumpRawData);
    241                 processor.setSessionOutFile (m_outFileName);
    242                 processor.setSessionOutMerge (m_outDataMerge);
    243                 if ($assert.ENABLED) $assert.ASSERT (m_reportTypes != null, "m_reportTypes no set");
    244                 processor.setReportTypes (m_reportTypes);
    245                 processor.setPropertyOverrides (m_propertyOverrides);
    246 
    247                 processor.run ();
    248             }
    249         }
    250         catch (EMMARuntimeException yre)
    251         {
    252             // TODO: see below
    253 
    254             exit (true, yre.getMessage (), yre, RC_UNEXPECTED); // does not return
    255             return;
    256         }
    257         catch (Throwable t)
    258         {
    259             // TODO: embed: OS/JVM fingerprint, build #, etc
    260             // TODO: save stack trace in a file and prompt user to send it to ...
    261 
    262             exit (true, "unexpected failure: ", t, RC_UNEXPECTED); // does not return
    263             return;
    264         }
    265 
    266         exit (false, null, null, RC_OK);
    267     }
    268 
    269     // protected: .............................................................
    270 
    271 
    272     protected runCommand (final String usageToolName, final String [] args)
    273     {
    274         super (usageToolName, args);
    275     }
    276 
    277     protected void initialize ()
    278     {
    279         // TODO: clean up instance state
    280 
    281         super.initialize ();
    282     }
    283 
    284     protected String usageArgsMsg ()
    285     {
    286         return "[options] class [args...] | -jar [options] jarfile [args...]";
    287     }
    288 
    289     // package: ...............................................................
    290 
    291     // private: ...............................................................
    292 
    293 
    294     private static String openJarFile (final File file)
    295         throws IOException
    296     {
    297         JarFile jarfile = null;
    298         try
    299         {
    300             jarfile = new JarFile (file, false);
    301 
    302             final Manifest manifest = jarfile.getManifest ();
    303             if (manifest == null) return null;
    304 
    305             final Attributes attributes = manifest.getMainAttributes ();
    306             if (attributes == null) return null;
    307 
    308             final String jarMainClass = attributes.getValue (Attributes.Name.MAIN_CLASS);
    309 
    310             return jarMainClass;
    311         }
    312         finally
    313         {
    314             if (jarfile != null) try { jarfile.close (); } catch (IOException ignore) {}
    315         }
    316     }
    317 
    318 
    319     private String [] m_classpath, m_srcpath;
    320     private boolean m_jarMode;
    321     private boolean m_scanCoveragePath; // defaults to false
    322     private String [] m_ixpath;
    323     private String [] m_appArgs;
    324     private boolean m_dumpRawData; // defaults to false
    325     private String m_outFileName;
    326     private Boolean m_outDataMerge;
    327     private String [] m_reportTypes;
    328 
    329     private static final boolean DEFAULT_TO_SYSTEM_CLASSPATH = false;
    330 
    331 } // end of class
    332 // ----------------------------------------------------------------------------