View Javadoc

1   /*
2    * Copyright 2005 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
15   */
16  
17  package org.apache.jdo.enhancer;
18  
19  import java.io.IOException;
20  import java.io.File;
21  import java.io.InputStream;
22  import java.io.OutputStream;
23  import java.io.FileInputStream;
24  import java.io.FileOutputStream;
25  import java.io.BufferedInputStream;
26  import java.io.BufferedOutputStream;
27  import java.io.PrintWriter;
28  
29  import java.util.List;
30  import java.util.Iterator;
31  import java.util.Properties;
32  
33  import java.util.zip.ZipInputStream;
34  import java.util.zip.ZipOutputStream;
35  
36  import org.apache.jdo.impl.enhancer.ClassFileEnhancer;
37  import org.apache.jdo.impl.enhancer.ClassFileEnhancerHelper;
38  import org.apache.jdo.impl.enhancer.ClassFileEnhancerTimer;
39  import org.apache.jdo.impl.enhancer.EnhancerFatalError;
40  import org.apache.jdo.impl.enhancer.EnhancerOptions;
41  import org.apache.jdo.impl.enhancer.EnhancerUserException;
42  import org.apache.jdo.impl.enhancer.JdoMetaMain;
43  import org.apache.jdo.impl.enhancer.OutputStreamWrapper;
44  import org.apache.jdo.impl.enhancer.core.EnhancerFilter;
45  
46  
47  
48  /***
49   * JDO command line enhancer.
50   *
51   * @author Martin Zaun
52   */
53  public class EnhancerMain
54      extends JdoMetaMain
55  {
56      /***
57       *  The options and arguments.
58       */
59      protected EnhancerOptions options;
60  
61      /***
62       *  The byte code enhancer.
63       */
64      protected ClassFileEnhancer enhancer;
65  
66      /***
67       * Creates an instance.
68       */
69      public EnhancerMain(PrintWriter out,
70                          PrintWriter err)
71      {
72          this(out, err, new EnhancerOptions(out, err));
73      }
74  
75      /***
76       * Creates an instance.
77       */
78      public EnhancerMain(PrintWriter out,
79                          PrintWriter err,
80                          EnhancerOptions options)
81      {
82          super(out, err, options);
83          this.options = options;
84      }
85  
86      // ----------------------------------------------------------------------
87  
88      /***
89       * Enhances all files entered in the command line.
90       *
91       * @param  classNames  List of class names.
92       * @param  classFileNames  List of class file names.
93       * @param  archiveFileNames  List of archive file names.
94       */
95      private int enhanceInputFiles(List classNames,
96                                    List classFileNames,
97                                    List archiveFileNames)
98      {
99          int res = 0;
100         try {
101             String name = null;
102             for (Iterator i = archiveFileNames.iterator(); i.hasNext();) {
103                 try {
104                     name = (String)i.next();
105                     enhanceArchiveFile(name);
106                 } catch (EnhancerUserException ex) {
107                     printlnErr("Error while enhancing " + name, ex, 
108                                options.verbose.value);
109                     res++;
110                     continue;
111                 }
112             }
113             for (Iterator i = classFileNames.iterator(); i.hasNext();) {
114                 try {
115                     name = (String)i.next();
116                     enhanceClassFile(openFileInputStream(name));
117                 } catch (EnhancerUserException ex) {
118                     printlnErr("Error while enhancing " + name, ex, 
119                                options.verbose.value);
120                     res++;
121                     continue;
122                 }
123             }
124             for (Iterator i = classNames.iterator(); i.hasNext();) {
125                 try {
126                     name = (String)i.next();
127                     enhanceClassFile(openClassInputStream(name));
128                 } catch (EnhancerUserException ex) {
129                     printlnErr("Error while enhancing " + name, ex, 
130                                options.verbose.value);
131                     res++;
132                     continue;
133                 }
134             }
135         } catch (IOException ex) {
136             printlnErr("IO Error while enhancing", ex, options.verbose.value);
137             return ++res;
138         } catch (EnhancerFatalError ex) {
139             // enhancer is not anymore guaranteed to be consistent
140             printlnErr("Fatal error while enhancing", ex, options.verbose.value);
141             enhancer = null;
142             return ++res;
143         }
144         return res;
145     }
146 
147     /***
148      * Enhances a classfile.
149      *
150      * @param  in  The input stream of the classfile.
151      */
152     private void enhanceClassFile(InputStream in)
153         throws IOException, EnhancerUserException, EnhancerFatalError
154     {
155         OutputStream out = null;
156         try {
157             final File temp = File.createTempFile("enhancer", ".class");
158             out = new BufferedOutputStream(new FileOutputStream(temp));
159 
160             //enhance
161             final OutputStreamWrapper wrapper = new OutputStreamWrapper(out);
162             final boolean enhanced = enhancer.enhanceClassFile(in, wrapper);
163 
164             closeOutputStream(out);
165             out = null;
166             createOutputFile(enhanced,
167                              getClassFileName(wrapper.getClassName()), temp);
168         } finally {
169             closeInputStream(in);
170             closeOutputStream(out);
171         }
172     }
173 
174     /***
175      * Enhances a archive file.
176      *
177      * @param  fileName  The filename of the archive file.
178      */
179     private void enhanceArchiveFile(String fileName)
180         throws IOException, EnhancerUserException, EnhancerFatalError
181     {
182         ZipInputStream in = null;
183         ZipOutputStream out = null;
184         try {
185             final File temp = File.createTempFile("enhancer", ".zip");
186             in = new ZipInputStream(new BufferedInputStream(
187                 new FileInputStream(new File(fileName))));
188             out = new ZipOutputStream(new BufferedOutputStream(
189                 new FileOutputStream(temp)));
190 
191             // enhance the archive file
192             final boolean enhanced
193                 = ClassFileEnhancerHelper.enhanceZipFile(enhancer, in, out);
194 
195             // create the output file
196             closeOutputStream(out);
197             out = null;
198             createOutputFile(enhanced, new File(fileName).getName(), temp);
199         } finally {
200             closeOutputStream(out);
201             closeInputStream(in);
202         }
203     }
204 
205     /***
206      * Creates a file object that represents the output archive file for
207      * a given archive file to enhance.
208      *
209      * @param  archiveFileName  the input archive file name
210      * @return  the output archive file
211      */
212     private File createArchiveOutputFile(String archiveFileName)
213     {
214         return new File(options.destDir.value,
215                         new File(archiveFileName).getName());
216     }
217 
218     /***
219      * Creates the output file for an enhanced class- or archive file. If the
220      * enhanced file is written back depends on the command line options.
221      *
222      * @param  enhanced  Has the input file been enhanced?
223      * @param  fileName  The name of the output file.
224      * @param  temp      The temp file, the output is written to.
225      * @exception  IOException  If the file could not be created.
226      */
227     private void createOutputFile(boolean enhanced,
228                                   String fileName,
229                                   File temp)
230         throws IOException
231     {
232         //noWrite or (not enhanced and not forceWrite)
233         if (options.noWrite.value
234             || (!enhanced && !options.forceWrite.value)) {
235             temp.deleteOnExit();
236             return;
237         }
238 
239         // create file and its parent directory
240         final File file = new File(options.destDir.value, fileName);
241         final File dir = file.getAbsoluteFile().getParentFile();
242         if (!dir.exists() && !dir.mkdirs()) {
243             throw new IOException("Error creating directory '"
244                                   + dir.getAbsolutePath() + "'.");
245         }
246 
247         file.delete();  //delete old file if exists
248         boolean renamed = temp.renameTo(file);
249         if (!renamed) {
250             //@dave: empirical evidence shows that renameTo does not allow for
251             // crossing filesystem boundaries.  If it fails, try "by hand".
252             InputStream in = null;
253             OutputStream out = null;
254             try {
255                 in = new FileInputStream(temp);
256                 out = new FileOutputStream(file);
257                 int PAGESIZE = 4096; // Suggest a better size?
258                 byte data[] = new byte[PAGESIZE];
259                 while (in.available() > 0) {
260                     int numRead = in.read(data, 0, PAGESIZE);
261                     out.write(data, 0, numRead);
262                 }
263                 renamed = true;
264             } catch (IOException ex) {
265                 throw new IOException("Could not rename temp file '" +
266                                       temp.getAbsolutePath() +
267                                       "' to '" + file.getAbsolutePath()
268                                       + "': " + ex);
269             } finally {
270                 closeInputStream(in);
271                 closeOutputStream(out);
272             }
273             if (renamed) {
274                 temp.delete();  //delete temporary file
275             }
276             else {
277                 throw new IOException("Could not rename temp file '" +
278                                       temp.getAbsolutePath() +
279                                       "' to '" + file.getAbsolutePath() + "'.");
280             }
281         }
282     }
283 
284     /***
285      * Closes an output stream.
286      *
287      * @param  out  the output stream
288      */
289     private void closeOutputStream(OutputStream out)
290     {
291         if (out != null) {
292             try {
293                 out.close();
294             } catch (IOException ex) {
295                 printlnErr("", ex, options.verbose.value);
296             }
297         }
298     }
299 
300     // ----------------------------------------------------------------------
301 
302     /***
303      * Initializes all components.
304      */
305     protected void init()
306         throws EnhancerFatalError, EnhancerUserException
307     {
308         super.init();
309 
310         final Properties props = new Properties();
311         if (options.verbose.value) {
312             props.put(EnhancerFilter.VERBOSE_LEVEL,
313                       EnhancerFilter.VERBOSE_LEVEL_VERBOSE);
314         }
315         
316         if (options.doTiming.value) {
317             props.put(EnhancerFilter.DO_TIMING_STATISTICS,
318                       Boolean.TRUE.toString());
319         }
320 
321         if (options.dumpClass.value) {
322             props.put(EnhancerFilter.DUMP_CLASS,
323                       Boolean.TRUE.toString());
324         }
325 
326         if (options.noAugment.value) {
327             props.put(EnhancerFilter.NO_AUGMENT,
328                       Boolean.TRUE.toString());
329         }
330 
331         if (options.noAnnotate.value) {
332             props.put(EnhancerFilter.NO_ANNOTATE,
333                       Boolean.TRUE.toString());
334         }
335 
336         try {
337             enhancer = new EnhancerFilter(jdoMeta, props, out, err);
338             if (options.doTiming.value) {
339                 // wrap with timing byte-code enhancer
340                 enhancer = new ClassFileEnhancerTimer(enhancer);
341             }
342         } catch (EnhancerUserException ex) {
343             printlnErr("Error while creating the enhancer", ex, 
344                        options.verbose.value);
345             throw ex;
346         } catch (EnhancerFatalError ex) {
347             // enhancer is not anymore guaranteed to be consistent
348             printlnErr("Fatal error while creating the enhancer", ex, 
349                        options.verbose.value);
350             enhancer = null;
351             throw ex;
352         } catch (RuntimeException ex) {
353             // enhancer is not anymore guaranteed to be consistent
354             printlnErr("Internal error while creating the enhancer", ex, 
355                        options.verbose.value);
356             enhancer = null;
357             throw new EnhancerFatalError(ex);
358         }
359     }
360 
361     /***
362      * Run the enhancer.
363      */
364     protected int process()
365     {
366         return enhanceInputFiles(options.classNames,
367                                  options.classFileNames,
368                                  options.archiveFileNames);
369     }
370 
371     // ----------------------------------------------------------------------
372 
373     /***
374      * Runs this class
375      */
376     static public void main(String[] args)
377     {
378         final PrintWriter out = new PrintWriter(System.out, true);
379         out.println("--> EnhancerMain.main()");
380         //out.println("JDO RI Class-File Enhancer");
381         final EnhancerMain main = new EnhancerMain(out, out);
382         int res = main.run(args);
383         //out.println("done.");
384         out.println("<-- EnhancerMain.main(): exit = " + res);
385         System.exit(res);
386     }
387 }