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