001: /*
002: Copyright (c) 2003-2005, Dennis M. Sosnoski
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without modification,
006: are permitted provided that the following conditions are met:
007:
008: * Redistributions of source code must retain the above copyright notice, this
009: list of conditions and the following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: * Neither the name of JiBX nor the names of its contributors may be used
014: to endorse or promote products derived from this software without specific
015: prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028:
029: package org.jibx.binding;
030:
031: import java.io.ByteArrayOutputStream;
032: import java.io.File;
033: import java.io.IOException;
034: import java.net.URL;
035: import java.net.URLClassLoader;
036:
037: import org.apache.bcel.classfile.Method;
038: import org.apache.bcel.verifier.VerificationResult;
039: import org.apache.bcel.verifier.Verifier;
040: import org.apache.bcel.verifier.VerifierFactory;
041: import org.jibx.binding.classes.BoundClass;
042: import org.jibx.binding.classes.BranchWrapper;
043: import org.jibx.binding.classes.ClassCache;
044: import org.jibx.binding.classes.ClassFile;
045: import org.jibx.binding.classes.MungedClass;
046: import org.jibx.binding.def.BindingDefinition;
047: import org.jibx.runtime.JiBXException;
048:
049: /**
050: * Binding compiler. This version checks the modified and generated classes
051: * by loading them and listing method information.
052: *
053: * @author Dennis M. Sosnoski
054: * @version 1.0
055: */
056:
057: public class Compile {
058: private boolean m_verbose;
059: private boolean m_load;
060: private boolean m_verify;
061: private boolean m_trackBranches;
062: private boolean m_errorOverride;
063: private boolean m_skipValidate;
064:
065: /**
066: * Default constructor. This just initializes all options disabled.
067: */
068:
069: public Compile() {
070: }
071:
072: /**
073: * Constructor with settings specified.
074: *
075: * @param verbose report binding details and results
076: * @param load test load modified classes to validate
077: * @param verify use BCEL validation of modified classes
078: * @param track keep tracking information for source of branch generation
079: * @param over override code generation error handling
080: */
081: public Compile(boolean verbose, boolean load, boolean verify,
082: boolean track, boolean over) {
083: m_verbose = verbose;
084: m_load = load;
085: m_verify = verify;
086: m_trackBranches = track;
087: m_errorOverride = over;
088: }
089:
090: /**
091: * Verify generated and modified files using BCEL verifier. This provides a
092: * more comprehensive listing of errors than just loading a class in the
093: * JVM.
094: *
095: * @param file information for class to be verified
096: * @return <code>true</code> if successfully verified, <code>false</code> if
097: * problem found (automatically reported)
098: */
099: private boolean verifyBCEL(ClassFile file) {
100: try {
101:
102: // construct verifier for class file
103: Verifier verifier = VerifierFactory.getVerifier(file
104: .getName());
105:
106: // run validation in stages with error handling for each stage
107: boolean verified = false;
108: VerificationResult vr = verifier.doPass1();
109: if (vr.getStatus() == VerificationResult.VERIFIED_OK) {
110: vr = verifier.doPass2();
111: if (vr.getStatus() == VerificationResult.VERIFIED_OK) {
112: Method[] methods = file.getRawClass().getMethods();
113: for (int j = 0; j < methods.length; j++) {
114: vr = verifier.doPass3a(j);
115: if (vr.getStatus() == VerificationResult.VERIFIED_OK) {
116: vr = verifier.doPass3b(j);
117: }
118: if (vr.getStatus() == VerificationResult.VERIFIED_OK) {
119: verified = true;
120: } else {
121: System.out
122: .println("Verification failure on method "
123: + methods[j].getName()
124: + " of class "
125: + file.getName() + ":");
126: System.out.println(" " + vr.toString());
127: }
128: }
129: } else {
130: System.out.println("Verification failure on class "
131: + file.getName() + ":");
132: System.out.println(" " + vr.toString());
133: }
134: } else {
135: System.out.println("Verification failure on class "
136: + file.getName() + ":");
137: System.out.println(" " + vr.toString());
138: }
139: return verified;
140:
141: } catch (Exception ex) { // catch BCEL errors
142: System.out.println("BCEL failure:");
143: ex.printStackTrace();
144: return false;
145: }
146: }
147:
148: /**
149: * Output all generated and modified class files. Writes the actual files,
150: * and optionally verifies and/or reports the changes.
151: *
152: * @param paths array of paths used for loading files
153: * @exception JiBXException if error in processing the binding definition
154: * @exception IOException if path cannot be accessed
155: */
156: private void handleOutput(String[] paths) throws JiBXException,
157: IOException {
158:
159: // output the modified class files
160: ClassFile[][] lists = MungedClass.fixChanges(true);
161:
162: // report modified file results to user
163: ClassFile[] files = lists[0];
164: if (m_verbose) {
165: System.out.println("\nWrote " + files.length + " files");
166: }
167: if (m_verbose || m_load) {
168:
169: // generate class paths as URLs if needed for test loading
170: URL[] urls = null;
171: if (m_load) {
172: urls = new URL[paths.length];
173: for (int i = 0; i < urls.length; i++) {
174: urls[i] = new File(paths[i]).toURL();
175: }
176: }
177: for (int i = 0; i < files.length; i++) {
178:
179: // write class file to bytes
180: ClassFile file = files[i];
181: ByteArrayOutputStream bos = new ByteArrayOutputStream();
182: file.writeFile(bos);
183: byte[] bytes = bos.toByteArray();
184: if (m_verbose) {
185: System.out.println("\n " + file.getName()
186: + " output file size is " + bytes.length
187: + " bytes");
188: }
189:
190: // verify using BCEL verifier
191: if (m_verify) {
192: verifyBCEL(file);
193: }
194:
195: // load to JVM and list method information from class
196: if (m_load) {
197: DirectLoader cloader = new DirectLoader(urls);
198: Class clas = cloader.load(file.getName(), bytes);
199: if (m_verbose) {
200: java.lang.reflect.Method[] methods = clas
201: .getDeclaredMethods();
202: System.out.println(" Found " + methods.length
203: + " methods:");
204: for (int j = 0; j < methods.length; j++) {
205: java.lang.reflect.Method method = methods[j];
206: System.out.println(" "
207: + method.getReturnType().getName()
208: + " " + method.getName());
209: }
210: }
211: }
212: }
213: }
214:
215: // report summary information for files unchanged or deleted
216: if (m_verbose) {
217: files = lists[1];
218: System.out.println("\nKept " + files.length
219: + " files unchanged:");
220: for (int i = 0; i < files.length; i++) {
221: System.out.println(" " + files[i].getName());
222: }
223: files = lists[2];
224: System.out.println("\nDeleted " + files.length + " files:");
225: for (int i = 0; i < files.length; i++) {
226: System.out.println(" " + files[i].getName());
227: }
228: }
229: }
230:
231: /**
232: * Set control flag for test loading generated/modified classes.
233: *
234: * @param load test load generated/modified classes flag
235: */
236: public void setLoad(boolean load) {
237: m_load = load;
238: }
239:
240: /**
241: * Set control flag for verbose processing reports.
242: *
243: * @param verbose report verbose information in processing bindings flag
244: */
245: public void setVerbose(boolean verbose) {
246: m_verbose = verbose;
247: }
248:
249: /**
250: * Set control flag for verifying generated/modified classes with BCEL.
251: *
252: * @param verify use BCEL verification for generated/modified classes flag
253: */
254: public void setVerify(boolean verify) {
255: m_verify = verify;
256: }
257:
258: /**
259: * Set control flag for skipping binding validation. This flag is intended
260: * only for use while processing the binding model components within JiBX.
261: * Otherwise it'd be impossible to correct errors in the binding validation.
262: *
263: * @param skip test load generated/modified classes flag
264: */
265: public void setSkipValidate(boolean skip) {
266: m_skipValidate = skip;
267: }
268:
269: /**
270: * Compile a set of bindings using supplied classpaths.
271: *
272: * @param paths list of paths for loading classes
273: * @param files list of binding definition files
274: * @exception JiBXException if error in processing the binding definition
275: */
276: public void compile(String[] paths, String[] files)
277: throws JiBXException {
278: try {
279:
280: // include current version information in verbose output
281: if (m_verbose) {
282: System.out.println("Running binding compiler version "
283: + BindingDefinition.CURRENT_VERSION_NAME);
284: }
285:
286: // set paths to be used for loading referenced classes
287: ClassCache.setPaths(paths);
288: ClassFile.setPaths(paths);
289:
290: // reset static information accumulation for binding
291: BoundClass.reset();
292: MungedClass.reset();
293: BindingDefinition.reset();
294: BranchWrapper.setTracking(m_trackBranches);
295: BranchWrapper.setErrorOverride(m_errorOverride);
296:
297: // load all supplied bindings
298: BindingDefinition[] defs = new BindingDefinition[files.length];
299: for (int i = 0; i < files.length; i++) {
300: defs[i] = Utility.loadFileBinding(files[i],
301: !m_skipValidate);
302: if (m_verbose) {
303: defs[i].print();
304: }
305: }
306:
307: // modify the class files with JiBX hooks
308: for (int i = 0; i < defs.length; i++) {
309: try {
310: defs[i].generateCode(m_verbose);
311: } catch (RuntimeException e) {
312: throw new JiBXException(
313: "\n*** Error during code generation for file '"
314: + files[i]
315: + "' - please enter a bug report for this "
316: + "error in Jira if the problem is not listed as fixed "
317: + "on the online status page ***\n",
318: e);
319: }
320: }
321:
322: // handle file and reporting output
323: handleOutput(paths);
324:
325: } catch (IOException ex) {
326: throw new JiBXException("IOException in compile", ex);
327: } catch (ExceptionInInitializerError ex) {
328: throw new JiBXException("Error during initialization;"
329: + " is jibx-run.jar in load classpath?", ex
330: .getException());
331: } catch (Throwable ex) {
332: throw new JiBXException("Error running binding compiler",
333: ex);
334: }
335: }
336:
337: /**
338: * Main method for running compiler as application.
339: *
340: * @param args command line arguments
341: */
342: public static void main(String[] args) {
343: if (args.length > 0) {
344: try {
345:
346: // check for various flags set
347: boolean verbose = false;
348: boolean load = false;
349: boolean verify = false;
350: boolean track = false;
351: boolean over = false;
352: boolean skip = false;
353: int offset = 0;
354: for (; offset < 5 && offset < args.length; offset++) {
355: String arg = args[offset];
356: if ("-v".equalsIgnoreCase(arg)) {
357: verbose = true;
358: } else if ("-l".equalsIgnoreCase(arg)) {
359: load = true;
360: } else if ("-b".equalsIgnoreCase(arg)) {
361: verify = true;
362: } else if ("-o".equalsIgnoreCase(arg)) {
363: over = true;
364: } else if ("-s".equalsIgnoreCase(arg)) {
365: skip = true;
366: } else if ("-t".equalsIgnoreCase(arg)) {
367: track = true;
368: } else {
369: break;
370: }
371: }
372:
373: // set up path and binding lists
374: String[] clsspths = Utility.getClassPaths();
375: String[] bindings = new String[args.length - offset];
376: System.arraycopy(args, offset, bindings, 0,
377: bindings.length);
378:
379: // report on the configuration
380: if (verbose) {
381: System.out.println("Using paths:");
382: for (int i = 0; i < clsspths.length; i++) {
383: System.out.println(" " + clsspths[i]);
384: }
385: System.out.println("Using bindings:");
386: for (int i = 0; i < bindings.length; i++) {
387: System.out.println(" " + bindings[i]);
388: }
389: }
390:
391: // compile the bindings
392: Compile compiler = new Compile(verbose, load, verify,
393: track, over);
394: compiler.setSkipValidate(skip);
395: compiler.compile(clsspths, bindings);
396:
397: } catch (JiBXException ex) {
398: ex.printStackTrace(System.out);
399: System.exit(1);
400: }
401:
402: } else {
403: System.out
404: .println("\nUsage: java org.jibx.binding.Compile [-b] [-l] [-v] "
405: + "binding1 binding2 ...\nwhere:\n -b turns on BCEL "
406: + "verification (debug option),\n -l turns on test loading of "
407: + "modified or generated classes for validation, and\n"
408: + " -v turns on verbose output\nThe bindingn files are "
409: + "different bindings to be compiled.\n");
410: System.exit(1);
411: }
412: }
413:
414: /**
415: * Direct class loader. This is optionally used for test loading the
416: * modified class files to make sure they're still valid.
417: */
418: private static class DirectLoader extends URLClassLoader {
419: protected DirectLoader(URL[] urls) {
420: super (urls, DirectLoader.class.getClassLoader());
421: }
422:
423: protected Class load(String name, byte[] data) {
424: return defineClass(name, data, 0, data.length);
425: }
426: }
427: }
|