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.impl.enhancer;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.io.OutputStream;
22  import java.io.ByteArrayInputStream;
23  import java.io.ByteArrayOutputStream;
24  
25  import java.util.zip.CRC32;
26  import java.util.zip.ZipEntry;
27  import java.util.zip.ZipInputStream;
28  import java.util.zip.ZipOutputStream;
29  
30  
31  /***
32   *  This is a helper-class to perform some useful operations outside a
33   *  byte code enhancer and delegate the real work to the enhancer.
34   */
35  public class ClassFileEnhancerHelper
36  {
37      /***
38       *  Enhances a classfile.
39       *
40       *  @param  enhancer  The enhancer to delegate the work to.
41       *  @param  in        The input stream with the Java class.
42       *  @param  out       The output stream to write the enhanced class to.
43       *
44       *  @return  Has the input stream been enhanced?
45       *
46       *  @exception  EnhancerUserException  If something went wrong.
47       *  @exception  EnhancerFatalError     If something went wrong.
48       *
49       *  @see  ClassFileEnhancer#enhanceClassFile
50       */
51      static public boolean enhanceClassFile(ClassFileEnhancer enhancer,
52                                             InputStream      in,
53                                             OutputStream     out)
54          throws EnhancerUserException,
55          EnhancerFatalError
56      {
57          return enhancer.enhanceClassFile(in, new OutputStreamWrapper(out));
58      }
59  
60      /***
61       *  Enhances a zip file. The zip file is given as a uip input stream.
62       *  It's entries are read and - if necessary - individually enhanced.
63       *  The output stream has the same compressÌon (if any) as the input
64       *  stream.
65       *
66       *  @param  enhancer  The enhancer.
67       *  @param  zip_in    The zip input stream.
68       *  @param  zip_out   The zip output stream.
69       *
70       *  @return  <code>true</code> if at least one entry of the zip file has
71       *           been enhanced, <code>false</code> otherwise.
72       *
73       *  @exception  EnhancerUserException  If something went wrong.
74       *  @exception  EnhancerFatalError     If something went wrong.
75       *
76       *  @see  ClassFileEnhancer#enhanceClassFile
77       */
78      static public boolean enhanceZipFile(ClassFileEnhancer enhancer,
79                                           ZipInputStream zip_in,
80                                           ZipOutputStream zip_out)
81          throws EnhancerUserException,
82          EnhancerFatalError
83      {
84          boolean enhanced = false;
85          try {
86              CRC32 crc32 = new CRC32();
87              ZipEntry entry;
88              while ((entry = zip_in.getNextEntry()) != null) {
89                  InputStream in = zip_in;
90                  final ZipEntry out_entry = new ZipEntry(entry);
91  
92                  // try to enhance
93                  if (isClassFileEntry(entry)) {
94                      // enhance the classfile
95                      // we have to copy the classfile, because if it won't be
96                      // enhanced, the OutputStream is empty and we have to
97                      // re-read the InputStream, which is impossible with a
98                      // ZipInputStream (no mark/reset)
99                      in = openZipEntry(zip_in);
100                     in.mark(Integer.MAX_VALUE);
101                     final ByteArrayOutputStream tmp
102                         = new ByteArrayOutputStream();
103                     if (enhancer.enhanceClassFile(in, tmp)) {
104                         enhanced = true;
105                         final byte[] bytes = tmp.toByteArray();
106                         tmp.close();
107                         in.close();
108                         modifyZipEntry(out_entry, bytes, crc32);
109                         in = new ByteArrayInputStream(bytes);
110                     } else {
111                         // the classfile has not been enhanced
112                         in.reset();
113                     }
114                 }
115 
116                 // copy the entry
117                 zip_out.putNextEntry(out_entry);
118                 copyZipEntry(in, zip_out);
119                 zip_out.closeEntry();
120 
121                 if (in != zip_in) {
122                     in.close();
123                 }
124             }
125         } catch (IOException ex) {
126             throw new EnhancerFatalError(ex);
127         }
128 
129         return enhanced;
130     }
131 
132     /***
133      *  Copies a zip entry from one stream to another.
134      *
135      *  @param  in   The inout stream.
136      *  @param  out  The output stream.
137      *
138      *  @exception  IOException  If the stream access failed.
139      */
140     static private void copyZipEntry(InputStream  in,
141                                      OutputStream out)
142         throws IOException
143     {
144         int b;
145         while ((in.available() > 0) && (b = in.read()) > -1) {
146             out.write(b);
147         }
148     }
149 
150     /***
151      *  Opens the next zip entry of a zip input stream and copies it to
152      *  a <code>java.io.ByteArrayOutputStream</code>. It's byte array is made
153      *  available via an <code>java.io.ByteArrayInputStream</code> which is
154      *  returned.
155      *
156      *  @param  in  The zip input stream.
157      *
158      *  @return  The newly created input stream with the next zip entry.
159      *
160      *  @exception  IOException  If an I/O operation failed.
161      */
162     static private InputStream openZipEntry(ZipInputStream in)
163         throws IOException
164     {
165         ByteArrayOutputStream out = new ByteArrayOutputStream();
166         copyZipEntry(in, out);
167 
168         return new ByteArrayInputStream(out.toByteArray());
169     }
170 
171     /***
172      *  Modifies the given zip entry so that it can be added to zip file.
173      *  The given zip entry represents an enhanced class, so the zip entry
174      *  has to get the correct size and checksum (but only if the entry won't
175      *  be compressed).
176      *
177      *  @param  entry  The zip entry to modify.
178      *  @param  bytes  The uncompressed byte representation of the classfile.
179      *  @param  crc32  The checksum evaluator.
180      */
181     private static void modifyZipEntry(ZipEntry entry,
182                                        byte []  bytes,
183                                        CRC32    crc32)
184     {
185         entry.setSize(bytes.length);
186         if (entry.getMethod() == 0) {
187             //no compression (ZipInputStream.STORED - not accessible)
188             crc32.reset();
189             crc32.update(bytes);
190             entry.setCrc(crc32.getValue());
191             entry.setCompressedSize(bytes.length);
192         }
193     }
194 
195     /***
196      *  Determines if a given entry represents a classfile.
197      *
198      *  @return  Does the given entry represent a classfile?
199      */
200     private static boolean isClassFileEntry(ZipEntry entry)
201     {
202         return entry.getName().endsWith(".class");
203     }
204 }