View Javadoc

1   //////////////////////////////////////////////////////////////////////////////
2   // Clirr: compares two versions of a java library for binary compatibility
3   // Copyright (C) 2003 - 2005  Lars Kühne
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  //////////////////////////////////////////////////////////////////////////////
19  
20  package net.sf.clirr.cli;
21  
22  import net.sf.clirr.core.Checker;
23  import net.sf.clirr.core.CheckerException;
24  import net.sf.clirr.core.ClassSelector;
25  import net.sf.clirr.core.PlainDiffListener;
26  import net.sf.clirr.core.XmlDiffListener;
27  import net.sf.clirr.core.DiffListener;
28  import net.sf.clirr.core.internal.bcel.BcelTypeArrayBuilder;
29  import net.sf.clirr.core.spi.JavaType;
30  import net.sf.clirr.core.spi.Scope;
31  
32  import org.apache.commons.cli.BasicParser;
33  import org.apache.commons.cli.CommandLine;
34  import org.apache.commons.cli.HelpFormatter;
35  import org.apache.commons.cli.Options;
36  import org.apache.commons.cli.ParseException;
37  
38  import java.io.File;
39  import java.io.IOException;
40  import java.io.PrintWriter;
41  import java.net.MalformedURLException;
42  import java.net.URL;
43  import java.net.URLClassLoader;
44  import java.util.ArrayList;
45  
46  /***
47   * Commandline interface for generating a difference report or checking
48   * for binary compatibility between two versions of the same application.
49   */
50  
51  public class Clirr
52  {
53  
54      public static void main(String[] args)
55      {
56          new Clirr().run(args);
57      }
58  
59      // ===================================================================
60  
61      private void run(String[] args)
62      {
63          Options options = defineOptions();
64  
65          CommandLine cmdline = parseCommandLine(args, options);
66  
67          String oldPath = cmdline.getOptionValue('o');
68          String newPath = cmdline.getOptionValue('n');
69          String oldClassPath = cmdline.getOptionValue("ocp");
70          String newClassPath = cmdline.getOptionValue("ncp");
71          String style = cmdline.getOptionValue('s', "text");
72          String outputFileName = cmdline.getOptionValue('f');
73          String[] includePkgs = cmdline.getOptionValues('i');
74          boolean showAll = cmdline.hasOption('a');
75          boolean showPkg = cmdline.hasOption('p');
76  
77          if ((oldPath == null) || (newPath == null))
78          {
79              usage(options);
80              System.exit(-1);
81          }
82  
83          Checker checker = new Checker();
84          if (showAll)
85          {
86              checker.getScopeSelector().setScope(Scope.PRIVATE);
87          }
88          else if (showPkg)
89          {
90              checker.getScopeSelector().setScope(Scope.PACKAGE);
91          }
92  
93          ClassSelector classSelector;
94          if ((includePkgs != null) && (includePkgs.length > 0))
95          {
96              classSelector = new ClassSelector(ClassSelector.MODE_IF);
97              for (int i = 0; i < includePkgs.length; ++i)
98              {
99                  classSelector.addPackageTree(includePkgs[i]);
100             }
101         }
102         else
103         {
104             // a selector that selects everything
105             classSelector = new ClassSelector(ClassSelector.MODE_UNLESS);
106         }
107 
108         DiffListener diffListener = null;
109         if (style.equals("text"))
110         {
111             try
112             {
113                 diffListener = new PlainDiffListener(outputFileName);
114             }
115             catch (IOException ex)
116             {
117                 System.err.println("Invalid output file name.");
118             }
119         }
120         else if (style.equals("xml"))
121         {
122             try
123             {
124                 diffListener = new XmlDiffListener(outputFileName);
125             }
126             catch (IOException ex)
127             {
128                 System.err.println("Invalid output file name.");
129             }
130         }
131         else
132         {
133             System.err.println("Invalid style option. Must be one of 'text', 'xml'.");
134             usage(options);
135             System.exit(-1);
136         }
137 
138 
139         File[] origJars = pathToFileArray(oldPath);
140         File[] newJars = pathToFileArray(newPath);
141 
142         checker.addDiffListener(diffListener);
143 
144         try
145         {
146             ClassLoader loader1 = new URLClassLoader(convertFilesToURLs(pathToFileArray(oldClassPath)));
147             ClassLoader loader2 = new URLClassLoader(convertFilesToURLs(pathToFileArray(newClassPath)));
148 
149             final JavaType[] origClasses =
150                 BcelTypeArrayBuilder.createClassSet(origJars, loader1, classSelector);
151             
152             final JavaType[] newClasses =
153                 BcelTypeArrayBuilder.createClassSet(newJars, loader2, classSelector);
154             
155             checker.reportDiffs(origClasses, newClasses);
156             
157             System.exit(0);
158         }
159         catch (CheckerException ex)
160         {
161             System.err.println("Unable to complete checks:" + ex.getMessage());
162             System.exit(1);
163         }
164         catch (MalformedURLException ex)
165         {
166             System.err.println("Unable to create classloader for 3rd party classes:" + ex.getMessage());
167             System.err.println("old classpath: " + oldClassPath);
168             System.err.println("new classpath: " + newClassPath);
169             System.exit(1);
170         }
171     }
172 
173     /***
174      * @param args
175      * @param parser
176      * @param options
177      * @return
178      */
179     private CommandLine parseCommandLine(String[] args, Options options) 
180     {
181         BasicParser parser = new BasicParser();
182         CommandLine cmdline = null;
183         try
184         {
185             cmdline = parser.parse(options, args);
186         }
187         catch (ParseException ex)
188         {
189             System.err.println("Invalid command line arguments.");
190             usage(options);
191             System.exit(-1);
192         }
193         return cmdline;
194     }
195 
196     /***
197      * @return
198      */
199     private Options defineOptions() {
200         Options options = new Options();
201         options.addOption("o", "old-version", true, "jar files of old version");
202         options.addOption("n", "new-version", true, "jar files of new version");
203         options.addOption("ocp", "orig-classpath", true, "3rd party classpath that is referenced by old-version");
204         options.addOption("ncp", "new-classpath", true, "3rd party classpath that is referenced by new-version");
205         options.addOption("s", "style", true, "output style [text|xml]");
206         options.addOption("i", "include-pkg", true,
207             "include only classes from this package and its subpackages");
208         options.addOption("p", "show-pkg-scope", false,
209             "show package scope classes");
210         options.addOption("a", "show-all-scopes", false,
211             "show private and package classes");
212         options.addOption("f", "output-file", true, "output file name");
213         return options;
214     }
215 
216     private void usage(Options options)
217     {
218         HelpFormatter hf = new HelpFormatter();
219         PrintWriter out = new PrintWriter(System.err);
220         hf.printHelp(
221             75,
222             "java " + getClass().getName() + " -o path -n path [options]",
223             null, options, null);
224     }
225 
226     private File[] pathToFileArray(String path)
227     {
228         if (path == null)
229         {
230             return new File[0];            
231         }
232         
233         ArrayList files = new ArrayList();
234 
235         int pos = 0;
236         while (pos < path.length())
237         {
238             int colonPos = path.indexOf(pos, File.pathSeparatorChar);
239             if (colonPos == -1)
240             {
241                 files.add(new File(path.substring(pos)));
242                 break;
243             }
244 
245             files.add(new File(path.substring(pos, colonPos)));
246             pos = colonPos + 1;
247         }
248 
249         return (File[]) files.toArray(new File[files.size()]);
250     }
251     
252     private URL[] convertFilesToURLs(File[] files) throws MalformedURLException
253     {
254         URL[] ret = new URL[files.length];
255         for (int i = 0; i < files.length; i++) {
256             ret[i] = files[i].toURL();
257         }
258         return ret;
259     }
260 }