1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.services.reflect.JarLoader |
4 | |
5 | Copyright 1998, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.impl.services.reflect; |
22 | |
23 | import org.apache.derby.impl.sql.execute.JarUtil; |
24 | import org.apache.derby.iapi.services.stream.HeaderPrintWriter; |
25 | import org.apache.derby.iapi.error.StandardException; |
26 | |
27 | import java.io.File; |
28 | import java.io.InputStream; |
29 | import java.io.IOException; |
30 | |
31 | import java.util.zip.ZipFile; |
32 | import java.util.zip.ZipInputStream; |
33 | import java.util.zip.ZipEntry; |
34 | |
35 | |
36 | import org.apache.derby.iapi.services.io.LimitInputStream; |
37 | import org.apache.derby.iapi.util.IdUtil; |
38 | |
39 | import org.apache.derby.iapi.reference.MessageId; |
40 | import org.apache.derby.iapi.services.i18n.MessageService; |
41 | |
42 | |
43 | public class JarLoader extends ClassLoader { |
44 | |
45 | private static final JarFile jarFileFactory; |
46 | |
47 | static { |
48 | |
49 | // |
50 | jarFileFactory = new JarFileJava2(); |
51 | } |
52 | |
53 | private UpdateLoader updateLoader; |
54 | private JarFile jf; |
55 | private HeaderPrintWriter vs; |
56 | |
57 | JarLoader(UpdateLoader updateLoader, String[] name, HeaderPrintWriter vs) { |
58 | |
59 | this.updateLoader = updateLoader; |
60 | this.jf = jarFileFactory.newJarFile(name); |
61 | this.vs = vs; |
62 | } |
63 | |
64 | // Initialize the class loader so it knows if it |
65 | // is loading from a ZipFile or an InputStream |
66 | void initialize() { |
67 | |
68 | Object zipData = load(); |
69 | |
70 | try { |
71 | |
72 | if (zipData instanceof File) { |
73 | jf.initialize((File) zipData); |
74 | return; |
75 | } |
76 | |
77 | if (zipData instanceof InputStream) { |
78 | jf.isStream = true; |
79 | try { |
80 | ((InputStream) zipData).close(); |
81 | } catch (IOException ioe) { |
82 | } |
83 | return; |
84 | } |
85 | } catch (IOException ioe) { |
86 | if (vs != null) |
87 | vs.println(MessageService.getTextMessage(MessageId.CM_LOAD_JAR_EXCEPTION, getJarName(), ioe)); |
88 | } |
89 | |
90 | // No such zip. |
91 | setInvalid(false); |
92 | } |
93 | |
94 | /** |
95 | Handle all requests to the top-level loader. |
96 | |
97 | @exception ClassNotFoundException Class can not be found |
98 | */ |
99 | public Class loadClass(String className, boolean resolve) |
100 | throws ClassNotFoundException { |
101 | |
102 | // we attempt the system class load even if we |
103 | // are stale because otherwise we will fail |
104 | // to load java.* classes which confuses some VMs |
105 | try { |
106 | return Class.forName(className); |
107 | } catch (ClassNotFoundException cnfe) { |
108 | |
109 | if (updateLoader == null) |
110 | throw new ClassNotFoundException(MessageService.getTextMessage(MessageId.CM_STALE_LOADER, className)); |
111 | |
112 | Class c = updateLoader.loadClass(className, resolve); |
113 | if (c == null) |
114 | throw cnfe; |
115 | return c; |
116 | } |
117 | } |
118 | |
119 | /** |
120 | |
121 | */ |
122 | public InputStream getResourceAsStream(String name) { |
123 | if (updateLoader == null) |
124 | return null; |
125 | return updateLoader.getResourceAsStream(name); |
126 | } |
127 | |
128 | /* |
129 | ** Package level api |
130 | */ |
131 | final String getJarName() { |
132 | return jf.getJarName(); |
133 | } |
134 | |
135 | Class loadClassData(String className, String jvmClassName, boolean resolve) { |
136 | |
137 | if (updateLoader == null) |
138 | return null; |
139 | |
140 | try { |
141 | if (jf.isZip()) |
142 | return loadClassDataFromJar(className, jvmClassName, resolve); |
143 | |
144 | if (jf.isStream) { |
145 | // have to use a new stream each time |
146 | return loadClassData((InputStream) load(), |
147 | className, jvmClassName, resolve); |
148 | } |
149 | |
150 | return null; |
151 | } catch (IOException ioe) { |
152 | if (vs != null) |
153 | vs.println(MessageService.getTextMessage(MessageId.CM_CLASS_LOAD_EXCEPTION, className, getJarName(), ioe)); |
154 | return null; |
155 | } |
156 | } |
157 | |
158 | /** |
159 | Get an InputStream for the given resource. |
160 | */ |
161 | InputStream getStream(String name) { |
162 | |
163 | if (updateLoader == null) |
164 | return null; |
165 | |
166 | if (jf.isZip()) |
167 | return getRawStream(jf.getZip(), name); |
168 | |
169 | if (jf.isStream) { |
170 | return getRawStream((InputStream) load(), name); |
171 | } |
172 | return null; |
173 | } |
174 | |
175 | |
176 | /* |
177 | ** Private api |
178 | */ |
179 | |
180 | |
181 | private Class loadClassDataFromJar(String className, String jvmClassName, boolean resolve) |
182 | throws IOException { |
183 | |
184 | ZipEntry ze = jf.getEntry(jvmClassName); |
185 | if (ze == null) |
186 | return null; |
187 | |
188 | InputStream in = jf.getZip().getInputStream(ze); |
189 | |
190 | try { |
191 | return loadClassData(ze, in, className, resolve); |
192 | } finally { |
193 | in.close(); |
194 | } |
195 | } |
196 | |
197 | private Class loadClassData( |
198 | InputStream in, String className, String jvmClassName, boolean resolve) |
199 | throws IOException { |
200 | |
201 | ZipInputStream zipIn = jf.getZipOnStream(in); |
202 | |
203 | for (;;) { |
204 | |
205 | ZipEntry ze = jf.getNextEntry(zipIn); |
206 | if (ze == null) { |
207 | zipIn.close(); |
208 | return null; |
209 | } |
210 | |
211 | if (ze.getName().equals(jvmClassName)) { |
212 | Class c = loadClassData(ze, zipIn, className, resolve); |
213 | zipIn.close(); |
214 | return c; |
215 | } |
216 | } |
217 | |
218 | } |
219 | |
220 | private Class loadClassData(ZipEntry ze, InputStream in, |
221 | String className, boolean resolve) throws IOException { |
222 | |
223 | byte[] data = jf.readData(ze, in, className); |
224 | |
225 | Object[] signers = jf.getSigners(className, ze); |
226 | |
227 | synchronized (updateLoader) { |
228 | // see if someone else loaded it while we |
229 | // were getting the bytes ... |
230 | Class c = updateLoader.checkLoaded(className, resolve); |
231 | if (c == null) { |
232 | c = defineClass(className, data, 0, data.length); |
233 | if (signers != null) { |
234 | setSigners(c, signers); |
235 | } |
236 | if (resolve) |
237 | resolveClass(c); |
238 | } |
239 | return c; |
240 | |
241 | } |
242 | } |
243 | |
244 | Class checkLoaded(String className, boolean resolve) { |
245 | if (updateLoader == null) |
246 | return null; |
247 | |
248 | Class c = findLoadedClass(className); |
249 | if ((c != null) && resolve) |
250 | resolveClass(c); |
251 | return c; |
252 | } |
253 | |
254 | private Object load() { |
255 | |
256 | String[] dbJarName = jf.name; |
257 | |
258 | String schemaName = dbJarName[IdUtil.DBCP_SCHEMA_NAME]; |
259 | String sqlName = dbJarName[IdUtil.DBCP_SQL_JAR_NAME]; |
260 | |
261 | // don't need a connection, just call the code directly |
262 | try { |
263 | return updateLoader.getJarReader().readJarFile(schemaName, sqlName); |
264 | } catch (StandardException se) { |
265 | if (vs != null) |
266 | vs.println(MessageService.getTextMessage(MessageId.CM_LOAD_JAR_EXCEPTION, jf.getJarName(), se)); |
267 | return null; |
268 | } |
269 | |
270 | } |
271 | |
272 | JarFile setInvalid(boolean newJarFile) { |
273 | |
274 | jf.setInvalid(); |
275 | updateLoader = null; |
276 | return newJarFile ? jarFileFactory.newJarFile(jf.name) : null; |
277 | } |
278 | |
279 | /* |
280 | ** Routines to get an InputStream for a namedResource |
281 | */ |
282 | |
283 | /** |
284 | Get a stream directly from a ZipFile. |
285 | In this case we can safely return the stream directly. |
286 | It's a new stream set up by the zip code to read just |
287 | the contents of this entry. |
288 | */ |
289 | private InputStream getRawStream(ZipFile zip, String name) { |
290 | |
291 | try { |
292 | ZipEntry ze = zip.getEntry(name); |
293 | if (ze == null) |
294 | return null; |
295 | |
296 | return zip.getInputStream(ze); |
297 | } catch (IOException ioe) { |
298 | return null; |
299 | } |
300 | } |
301 | |
302 | /** |
303 | Get a stream from a zip file that is itself a stream. |
304 | Here we need to get the size of the zip entry and |
305 | put a limiting stream around it. Otherwise the |
306 | caller would end up reading the entire zip file! |
307 | */ |
308 | private InputStream getRawStream(InputStream in, String name) { |
309 | |
310 | ZipInputStream zipIn = null; |
311 | try { |
312 | zipIn = new ZipInputStream(in); |
313 | |
314 | ZipEntry ze; |
315 | while ((ze = jf.getNextEntry(zipIn)) != null) { |
316 | |
317 | if (ze.getName().equals(name)) { |
318 | LimitInputStream lis = new LimitInputStream(zipIn); |
319 | lis.setLimit((int) ze.getSize()); |
320 | return lis; |
321 | } |
322 | } |
323 | |
324 | zipIn.close(); |
325 | |
326 | } catch (IOException ioe) { |
327 | if (zipIn != null) { |
328 | try { |
329 | zipIn.close(); |
330 | } catch (IOException ioe2) { |
331 | } |
332 | } |
333 | } |
334 | return null; |
335 | } |
336 | } |