View Javadoc

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