001: //////////////////////////////////////////////////////////////////////////////
002: // Clirr: compares two versions of a java library for binary compatibility
003: // Copyright (C) 2003 - 2005 Lars Kühne
004: //
005: // This library is free software; you can redistribute it and/or
006: // modify it under the terms of the GNU Lesser General Public
007: // License as published by the Free Software Foundation; either
008: // version 2.1 of the License, or (at your option) any later version.
009: //
010: // This library is distributed in the hope that it will be useful,
011: // but WITHOUT ANY WARRANTY; without even the implied warranty of
012: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: // Lesser General Public License for more details.
014: //
015: // You should have received a copy of the GNU Lesser General Public
016: // License along with this library; if not, write to the Free Software
017: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: //////////////////////////////////////////////////////////////////////////////
019:
020: package net.sf.clirr.cli;
021:
022: import net.sf.clirr.core.Checker;
023: import net.sf.clirr.core.CheckerException;
024: import net.sf.clirr.core.ClassSelector;
025: import net.sf.clirr.core.PlainDiffListener;
026: import net.sf.clirr.core.XmlDiffListener;
027: import net.sf.clirr.core.DiffListener;
028: import net.sf.clirr.core.internal.bcel.BcelTypeArrayBuilder;
029: import net.sf.clirr.core.spi.JavaType;
030: import net.sf.clirr.core.spi.Scope;
031:
032: import org.apache.commons.cli.BasicParser;
033: import org.apache.commons.cli.CommandLine;
034: import org.apache.commons.cli.HelpFormatter;
035: import org.apache.commons.cli.Options;
036: import org.apache.commons.cli.ParseException;
037:
038: import java.io.File;
039: import java.io.IOException;
040: import java.io.PrintWriter;
041: import java.net.MalformedURLException;
042: import java.net.URL;
043: import java.net.URLClassLoader;
044: import java.util.ArrayList;
045:
046: /**
047: * Commandline interface for generating a difference report or checking
048: * for binary compatibility between two versions of the same application.
049: */
050:
051: public class Clirr {
052:
053: public static void main(String[] args) {
054: new Clirr().run(args);
055: }
056:
057: // ===================================================================
058:
059: private void run(String[] args) {
060: Options options = defineOptions();
061:
062: CommandLine cmdline = parseCommandLine(args, options);
063:
064: String oldPath = cmdline.getOptionValue('o');
065: String newPath = cmdline.getOptionValue('n');
066: String oldClassPath = cmdline.getOptionValue("ocp");
067: String newClassPath = cmdline.getOptionValue("ncp");
068: String style = cmdline.getOptionValue('s', "text");
069: String outputFileName = cmdline.getOptionValue('f');
070: String[] includePkgs = cmdline.getOptionValues('i');
071: boolean showAll = cmdline.hasOption('a');
072: boolean showPkg = cmdline.hasOption('p');
073:
074: if ((oldPath == null) || (newPath == null)) {
075: usage(options);
076: System.exit(-1);
077: }
078:
079: Checker checker = new Checker();
080: if (showAll) {
081: checker.getScopeSelector().setScope(Scope.PRIVATE);
082: } else if (showPkg) {
083: checker.getScopeSelector().setScope(Scope.PACKAGE);
084: }
085:
086: ClassSelector classSelector;
087: if ((includePkgs != null) && (includePkgs.length > 0)) {
088: classSelector = new ClassSelector(ClassSelector.MODE_IF);
089: for (int i = 0; i < includePkgs.length; ++i) {
090: classSelector.addPackageTree(includePkgs[i]);
091: }
092: } else {
093: // a selector that selects everything
094: classSelector = new ClassSelector(ClassSelector.MODE_UNLESS);
095: }
096:
097: DiffListener diffListener = null;
098: if (style.equals("text")) {
099: try {
100: diffListener = new PlainDiffListener(outputFileName);
101: } catch (IOException ex) {
102: System.err.println("Invalid output file name.");
103: }
104: } else if (style.equals("xml")) {
105: try {
106: diffListener = new XmlDiffListener(outputFileName);
107: } catch (IOException ex) {
108: System.err.println("Invalid output file name.");
109: }
110: } else {
111: System.err
112: .println("Invalid style option. Must be one of 'text', 'xml'.");
113: usage(options);
114: System.exit(-1);
115: }
116:
117: File[] origJars = pathToFileArray(oldPath);
118: File[] newJars = pathToFileArray(newPath);
119:
120: checker.addDiffListener(diffListener);
121:
122: try {
123: ClassLoader loader1 = new URLClassLoader(
124: convertFilesToURLs(pathToFileArray(oldClassPath)));
125: ClassLoader loader2 = new URLClassLoader(
126: convertFilesToURLs(pathToFileArray(newClassPath)));
127:
128: final JavaType[] origClasses = BcelTypeArrayBuilder
129: .createClassSet(origJars, loader1, classSelector);
130:
131: final JavaType[] newClasses = BcelTypeArrayBuilder
132: .createClassSet(newJars, loader2, classSelector);
133:
134: checker.reportDiffs(origClasses, newClasses);
135:
136: System.exit(0);
137: } catch (CheckerException ex) {
138: System.err.println("Unable to complete checks:"
139: + ex.getMessage());
140: System.exit(1);
141: } catch (MalformedURLException ex) {
142: System.err
143: .println("Unable to create classloader for 3rd party classes:"
144: + ex.getMessage());
145: System.err.println("old classpath: " + oldClassPath);
146: System.err.println("new classpath: " + newClassPath);
147: System.exit(1);
148: }
149: }
150:
151: /**
152: * @param args
153: * @param parser
154: * @param options
155: * @return
156: */
157: private CommandLine parseCommandLine(String[] args, Options options) {
158: BasicParser parser = new BasicParser();
159: CommandLine cmdline = null;
160: try {
161: cmdline = parser.parse(options, args);
162: } catch (ParseException ex) {
163: System.err.println("Invalid command line arguments.");
164: usage(options);
165: System.exit(-1);
166: }
167: return cmdline;
168: }
169:
170: /**
171: * @return
172: */
173: private Options defineOptions() {
174: Options options = new Options();
175: options.addOption("o", "old-version", true,
176: "jar files of old version");
177: options.addOption("n", "new-version", true,
178: "jar files of new version");
179: options
180: .addOption("ocp", "orig-classpath", true,
181: "3rd party classpath that is referenced by old-version");
182: options
183: .addOption("ncp", "new-classpath", true,
184: "3rd party classpath that is referenced by new-version");
185: options
186: .addOption("s", "style", true,
187: "output style [text|xml]");
188: options
189: .addOption("i", "include-pkg", true,
190: "include only classes from this package and its subpackages");
191: options.addOption("p", "show-pkg-scope", false,
192: "show package scope classes");
193: options.addOption("a", "show-all-scopes", false,
194: "show private and package classes");
195: options.addOption("f", "output-file", true, "output file name");
196: return options;
197: }
198:
199: private void usage(Options options) {
200: HelpFormatter hf = new HelpFormatter();
201: PrintWriter out = new PrintWriter(System.err);
202: hf.printHelp(75, "java " + getClass().getName()
203: + " -o path -n path [options]", null, options, null);
204: }
205:
206: private File[] pathToFileArray(String path) {
207: if (path == null) {
208: return new File[0];
209: }
210:
211: ArrayList files = new ArrayList();
212:
213: int pos = 0;
214: while (pos < path.length()) {
215: int colonPos = path.indexOf(pos, File.pathSeparatorChar);
216: if (colonPos == -1) {
217: files.add(new File(path.substring(pos)));
218: break;
219: }
220:
221: files.add(new File(path.substring(pos, colonPos)));
222: pos = colonPos + 1;
223: }
224:
225: return (File[]) files.toArray(new File[files.size()]);
226: }
227:
228: private URL[] convertFilesToURLs(File[] files)
229: throws MalformedURLException {
230: URL[] ret = new URL[files.length];
231: for (int i = 0; i < files.length; i++) {
232: ret[i] = files[i].toURL();
233: }
234: return ret;
235: }
236: }
|