001: /*
002:
003: Derby - Class org.apache.derby.impl.services.reflect.JarLoader
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.services.reflect;
023:
024: import org.apache.derby.impl.sql.execute.JarUtil;
025: import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
026: import org.apache.derby.iapi.error.StandardException;
027:
028: import java.io.File;
029: import java.io.InputStream;
030: import java.io.IOException;
031:
032: import java.util.zip.ZipFile;
033: import java.util.zip.ZipInputStream;
034: import java.util.zip.ZipEntry;
035:
036: import org.apache.derby.iapi.services.io.LimitInputStream;
037: import org.apache.derby.iapi.util.IdUtil;
038:
039: import org.apache.derby.iapi.reference.MessageId;
040: import org.apache.derby.iapi.services.i18n.MessageService;
041:
042: public class JarLoader extends ClassLoader {
043:
044: private static final JarFile jarFileFactory;
045:
046: static {
047:
048: //
049: jarFileFactory = new JarFileJava2();
050: }
051:
052: private UpdateLoader updateLoader;
053: private JarFile jf;
054: private HeaderPrintWriter vs;
055:
056: JarLoader(UpdateLoader updateLoader, String[] name,
057: HeaderPrintWriter vs) {
058:
059: this .updateLoader = updateLoader;
060: this .jf = jarFileFactory.newJarFile(name);
061: this .vs = vs;
062: }
063:
064: // Initialize the class loader so it knows if it
065: // is loading from a ZipFile or an InputStream
066: void initialize() {
067:
068: Object zipData = load();
069:
070: try {
071:
072: if (zipData instanceof File) {
073: jf.initialize((File) zipData);
074: return;
075: }
076:
077: if (zipData instanceof InputStream) {
078: jf.isStream = true;
079: try {
080: ((InputStream) zipData).close();
081: } catch (IOException ioe) {
082: }
083: return;
084: }
085: } catch (IOException ioe) {
086: if (vs != null)
087: vs.println(MessageService.getTextMessage(
088: MessageId.CM_LOAD_JAR_EXCEPTION, getJarName(),
089: ioe));
090: }
091:
092: // No such zip.
093: setInvalid(false);
094: }
095:
096: /**
097: Handle all requests to the top-level loader.
098:
099: @exception ClassNotFoundException Class can not be found
100: */
101: public Class loadClass(String className, boolean resolve)
102: throws ClassNotFoundException {
103:
104: // we attempt the system class load even if we
105: // are stale because otherwise we will fail
106: // to load java.* classes which confuses some VMs
107: try {
108: return Class.forName(className);
109: } catch (ClassNotFoundException cnfe) {
110:
111: if (updateLoader == null)
112: throw new ClassNotFoundException(MessageService
113: .getTextMessage(MessageId.CM_STALE_LOADER,
114: className));
115:
116: Class c = updateLoader.loadClass(className, resolve);
117: if (c == null)
118: throw cnfe;
119: return c;
120: }
121: }
122:
123: /**
124:
125: */
126: public InputStream getResourceAsStream(String name) {
127: if (updateLoader == null)
128: return null;
129: return updateLoader.getResourceAsStream(name);
130: }
131:
132: /*
133: ** Package level api
134: */
135: final String getJarName() {
136: return jf.getJarName();
137: }
138:
139: Class loadClassData(String className, String jvmClassName,
140: boolean resolve) {
141:
142: if (updateLoader == null)
143: return null;
144:
145: try {
146: if (jf.isZip())
147: return loadClassDataFromJar(className, jvmClassName,
148: resolve);
149:
150: if (jf.isStream) {
151: // have to use a new stream each time
152: return loadClassData((InputStream) load(), className,
153: jvmClassName, resolve);
154: }
155:
156: return null;
157: } catch (IOException ioe) {
158: if (vs != null)
159: vs.println(MessageService.getTextMessage(
160: MessageId.CM_CLASS_LOAD_EXCEPTION, className,
161: getJarName(), ioe));
162: return null;
163: }
164: }
165:
166: /**
167: Get an InputStream for the given resource.
168: */
169: InputStream getStream(String name) {
170:
171: if (updateLoader == null)
172: return null;
173:
174: if (jf.isZip())
175: return getRawStream(jf.getZip(), name);
176:
177: if (jf.isStream) {
178: return getRawStream((InputStream) load(), name);
179: }
180: return null;
181: }
182:
183: /*
184: ** Private api
185: */
186:
187: private Class loadClassDataFromJar(String className,
188: String jvmClassName, boolean resolve) throws IOException {
189:
190: ZipEntry ze = jf.getEntry(jvmClassName);
191: if (ze == null)
192: return null;
193:
194: InputStream in = jf.getZip().getInputStream(ze);
195:
196: try {
197: return loadClassData(ze, in, className, resolve);
198: } finally {
199: in.close();
200: }
201: }
202:
203: private Class loadClassData(InputStream in, String className,
204: String jvmClassName, boolean resolve) throws IOException {
205:
206: ZipInputStream zipIn = jf.getZipOnStream(in);
207:
208: for (;;) {
209:
210: ZipEntry ze = jf.getNextEntry(zipIn);
211: if (ze == null) {
212: zipIn.close();
213: return null;
214: }
215:
216: if (ze.getName().equals(jvmClassName)) {
217: Class c = loadClassData(ze, zipIn, className, resolve);
218: zipIn.close();
219: return c;
220: }
221: }
222:
223: }
224:
225: private Class loadClassData(ZipEntry ze, InputStream in,
226: String className, boolean resolve) throws IOException {
227:
228: byte[] data = jf.readData(ze, in, className);
229:
230: Object[] signers = jf.getSigners(className, ze);
231:
232: synchronized (updateLoader) {
233: // see if someone else loaded it while we
234: // were getting the bytes ...
235: Class c = updateLoader.checkLoaded(className, resolve);
236: if (c == null) {
237: c = defineClass(className, data, 0, data.length);
238: if (signers != null) {
239: setSigners(c, signers);
240: }
241: if (resolve)
242: resolveClass(c);
243: }
244: return c;
245:
246: }
247: }
248:
249: Class checkLoaded(String className, boolean resolve) {
250: if (updateLoader == null)
251: return null;
252:
253: Class c = findLoadedClass(className);
254: if ((c != null) && resolve)
255: resolveClass(c);
256: return c;
257: }
258:
259: private Object load() {
260:
261: String[] dbJarName = jf.name;
262:
263: String schemaName = dbJarName[IdUtil.DBCP_SCHEMA_NAME];
264: String sqlName = dbJarName[IdUtil.DBCP_SQL_JAR_NAME];
265:
266: // don't need a connection, just call the code directly
267: try {
268: return updateLoader.getJarReader().readJarFile(schemaName,
269: sqlName);
270: } catch (StandardException se) {
271: if (vs != null)
272: vs.println(MessageService.getTextMessage(
273: MessageId.CM_LOAD_JAR_EXCEPTION, jf
274: .getJarName(), se));
275: return null;
276: }
277:
278: }
279:
280: JarFile setInvalid(boolean newJarFile) {
281:
282: jf.setInvalid();
283: updateLoader = null;
284: return newJarFile ? jarFileFactory.newJarFile(jf.name) : null;
285: }
286:
287: /*
288: ** Routines to get an InputStream for a namedResource
289: */
290:
291: /**
292: Get a stream directly from a ZipFile.
293: In this case we can safely return the stream directly.
294: It's a new stream set up by the zip code to read just
295: the contents of this entry.
296: */
297: private InputStream getRawStream(ZipFile zip, String name) {
298:
299: try {
300: ZipEntry ze = zip.getEntry(name);
301: if (ze == null)
302: return null;
303:
304: return zip.getInputStream(ze);
305: } catch (IOException ioe) {
306: return null;
307: }
308: }
309:
310: /**
311: Get a stream from a zip file that is itself a stream.
312: Here we need to get the size of the zip entry and
313: put a limiting stream around it. Otherwise the
314: caller would end up reading the entire zip file!
315: */
316: private InputStream getRawStream(InputStream in, String name) {
317:
318: ZipInputStream zipIn = null;
319: try {
320: zipIn = new ZipInputStream(in);
321:
322: ZipEntry ze;
323: while ((ze = jf.getNextEntry(zipIn)) != null) {
324:
325: if (ze.getName().equals(name)) {
326: LimitInputStream lis = new LimitInputStream(zipIn);
327: lis.setLimit((int) ze.getSize());
328: return lis;
329: }
330: }
331:
332: zipIn.close();
333:
334: } catch (IOException ioe) {
335: if (zipIn != null) {
336: try {
337: zipIn.close();
338: } catch (IOException ioe2) {
339: }
340: }
341: }
342: return null;
343: }
344: }
|