001: /*
002:
003: Derby - Class org.apache.derby.impl.services.reflect.DatabaseClasses
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.sanity.SanityManager;
025:
026: import org.apache.derby.iapi.services.loader.ClassFactory;
027: import org.apache.derby.iapi.services.loader.GeneratedClass;
028: import org.apache.derby.iapi.services.loader.ClassInspector;
029:
030: import org.apache.derby.iapi.services.monitor.ModuleControl;
031: import org.apache.derby.iapi.services.monitor.ModuleSupportable;
032: import org.apache.derby.iapi.services.monitor.Monitor;
033:
034: import org.apache.derby.iapi.error.StandardException;
035: import org.apache.derby.iapi.services.property.PropertyUtil;
036:
037: import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
038: import org.apache.derby.iapi.services.monitor.Monitor;
039:
040: import org.apache.derby.iapi.services.compiler.*;
041: import java.lang.reflect.Modifier;
042: import org.apache.derby.iapi.sql.compile.CodeGeneration;
043:
044: import org.apache.derby.iapi.util.ByteArray;
045: import org.apache.derby.iapi.services.io.FileUtil;
046: import org.apache.derby.iapi.services.i18n.MessageService;
047: import org.apache.derby.iapi.reference.Property;
048: import org.apache.derby.iapi.reference.SQLState;
049: import org.apache.derby.iapi.reference.MessageId;
050: import org.apache.derby.iapi.reference.ClassName;
051:
052: import java.util.Properties;
053: import java.util.Hashtable;
054:
055: import java.io.ObjectStreamClass;
056: import java.io.File;
057: import java.io.FileOutputStream;
058: import java.io.IOException;
059: import java.io.Serializable;
060:
061: /**
062:
063: An abstract implementation of the ClassFactory. This package can
064: be extended to fully implement a ClassFactory. Implementations can
065: differ in two areas, how they load a class and how they invoke methods
066: of the generated class.
067:
068: <P>
069: This class manages a hash table of loaded generated classes and
070: their GeneratedClass objects. A loaded class may be referenced
071: multiple times -- each class has a reference count associated
072: with it. When a load request arrives, if the class has already
073: been loaded, its ref count is incremented. For a remove request,
074: the ref count is decremented unless it is the last reference,
075: in which case the class is removed. This is transparent to users.
076:
077: @see org.apache.derby.iapi.services.loader.ClassFactory
078: */
079:
080: abstract class DatabaseClasses implements ClassFactory, ModuleControl {
081: /*
082: ** Fields
083: */
084:
085: private ClassInspector classInspector;
086: private JavaFactory javaFactory;
087:
088: private UpdateLoader applicationLoader;
089:
090: /*
091: ** Constructor
092: */
093:
094: DatabaseClasses() {
095: }
096:
097: /*
098: ** Public methods of ModuleControl
099: */
100:
101: public void boot(boolean create, Properties startParams)
102: throws StandardException {
103:
104: classInspector = new ClassInspector(this );
105:
106: //
107: //The ClassFactory runs per service (database) mode (booted as a service module after AccessFactory).
108: //If the code that booted
109: //us needs a per-database classpath then they pass in the classpath using
110: //the runtime property BOOT_DB_CLASSPATH in startParams
111:
112: String classpath = null;
113: if (startParams != null) {
114: classpath = startParams
115: .getProperty(Property.BOOT_DB_CLASSPATH);
116: }
117:
118: if (classpath != null) {
119: applicationLoader = new UpdateLoader(classpath, this , true,
120: true);
121: }
122:
123: javaFactory = (JavaFactory) org.apache.derby.iapi.services.monitor.Monitor
124: .startSystemModule(org.apache.derby.iapi.reference.Module.JavaFactory);
125: }
126:
127: public void stop() {
128: if (applicationLoader != null)
129: applicationLoader.close();
130: }
131:
132: /*
133: ** Public methods of ClassFactory
134: */
135:
136: /**
137: Here we load the newly added class now, rather than waiting for the
138: findGeneratedClass(). Thus we are assuming that the class is going
139: to be used sometime soon. Delaying the load would mean storing the class
140: data in a file, this wastes cycles and compilcates the cleanup.
141:
142: @see ClassFactory#loadGeneratedClass
143:
144: @exception StandardException Class format is bad.
145: */
146: public final GeneratedClass loadGeneratedClass(
147: String fullyQualifiedName, ByteArray classDump)
148: throws StandardException {
149:
150: try {
151:
152: return loadGeneratedClassFromData(fullyQualifiedName,
153: classDump);
154:
155: } catch (LinkageError le) {
156:
157: WriteClassFile(fullyQualifiedName, classDump, le);
158:
159: throw StandardException.newException(
160: SQLState.GENERATED_CLASS_LINKAGE_ERROR, le,
161: fullyQualifiedName);
162:
163: } catch (VirtualMachineError vme) { // these may be beyond saving, but fwiw
164:
165: WriteClassFile(fullyQualifiedName, classDump, vme);
166:
167: throw vme;
168: }
169:
170: }
171:
172: private static void WriteClassFile(String fullyQualifiedName,
173: ByteArray bytecode, Throwable t) {
174:
175: // get the un-qualified name and add the extension
176: int lastDot = fullyQualifiedName.lastIndexOf((int) '.');
177: String filename = fullyQualifiedName.substring(lastDot + 1,
178: fullyQualifiedName.length()).concat(".class");
179:
180: Object env = Monitor.getMonitor().getEnvironment();
181: File dir = env instanceof File ? (File) env : null;
182:
183: File classFile = FileUtil.newFile(dir, filename);
184:
185: // find the error stream
186: HeaderPrintWriter errorStream = Monitor.getStream();
187:
188: try {
189: FileOutputStream fis = new FileOutputStream(classFile);
190: fis.write(bytecode.getArray(), bytecode.getOffset(),
191: bytecode.getLength());
192: fis.flush();
193: if (t != null) {
194: errorStream.printlnWithHeader(MessageService
195: .getTextMessage(MessageId.CM_WROTE_CLASS_FILE,
196: fullyQualifiedName, classFile, t));
197: }
198: fis.close();
199: } catch (IOException e) {
200: if (SanityManager.DEBUG)
201: SanityManager
202: .THROWASSERT("Unable to write .class file");
203: }
204: }
205:
206: public ClassInspector getClassInspector() {
207: return classInspector;
208: }
209:
210: public final Class loadApplicationClass(String className)
211: throws ClassNotFoundException {
212:
213: if (className.startsWith("org.apache.derby.")) {
214: // Assume this is an engine class, if so
215: // try to load from this class loader,
216: // this ensures in strange class loader
217: // environments we do not get ClassCastExceptions
218: // when an engine class is loaded through a different
219: // class loader to the rest of the engine.
220: try {
221: return Class.forName(className);
222: } catch (ClassNotFoundException cnfe) {
223: // fall through to the code below,
224: // could be client or tools class
225: // in a different loader.
226: }
227: }
228:
229: Throwable loadError;
230: try {
231: try {
232: return loadClassNotInDatabaseJar(className);
233: } catch (ClassNotFoundException cnfe) {
234: if (applicationLoader == null)
235: throw cnfe;
236: Class c = applicationLoader.loadClass(className, true);
237: if (c == null)
238: throw cnfe;
239: return c;
240: }
241: } catch (SecurityException se) {
242: // Thrown if the class has been comprimised in some
243: // way, e.g. modified in a signed jar.
244: loadError = se;
245: } catch (LinkageError le) {
246: // some error linking the jar, again could
247: // be malicious code inserted into a jar.
248: loadError = le;
249: }
250: throw new ClassNotFoundException(className + " : "
251: + loadError.getMessage());
252: }
253:
254: abstract Class loadClassNotInDatabaseJar(String className)
255: throws ClassNotFoundException;
256:
257: public final Class loadApplicationClass(
258: ObjectStreamClass classDescriptor)
259: throws ClassNotFoundException {
260: return loadApplicationClass(classDescriptor.getName());
261: }
262:
263: public boolean isApplicationClass(Class theClass) {
264:
265: return theClass.getClassLoader() instanceof JarLoader;
266: }
267:
268: public void notifyModifyJar(boolean reload)
269: throws StandardException {
270: if (applicationLoader != null) {
271: applicationLoader.modifyJar(reload);
272: }
273: }
274:
275: /**
276: Notify the class manager that the classpath has been modified.
277:
278: @exception StandardException thrown on error
279: */
280: public void notifyModifyClasspath(String classpath)
281: throws StandardException {
282:
283: if (applicationLoader != null) {
284: applicationLoader.modifyClasspath(classpath);
285: }
286: }
287:
288: public int getClassLoaderVersion() {
289: if (applicationLoader != null) {
290: return applicationLoader.getClassLoaderVersion();
291: }
292:
293: return -1;
294: }
295:
296: public ByteArray buildSpecificFactory(String className,
297: String factoryName) throws StandardException {
298:
299: ClassBuilder cb = javaFactory
300: .newClassBuilder(this ,
301: CodeGeneration.GENERATED_PACKAGE_PREFIX,
302: Modifier.PUBLIC | Modifier.FINAL, factoryName,
303: "org.apache.derby.impl.services.reflect.GCInstanceFactory");
304:
305: MethodBuilder constructor = cb
306: .newConstructorBuilder(Modifier.PUBLIC);
307:
308: constructor.callSuper();
309: constructor.methodReturn();
310: constructor.complete();
311: constructor = null;
312:
313: MethodBuilder noArg = cb.newMethodBuilder(Modifier.PUBLIC,
314: ClassName.GeneratedByteCode, "getNewInstance");
315: noArg.pushNewStart(className);
316: noArg.pushNewComplete(0);
317: noArg.methodReturn();
318: noArg.complete();
319: noArg = null;
320:
321: return cb.getClassBytecode();
322: }
323:
324: /*
325: ** Class specific methods
326: */
327:
328: /*
329: ** Keep track of loaded generated classes and their GeneratedClass objects.
330: */
331:
332: abstract LoadedGeneratedClass loadGeneratedClassFromData(
333: String fullyQualifiedName, ByteArray classDump);
334: }
|