001: /*
002:
003: Derby - Class org.apache.derby.impl.services.reflect.UpdateLoader
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.iapi.services.context.ContextService;
025: import org.apache.derby.iapi.services.monitor.Monitor;
026: import org.apache.derby.iapi.services.monitor.Monitor;
027: import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
028: import org.apache.derby.iapi.util.IdUtil;
029: import org.apache.derby.iapi.error.StandardException;
030: import org.apache.derby.iapi.services.locks.ShExLockable;
031: import org.apache.derby.iapi.services.locks.ShExQual;
032: import org.apache.derby.iapi.services.locks.LockFactory;
033: import org.apache.derby.iapi.services.locks.Latch;
034: import org.apache.derby.iapi.services.locks.C_LockFactory;
035: import org.apache.derby.iapi.services.loader.ClassFactoryContext;
036: import org.apache.derby.iapi.services.loader.JarReader;
037: import org.apache.derby.iapi.services.property.PersistentSet;
038:
039: import org.apache.derby.iapi.services.property.PropertyUtil;
040: import org.apache.derby.iapi.reference.Property;
041:
042: import java.io.InputStream;
043:
044: import org.apache.derby.iapi.reference.MessageId;
045: import org.apache.derby.iapi.reference.Module;
046: import org.apache.derby.iapi.services.i18n.MessageService;
047:
048: public class UpdateLoader {
049:
050: private JarLoader[] jarList;
051: private HeaderPrintWriter vs;
052: private final ClassLoader myLoader;
053: private boolean initDone;
054: private String this Classpath;
055: private final LockFactory lf;
056: private final ShExLockable classLoaderLock;
057: private int version;
058: private boolean normalizeToUpper;
059: private DatabaseClasses parent;
060:
061: private boolean needReload;
062: private JarReader jarReader;
063:
064: public UpdateLoader(String classpath, DatabaseClasses parent,
065: boolean verbose, boolean normalizeToUpper)
066: throws StandardException {
067:
068: this .normalizeToUpper = normalizeToUpper;
069: this .parent = parent;
070: lf = (LockFactory) Monitor.getServiceModule(parent,
071: Module.LockFactory);
072:
073: if (verbose) {
074: vs = Monitor.getStream();
075: }
076:
077: myLoader = getClass().getClassLoader();
078:
079: this .classLoaderLock = new ClassLoaderLock(this );
080:
081: initializeFromClassPath(classpath);
082: }
083:
084: private void initializeFromClassPath(String classpath)
085: throws StandardException {
086:
087: String[][] elements = IdUtil.parseDbClassPath(classpath,
088: normalizeToUpper);
089:
090: int jarCount = elements.length;
091: jarList = new JarLoader[jarCount];
092:
093: for (int i = 0; i < jarCount; i++) {
094: jarList[i] = new JarLoader(this , elements[i], vs);
095: }
096:
097: if (vs != null) {
098: vs.println(MessageService.getTextMessage(
099: MessageId.CM_CLASS_LOADER_START, classpath));
100: }
101:
102: this Classpath = classpath;
103: initDone = false;
104: }
105:
106: /**
107: Load the class from the class path.
108:
109: @exception ClassNotFoundException Class can not be found
110: */
111: public Class loadClass(String className, boolean resolve)
112: throws ClassNotFoundException {
113:
114: JarLoader jl = null;
115:
116: boolean unlockLoader = false;
117: try {
118: unlockLoader = lockClassLoader(ShExQual.SH);
119:
120: synchronized (this ) {
121:
122: if (needReload) {
123: reload();
124: }
125:
126: Class clazz = checkLoaded(className, resolve);
127: if (clazz != null)
128: return clazz;
129:
130: String jvmClassName = className.replace('.', '/')
131: .concat(".class");
132:
133: if (!initDone)
134: initLoaders();
135:
136: for (int i = 0; i < jarList.length; i++) {
137:
138: jl = jarList[i];
139:
140: Class c = jl.loadClassData(className, jvmClassName,
141: resolve);
142: if (c != null) {
143: if (vs != null)
144: vs.println(MessageService.getTextMessage(
145: MessageId.CM_CLASS_LOAD, className,
146: jl.getJarName()));
147:
148: return c;
149: }
150: }
151: }
152:
153: return null;
154:
155: } catch (StandardException se) {
156: throw new ClassNotFoundException(MessageService
157: .getTextMessage(MessageId.CM_CLASS_LOAD_EXCEPTION,
158: className, jl == null ? null : jl
159: .getJarName(), se));
160: } finally {
161: if (unlockLoader) {
162: lf.unlock(this , this , classLoaderLock, ShExQual.SH);
163: }
164: }
165: }
166:
167: public InputStream getResourceAsStream(String name) {
168:
169: InputStream is = (myLoader == null) ? ClassLoader
170: .getSystemResourceAsStream(name) : myLoader
171: .getResourceAsStream(name);
172:
173: if (is != null)
174: return is;
175:
176: // match behaviour of standard class loaders.
177: if (name.endsWith(".class"))
178: return null;
179:
180: boolean unlockLoader = false;
181: try {
182: unlockLoader = lockClassLoader(ShExQual.SH);
183:
184: synchronized (this ) {
185:
186: if (needReload) {
187: reload();
188: }
189:
190: if (!initDone)
191: initLoaders();
192:
193: for (int i = 0; i < jarList.length; i++) {
194:
195: JarLoader jl = jarList[i];
196:
197: is = jl.getStream(name);
198: if (is != null) {
199: return is;
200: }
201: }
202: }
203: return null;
204:
205: } catch (StandardException se) {
206: return null;
207: } finally {
208: if (unlockLoader) {
209: lf.unlock(this , this , classLoaderLock, ShExQual.SH);
210: }
211: }
212: }
213:
214: public synchronized void modifyClasspath(String classpath)
215: throws StandardException {
216:
217: // lock transaction classloader exclusively
218: lockClassLoader(ShExQual.EX);
219: version++;
220:
221: modifyJar(false);
222: initializeFromClassPath(classpath);
223: }
224:
225: public synchronized void modifyJar(boolean reload)
226: throws StandardException {
227:
228: // lock transaction classloader exclusively
229: lockClassLoader(ShExQual.EX);
230: version++;
231:
232: if (!initDone)
233: return;
234:
235: if (reload) {
236: //first close the existing jar file opens
237: close();
238: initializeFromClassPath(this Classpath);
239: return;
240: }
241:
242: // first thing to do is to remove all Class entries
243: // and then get a complete set of loaders ...
244: synchronized (this ) {
245:
246: for (int i = 0; i < jarList.length; i++) {
247:
248: JarLoader jl = jarList[i];
249:
250: JarFile newJarFile = jl.setInvalid(reload);
251: }
252: }
253: }
254:
255: private boolean lockClassLoader(ShExQual qualifier)
256: throws StandardException {
257:
258: if (lf == null)
259: return false;
260:
261: ClassFactoryContext cfc = (ClassFactoryContext) ContextService
262: .getContextOrNull(ClassFactoryContext.CONTEXT_ID);
263:
264: // This method can be called from outside of the database
265: // engine, in which case tc will be null. In that case
266: // we lock the class loader only for the duration of
267: // the loadClass().
268: Object lockSpace = null;
269:
270: if (cfc != null) {
271: lockSpace = cfc.getLockSpace();
272: }
273: if (lockSpace == null)
274: lockSpace = this ;
275:
276: lf.lockObject(lockSpace, lockSpace, classLoaderLock, qualifier,
277: C_LockFactory.TIMED_WAIT);
278:
279: return (lockSpace == this );
280: }
281:
282: Class checkLoaded(String className, boolean resolve) {
283:
284: for (int i = 0; i < jarList.length; i++) {
285: Class c = jarList[i].checkLoaded(className, resolve);
286: if (c != null)
287: return c;
288: }
289: return null;
290: }
291:
292: public void close() {
293:
294: for (int i = 0; i < jarList.length; i++) {
295: jarList[i].setInvalid(false);
296: }
297:
298: }
299:
300: private void initLoaders() {
301:
302: if (initDone)
303: return;
304:
305: for (int i = 0; i < jarList.length; i++) {
306: jarList[i].initialize();
307: }
308: initDone = true;
309: }
310:
311: public int getClassLoaderVersion() {
312: return version;
313: }
314:
315: synchronized void needReload() {
316: version++;
317: needReload = true;
318: }
319:
320: private void reload() throws StandardException {
321: this Classpath = getClasspath();
322: // first close the existing jar file opens
323: close();
324: initializeFromClassPath(this Classpath);
325: needReload = false;
326: }
327:
328: private String getClasspath() throws StandardException {
329:
330: ClassFactoryContext cfc = (ClassFactoryContext) ContextService
331: .getContextOrNull(ClassFactoryContext.CONTEXT_ID);
332:
333: PersistentSet ps = cfc.getPersistentSet();
334:
335: String classpath = PropertyUtil.getServiceProperty(ps,
336: Property.DATABASE_CLASSPATH);
337:
338: //
339: //In per database mode we must always have a classpath. If we do not
340: //yet have one we make one up.
341: if (classpath == null)
342: classpath = "";
343:
344: return classpath;
345: }
346:
347: JarReader getJarReader() {
348: if (jarReader == null) {
349:
350: ClassFactoryContext cfc = (ClassFactoryContext) ContextService
351: .getContextOrNull(ClassFactoryContext.CONTEXT_ID);
352:
353: jarReader = cfc.getJarReader();
354: }
355: return jarReader;
356: }
357: }
358:
359: class ClassLoaderLock extends ShExLockable {
360:
361: private UpdateLoader myLoader;
362:
363: ClassLoaderLock(UpdateLoader myLoader) {
364: this .myLoader = myLoader;
365: }
366:
367: public void unlockEvent(Latch lockInfo) {
368: super .unlockEvent(lockInfo);
369:
370: if (lockInfo.getQualifier().equals(ShExQual.EX)) {
371: // how do we tell if we are reverting or not
372: myLoader.needReload();
373: }
374: }
375: }
|