Source code: edu/ucsb/ccs/jcontractor/jContractor.java
1 package edu.ucsb.ccs.jcontractor;
2
3 import java.util.*;
4 import java.io.*;
5 import java.lang.reflect.Method;
6 import java.lang.reflect.InvocationTargetException;
7 import com.werken.opt.*;
8
9 /**
10 * Main entry point into the jContractor system. This class can be
11 * run stand alone to run another program with contract checking
12 * enabled. jContractor will substitute its own class loader for the
13 * default class loader, and modify the bytecode of loaded classes to
14 * enforce contracts at runtime. The <code>runInstrumented(String,
15 * String []) method may be used to programatically run a class with
16 * contracts enabled.
17 *
18 * If specified, the instrumentation file controls the instrumentation
19 * level of each class in the system. The available instrumentation
20 * levels are:
21 *
22 * <pre>
23 * None - No contracts checked.
24 * Pre - Preconditions checked.
25 * Post - Preconditions and postconditions checked.
26 * All - Preconditions, postconditions, and invariants checked.
27 * </pre>
28 *
29 * The instrumentation file contains class names followed by
30 * instrumentation levels, and separated by whitespace. For example:
31 *
32 * <pre>
33 * * none
34 * edu.* pre
35 * edu.ucsb.ccs.jcontractor.jInstrument all
36 * </pre>
37 *
38 * This file says the the jInstrument class should be instrumented
39 * completetly and the other classes in edu and all subpackages should
40 * have precondition checks. Wildcard matching works as in Java
41 * import statements, except that the wildcard matches any class in
42 * the package or any subpackage. More specific filters override
43 * general filters (edu.ccs.jcontractor.jInstrument overrides edu.*).
44 *
45 * If you would prefer to instrument a class and save the instrumented
46 * bytecode so that it may be run without jContractor, take a look at
47 * the jInstrument class.
48 *
49 * <pre>
50 * Usage: jContractor [-options] class [args]
51 *
52 * Options:
53 * -f <file> Specify the instrumentation file.
54 * -v, --verbose Enable verbose logging. Prints name of each
55 * class as it is instrumented.
56 * -e, --version Print version number and exit.
57 * </pre>
58 *
59 * @see jInstrument
60 * @see jContractorClassLoader
61 *
62 * @author Parker Abercrombie
63 * @author Murat Karaorman
64 */
65
66 public class jContractor {
67
68 /**
69 * Usage message to be printed if the command line arguments do not
70 * match the specification.
71 */
72 public static final String USAGE_MESSAGE =
73 "Usage: jContractor [-options] class [args]\n\n"
74 + "Options:\n"
75 + " -f <file> \t\t\t Specify the instrumentation file.\n"
76 + " -n, --none <class or package> Suppresses instrumentation of the\n"
77 + " specified class(es).\n"
78 + " -p, --pre <class or package> Instruments the specified\n"
79 + " class(es) for preconditions\n"
80 + " checks.\n"
81 + " -o, --post <class or package> Instruments the specified class(es) for\n"
82 + " precondition and postcondition checks.\n"
83 + " -a, --all <class or package> Instruments the specified class(es) for\n"
84 + " all contract checks.\n"
85 + " -v, --verbose \t\t Enable verbose logging. Prints name of each \n"
86 + " \t\t class as it is instrumented.\n"
87 + " -e, --version \t\t Print version number and exit.\n";
88
89 /**
90 * Constant for the version number that is printed when the
91 * --version command line argument is given.
92 */
93 public static final String VERSION = "jContractor version 0.1";
94
95 /**
96 * Run a class with contracts enabled. This method will replace the
97 * default class loader with a jContractorClassLoader, and run the
98 * code. This method uses a default instance of jInstrument, and
99 * checks all contracts on all classes.
100 *
101 * @param className the name of the class to run. The class must
102 * exist on the class path, and define a
103 * <code>main(String[])</code>.
104 * @param args command line arguments to pass to the
105 * <code>main(String[])</code> method of the invoked
106 * class.
107 */
108 public static void runInstrumented (String classname,
109 String [] args) throws Throwable {
110 runInstrumented(classname, args, new jInstrument());
111 }
112
113 /**
114 * Run a class with contracts enabled, using the specified instance
115 * of jInstrument. This method will replace the default class
116 * loader with a jContractorClassLoader, and run the code.
117 *
118 * @param className the name of the class to run. The class must
119 * exist on the class path, and define a
120 * <code>main(String[])</code>.
121 * @param args command line arguments to pass to the
122 * <code>main(String[])</code> method of the invoked
123 * class.
124 * @param instrumentor an instance of jInstrument to use to
125 * instrument classes.
126 */
127 public static void runInstrumented (String classname,
128 String [] args,
129 jInstrument instrumentor) throws Throwable {
130 try {
131 jContractorClassLoader classLoader = new jContractorClassLoader(instrumentor);
132 Method m;
133
134 Class instrumentedClass = classLoader.loadClass(classname);
135
136 // Call the main() method of the target class
137 // pass cmd-line args to main
138 m = instrumentedClass.getMethod("main",
139 new Class[] { args.getClass() });
140
141 m.invoke(null, new Object[] { args });
142 } catch (RuntimeException e) {e.printStackTrace();
143 // This is thrown by jContractorClassLoader.loadClass when an
144 // instrumentation problem occurs.
145 System.err.println("jContractor execution aborted: "
146 + e.getMessage());
147 } catch (InvocationTargetException e) {
148 // If the invoked class threw an exception, rethrow the
149 // exception. This is probably more useful to clients than an
150 // InvocationTargetException.
151 throw e.getTargetException();
152 }
153 }
154
155 /**
156 * @deprecated Use edu.ucsb.ccs.jaqual.Logical.implies instead.
157 */
158 public static boolean implies (boolean op1, boolean op2) {
159 return !op1 || op2;
160 }
161
162 /**
163 * Main entry point to the application. This program requires one
164 * command line argument, the name of a class to run. jContractor
165 * will replace the default class loader with a
166 * jContractorClassLoader, and run the program.
167 *
168 * @param args command line argument. The first should be the name
169 * of a class to run. The class must exist on the class
170 * path, and define a <code>main(String [])</code>
171 * method.
172 */
173 public static void main (String args[]) {
174 Options opts = new Options();
175 CommandLine cmdLine;
176 String classname;
177
178 String instrumentationFile = null, packageList = null;
179 CompositeInstrumentationFilter filter = new CompositeInstrumentationFilter();
180 boolean useDefaultInstrumentationFilter = true;
181
182 jInstrument instrumentor = new jInstrument();
183
184 try {
185 opts.addOption('v', "verbose", false, "Verbose");
186 opts.addOption('e', "version", false, "Version");
187 opts.addOption('f', true, "Instrumentation file");
188 opts.addOption('n', "none", true, "No instrumentation");
189 opts.addOption('p', "pre", true, "Precondition level instrumentation");
190 opts.addOption('o', "post", true, "Postcondition level instrumentation");
191 opts.addOption('a', "all", true, "All level instrumentation");
192 } catch (DuplicateOptionException e) {
193 System.out.println("edu.ucsb.ccs.jcontractor.jContractor.main(): " + e);
194 }
195
196 try {
197 cmdLine = opts.parse(args);
198 instrumentor.setVerbose(cmdLine.optIsSet('v'));
199
200 // Parse the instrumentation file.
201 instrumentationFile = cmdLine.getOptValue('f');
202 if (instrumentationFile != null) {
203 useDefaultInstrumentationFilter = false;
204 jInstrument.parseInstrumentationFile(instrumentationFile, filter);
205 }
206
207 packageList = cmdLine.getOptValue('n'); // --none
208 if (packageList != null) {
209 useDefaultInstrumentationFilter = false;
210 jInstrument.parseInstrumentationList(packageList,
211 InstrumentationFilter.NONE,
212 filter);
213 }
214
215 packageList = cmdLine.getOptValue('p'); // --pre
216 if (packageList != null) {
217 useDefaultInstrumentationFilter = false;
218 jInstrument.parseInstrumentationList(packageList,
219 InstrumentationFilter.PRE,
220 filter);
221 }
222
223 packageList = cmdLine.getOptValue('o'); // --post
224 if (packageList != null) {
225 useDefaultInstrumentationFilter = false;
226 jInstrument.parseInstrumentationList(packageList,
227 InstrumentationFilter.POST,
228 filter);
229 }
230
231 packageList = cmdLine.getOptValue('a'); // --all
232 if (packageList != null) {
233 useDefaultInstrumentationFilter = false;
234 jInstrument.parseInstrumentationList(packageList,
235 InstrumentationFilter.ALL,
236 filter);
237 }
238
239 // Set a specialized instrumentation filter, if neccessary.
240 if (!useDefaultInstrumentationFilter) {
241 instrumentor.setInstrumentationFilter(filter);
242 }
243
244 // If the --version option is present, just print the version
245 // number and exit. Otherwise actually instrument stuff.
246 if (cmdLine.optIsSet('e')) {
247 System.out.println(VERSION);
248 } else if (!cmdLine.getArgs().isEmpty()) {
249 Iterator iter = cmdLine.getArgs().iterator();
250 classname = (String) iter.next();
251
252 // Create an array to hold the arguments passed to the invoked
253 // program. These arguments are everything after the class
254 // name on the command line.
255 Vector argsVector = new Vector();
256 while (iter.hasNext()) {
257 argsVector.addElement((String) iter.next());
258 }
259
260 // Convert the vector to a String []
261 String [] cargs = new String [argsVector.size()];
262 for (int i = 0; i < argsVector.size(); i++) {
263 cargs[i] = (String) argsVector.elementAt(i);
264 }
265
266 runInstrumented(classname, cargs, instrumentor);
267 } else {
268 throw new MissingArgumentException("Class name missing");
269 }
270 } catch (MissingArgumentException e) {
271 System.err.println(USAGE_MESSAGE);
272 System.exit(1);
273 } catch (UnrecognizedOptionException e) {
274 System.err.println(USAGE_MESSAGE);
275 System.exit(1);
276 } catch (Throwable t) {
277 System.out.println("jContractor Exception occured!");
278 t.printStackTrace();
279 }
280 }
281 }