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.impl.enhancer.core;
19  
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.io.OutputStream;
23  import java.io.PrintWriter;
24  import java.io.DataInputStream;
25  import java.io.DataOutputStream;
26  
27  import java.util.Properties;
28  
29  import org.apache.jdo.impl.enhancer.ClassFileEnhancer;
30  import org.apache.jdo.impl.enhancer.EnhancerFatalError;
31  import org.apache.jdo.impl.enhancer.EnhancerUserException;
32  import org.apache.jdo.impl.enhancer.OutputStreamWrapper;
33  import org.apache.jdo.impl.enhancer.classfile.ClassFile;
34  import org.apache.jdo.impl.enhancer.meta.EnhancerMetaData;
35  import org.apache.jdo.impl.enhancer.meta.EnhancerMetaDataUserException;
36  import org.apache.jdo.impl.enhancer.util.Support;
37  
38  
39  
40  
41  
42  
43  
44  /***
45   * Provides a JDO byte-code enhancer.
46   */
47  public class EnhancerFilter
48      extends Support
49      implements ClassFileEnhancer
50  {
51      static public final String DO_TIMING_STATISTICS
52          = "Enhancer.doTimingStatistics";
53      static public final String DUMP_CLASS
54          = "Enhancer.dumpClass";
55      static public final String NO_AUGMENT
56          = "Enhancer.noAugment";
57      static public final String NO_ANNOTATE
58          = "Enhancer.noAnnotate";
59      static public final String VERBOSE_LEVEL
60          = "Enhancer.verboseLevel";
61      static public final String VERBOSE_LEVEL_QUIET
62          = "quiet";
63      static public final String VERBOSE_LEVEL_WARN
64          = "warn";
65      static public final String VERBOSE_LEVEL_VERBOSE
66          = "verbose";
67      static public final String VERBOSE_LEVEL_DEBUG
68          = "debug";
69  
70      /* Central repository for the options selected by
71       * the user and the current state of the Filter execution */
72      private Environment env = new Environment();
73  
74      /***
75       * Initializes an instance of a JDO enhancer.
76       * @param metaData the JDO meta-data object
77       * @param settings enhancement properties
78       * @param out standard ouput stream for the enhancer
79       */
80      protected void init(EnhancerMetaData metaData,
81                          Properties  settings,
82                          PrintWriter out,
83                          PrintWriter err)
84          throws EnhancerUserException, EnhancerFatalError
85      {
86          if (metaData == null) {
87              throw new EnhancerFatalError(
88                  getI18N("enhancer.internal_error",
89                          "Illegal argument: metaData == null"));
90          }
91  
92          env.setEnhancerMetaData(metaData);
93  
94          final String doTiming
95              = (settings == null
96                 ? null
97                 : settings.getProperty(DO_TIMING_STATISTICS));
98          env.setDoTimingStatistics(Boolean.valueOf(doTiming).booleanValue());
99  
100         final String dumpClass
101             = (settings == null
102                ? null
103                : settings.getProperty(DUMP_CLASS));
104         env.setDumpClass(Boolean.valueOf(dumpClass).booleanValue());
105 
106         final String noAugment
107             = (settings == null
108                ? null
109                : settings.getProperty(NO_AUGMENT));
110         env.setNoAugment(Boolean.valueOf(noAugment).booleanValue());
111 
112         final String noAnnotate
113             = (settings == null
114                ? null
115                : settings.getProperty(NO_ANNOTATE));
116         env.setNoAnnotate(Boolean.valueOf(noAnnotate).booleanValue());
117 
118         // set verbose level
119         if  (err != null) {
120             env.setErrorWriter(err);
121         }
122         if  (out != null) {
123             env.setOutputWriter(out);
124         }
125         final String verboseLevel
126             = (settings == null ? null : settings.getProperty(VERBOSE_LEVEL));
127         if (VERBOSE_LEVEL_QUIET.equals(verboseLevel)) {
128             env.setVerbose(false);
129             env.setQuiet(true);
130         } else if (VERBOSE_LEVEL_WARN.equals(verboseLevel)) {
131             env.setVerbose(false);
132             env.setQuiet(false);
133         } else if (VERBOSE_LEVEL_VERBOSE.equals(verboseLevel)) {
134             env.setVerbose(true);
135             env.setQuiet(false);
136         } else if (VERBOSE_LEVEL_DEBUG.equals(verboseLevel)) {
137             env.setVerbose(true);
138             env.setQuiet(false);
139         } else {
140             env.setVerbose(false);
141             env.setQuiet(false);
142         }
143     }
144 
145     /***
146      * Creates an instance of a JDO enhancer.
147      * @param metaData the JDO meta-data object
148      * @param settings enhancement properties
149      * @param out standard ouput stream for the enhancer
150      */
151     public EnhancerFilter(EnhancerMetaData metaData,
152                     Properties  settings,
153                     PrintWriter out,
154                     PrintWriter err)
155         throws EnhancerUserException, EnhancerFatalError
156     {
157         init(metaData, settings, out, err);
158     }
159 
160     /***
161      * Enhances a given class according to the JDO meta-data.
162      */
163     private boolean enhanceClassFile1(InputStream inClassFile,
164                                       OutputStreamWrapper outClassFile)
165         throws EnhancerUserException
166     {
167         // check arguments
168         affirm(inClassFile, "Illegal argument: inClassFile == null.");
169         affirm(outClassFile, "Illegal argument: outClassFile == null.");
170 
171         // parse class
172         final ClassFile cf;
173         final Controller cc;
174         try {
175             // create class file
176             final DataInputStream dis = new DataInputStream(inClassFile);
177             final boolean allowJDK12ClassFiles = true;
178             cf = new ClassFile(dis, allowJDK12ClassFiles);
179             //@lars: do not close the input stream
180             //dis.close();
181 
182             // create class control
183             cc = new Controller(cf, env);
184 
185             // get real class name
186             final String className = cf.classNameString();
187         } catch (ClassFormatError ex) {
188             throw new EnhancerUserException(
189                 getI18N("enhancer.class_format_error"),
190                 ex);
191         }
192 
193         // enhance class
194         cc.enhanceClass();
195         if (env.errorCount() > 0) {
196             // retrieve error messages
197             env.getErrorWriter().flush();
198 
199             throw new EnhancerUserException(env.getLastErrorMessage());
200         }
201         affirm(env.errorCount() == 0);
202 
203         // write class
204         boolean changed = cc.updated();
205         try {
206             if (changed) {
207                 env.message("writing enhanced class " + cf.userClassName()
208                             + " to output stream");
209             } else {
210                 env.message("no changes to class " + cf.userClassName());
211             }
212             outClassFile.setClassName(cf.userClassName());
213             final DataOutputStream dos
214                 = new DataOutputStream(outClassFile.getStream());
215             cf.write(dos);
216             dos.flush();
217         } catch (IOException ex) {
218             throw new EnhancerUserException(
219                 getI18N("enhancer.io_error_while_writing_stream"),
220                 ex);
221         }
222         return changed;
223     }
224 
225     /***
226      * Enhances a given class according to the JDO meta-data.
227      */
228     public boolean enhanceClassFile(InputStream         inClassFile,
229                                     OutputStreamWrapper outClassFile)
230         throws EnhancerUserException, EnhancerFatalError
231     {
232         env.verbose("---------------------------------------------------------------------------");
233         env.messageNL("Enhancer: enhancing classfile ...");
234 
235         // reset environment to clear class map etc.
236         env.reset();
237 
238         // enhance class file; check Exceptions
239         final boolean changed;
240         try {
241             changed = enhanceClassFile1(inClassFile, outClassFile);
242         } catch (EnhancerUserException ex) {
243             // reset environment to clear class map etc.
244             env.reset();
245             throw ex;
246         } catch (EnhancerMetaDataUserException ex) {
247             // note: catch EnhancerMetaDataUserException before RuntimeException
248 
249             // reset environment to clear class map etc.
250             env.reset();
251             throw new EnhancerUserException(
252                 getI18N("enhancer.error", ex.getMessage()),
253                 ex);
254         } catch (RuntimeException ex) {
255             // reset environment to clear class map etc.
256             env.reset();
257             ex.printStackTrace ();
258             throw new EnhancerFatalError(
259                 getI18N("enhancer.internal_error", ex.getMessage()),
260                 ex);
261         }
262 
263         env.messageNL(changed
264                       ? "Enhancer: classfile enhanced successfully."
265                       : "Enhancer: classfile not changed.");
266         return changed;
267     }
268 
269     public boolean enhanceClassFile(InputStream in,
270                                     OutputStream out)
271         throws EnhancerUserException,
272         EnhancerFatalError
273     {
274         return enhanceClassFile(in, new OutputStreamWrapper(out));
275     }
276 }