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 */ 9package com.vladium.emma; 10 11import java.io.File; 12import java.io.IOException; 13import java.util.jar.Attributes; 14import java.util.jar.JarFile; 15import java.util.jar.Manifest; 16 17import com.vladium.util.ClassLoaderResolver; 18import com.vladium.util.Strings; 19import com.vladium.util.args.IOptsParser; 20import com.vladium.util.asserts.$assert; 21import com.vladium.emma.rt.AppRunner; 22 23// ---------------------------------------------------------------------------- 24/** 25 * @author Vlad Roubtsov, (C) 2003 26 */ 27public 28final 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// ----------------------------------------------------------------------------