| /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under |
| * the terms of the Common Public License v1.0 which accompanies this distribution, |
| * and is available at http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * $Id: runCommand.java,v 1.1.1.1.2.1 2004/07/16 23:32:03 vlad_r Exp $ |
| */ |
| package com.vladium.emma; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.jar.Attributes; |
| import java.util.jar.JarFile; |
| import java.util.jar.Manifest; |
| |
| import com.vladium.util.ClassLoaderResolver; |
| import com.vladium.util.Strings; |
| import com.vladium.util.args.IOptsParser; |
| import com.vladium.util.asserts.$assert; |
| import com.vladium.emma.rt.AppRunner; |
| |
| // ---------------------------------------------------------------------------- |
| /** |
| * @author Vlad Roubtsov, (C) 2003 |
| */ |
| public |
| final class runCommand extends Command |
| { |
| // public: ................................................................ |
| |
| |
| public synchronized void run () |
| { |
| ClassLoader loader; |
| try |
| { |
| loader = ClassLoaderResolver.getClassLoader (); |
| } |
| catch (Throwable t) |
| { |
| loader = getClass ().getClassLoader (); |
| } |
| |
| try |
| { |
| // process 'args': |
| { |
| final IOptsParser parser = getOptParser (loader); |
| final IOptsParser.IOpts parsedopts = parser.parse (m_args); |
| |
| // check if usage is requested before checking args parse errors etc: |
| { |
| final int usageRequestLevel = parsedopts.usageRequestLevel (); |
| |
| if (usageRequestLevel > 0) |
| { |
| usageexit (parser, usageRequestLevel, null); |
| return; |
| } |
| } |
| |
| final IOptsParser.IOpt [] opts = parsedopts.getOpts (); |
| |
| if (opts == null) // this means there were args parsing errors |
| { |
| parsedopts.error (m_out, STDOUT_WIDTH); |
| usageexit (parser, IOptsParser.SHORT_USAGE, null); |
| return; |
| } |
| |
| // process parsed args: |
| try |
| { |
| for (int o = 0; o < opts.length; ++ o) |
| { |
| final IOptsParser.IOpt opt = opts [o]; |
| final String on = opt.getCanonicalName (); |
| |
| if (! processOpt (opt)) |
| { |
| if ("cp".equals (on)) |
| { |
| m_classpath = getListOptValue (opt, PATH_DELIMITERS, true); |
| } |
| else if ("jar".equals (on)) |
| { |
| m_jarMode = true; |
| } |
| else if ("f".equals (on)) |
| { |
| m_scanCoveragePath = getOptionalBooleanOptValue (opt); |
| } |
| else if ("sp".equals (on)) |
| { |
| m_srcpath = getListOptValue (opt, PATH_DELIMITERS, true); |
| } |
| else if ("raw".equals (on)) |
| { |
| m_dumpRawData = getOptionalBooleanOptValue (opt); |
| } |
| else if ("out".equals (on)) |
| { |
| m_outFileName = opt.getFirstValue (); |
| } |
| else if ("merge".equals (on)) |
| { |
| m_outDataMerge = getOptionalBooleanOptValue (opt) ? Boolean.TRUE : Boolean.FALSE; |
| } |
| else if ("r".equals (on)) |
| { |
| m_reportTypes = Strings.merge (opt.getValues (), COMMA_DELIMITERS, true); |
| } |
| else if ("ix".equals (on)) |
| { |
| m_ixpath = getListOptValue (opt, COMMA_DELIMITERS, true); |
| } |
| } |
| } |
| |
| // user '-props' file property overrides: |
| |
| if (! processFilePropertyOverrides ()) return; |
| |
| // process prefixed opts: |
| |
| processCmdPropertyOverrides (parsedopts); |
| } |
| catch (IOException ioe) |
| { |
| throw new EMMARuntimeException (IAppErrorCodes.ARGS_IO_FAILURE, ioe); |
| } |
| |
| |
| // process free args: |
| { |
| final String [] freeArgs = parsedopts.getFreeArgs (); |
| |
| if (m_jarMode) |
| { |
| if ((freeArgs == null) || (freeArgs.length == 0)) |
| { |
| usageexit (parser, IOptsParser.SHORT_USAGE, "missing jar file name"); |
| return; |
| } |
| |
| if ($assert.ENABLED) $assert.ASSERT (freeArgs != null && freeArgs.length > 0, "invalid freeArgs"); |
| |
| final File jarfile = new File (freeArgs [0]); |
| final String jarMainClass; |
| try |
| { |
| jarMainClass = openJarFile (jarfile); // the rest of free args are *not* ignored |
| } |
| catch (IOException ioe) |
| { |
| // TODO: is the right error code? |
| throw new EMMARuntimeException (IAppErrorCodes.ARGS_IO_FAILURE, ioe); |
| } |
| |
| if (jarMainClass == null) |
| { |
| exit (true, "failed to load Main-Class manifest attribute from [" + jarfile.getAbsolutePath () + "]", null, RC_UNEXPECTED); |
| return; |
| } |
| |
| if ($assert.ENABLED) $assert.ASSERT (jarMainClass != null, "invalid jarMainClass"); |
| |
| m_appArgs = new String [freeArgs.length]; |
| System.arraycopy (freeArgs, 1, m_appArgs, 1, freeArgs.length - 1); |
| m_appArgs [0] = jarMainClass; |
| |
| m_classpath = new String [] { jarfile.getPath () }; |
| } |
| else |
| { |
| if ((freeArgs == null) || (freeArgs.length == 0)) |
| { |
| usageexit (parser, IOptsParser.SHORT_USAGE, "missing application class name"); |
| return; |
| } |
| |
| m_appArgs = freeArgs; |
| } |
| } |
| // [m_appArgs: { mainclass, arg1, arg2, ... }] |
| |
| |
| // handle cmd line-level defaults and option interaction |
| { |
| if (DEFAULT_TO_SYSTEM_CLASSPATH) |
| { |
| if (m_classpath == null) |
| { |
| // TODO" this is not guaranteed to work (in WebStart etc), so double check if I should remove this |
| |
| final String systemClasspath = System.getProperty ("java.class.path", ""); |
| if (systemClasspath.length () == 0) |
| { |
| // TODO: assume "." just like Sun JVMs in this case? |
| usageexit (parser, IOptsParser.SHORT_USAGE, "could not infer coverage classpath from 'java.class.path'; use an explicit -cp option"); |
| return; |
| } |
| |
| m_classpath = new String [] {systemClasspath}; |
| } |
| } |
| else |
| { |
| if (m_classpath == null) |
| { |
| usageexit (parser, IOptsParser.SHORT_USAGE, "either '-cp' or '-jar' option is required"); |
| return; |
| } |
| } |
| |
| // TODO: who owns setting this 'txt' default? most likely AppRunner |
| if (m_reportTypes == null) |
| { |
| m_reportTypes = new String [] {"txt"}; |
| } |
| } |
| } |
| |
| // run the app: |
| { |
| if ($assert.ENABLED) $assert.ASSERT (m_appArgs != null && m_appArgs.length > 0, "invalid m_appArgs"); |
| |
| final String [] appargs = new String [m_appArgs.length - 1]; |
| System.arraycopy (m_appArgs, 1, appargs, 0, appargs.length); |
| |
| final AppRunner processor = AppRunner.create (loader); |
| processor.setAppName (IAppConstants.APP_NAME); // for log prefixing |
| |
| processor.setAppClass (m_appArgs [0], appargs); |
| processor.setCoveragePath (m_classpath, true); // TODO: an option to set 'canonical'? |
| processor.setScanCoveragePath (m_scanCoveragePath); |
| processor.setSourcePath (m_srcpath); |
| processor.setInclExclFilter (m_ixpath); |
| processor.setDumpSessionData (m_dumpRawData); |
| processor.setSessionOutFile (m_outFileName); |
| processor.setSessionOutMerge (m_outDataMerge); |
| if ($assert.ENABLED) $assert.ASSERT (m_reportTypes != null, "m_reportTypes no set"); |
| processor.setReportTypes (m_reportTypes); |
| processor.setPropertyOverrides (m_propertyOverrides); |
| |
| processor.run (); |
| } |
| } |
| catch (EMMARuntimeException yre) |
| { |
| // TODO: see below |
| |
| exit (true, yre.getMessage (), yre, RC_UNEXPECTED); // does not return |
| return; |
| } |
| catch (Throwable t) |
| { |
| // TODO: embed: OS/JVM fingerprint, build #, etc |
| // TODO: save stack trace in a file and prompt user to send it to ... |
| |
| exit (true, "unexpected failure: ", t, RC_UNEXPECTED); // does not return |
| return; |
| } |
| |
| exit (false, null, null, RC_OK); |
| } |
| |
| // protected: ............................................................. |
| |
| |
| protected runCommand (final String usageToolName, final String [] args) |
| { |
| super (usageToolName, args); |
| } |
| |
| protected void initialize () |
| { |
| // TODO: clean up instance state |
| |
| super.initialize (); |
| } |
| |
| protected String usageArgsMsg () |
| { |
| return "[options] class [args...] | -jar [options] jarfile [args...]"; |
| } |
| |
| // package: ............................................................... |
| |
| // private: ............................................................... |
| |
| |
| private static String openJarFile (final File file) |
| throws IOException |
| { |
| JarFile jarfile = null; |
| try |
| { |
| jarfile = new JarFile (file, false); |
| |
| final Manifest manifest = jarfile.getManifest (); |
| if (manifest == null) return null; |
| |
| final Attributes attributes = manifest.getMainAttributes (); |
| if (attributes == null) return null; |
| |
| final String jarMainClass = attributes.getValue (Attributes.Name.MAIN_CLASS); |
| |
| return jarMainClass; |
| } |
| finally |
| { |
| if (jarfile != null) try { jarfile.close (); } catch (IOException ignore) {} |
| } |
| } |
| |
| |
| private String [] m_classpath, m_srcpath; |
| private boolean m_jarMode; |
| private boolean m_scanCoveragePath; // defaults to false |
| private String [] m_ixpath; |
| private String [] m_appArgs; |
| private boolean m_dumpRawData; // defaults to false |
| private String m_outFileName; |
| private Boolean m_outDataMerge; |
| private String [] m_reportTypes; |
| |
| private static final boolean DEFAULT_TO_SYSTEM_CLASSPATH = false; |
| |
| } // end of class |
| // ---------------------------------------------------------------------------- |