1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.services.reflect.DatabaseClasses |
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.iapi.services.sanity.SanityManager; |
24 | |
25 | import org.apache.derby.iapi.services.loader.ClassFactory; |
26 | import org.apache.derby.iapi.services.loader.GeneratedClass; |
27 | import org.apache.derby.iapi.services.loader.ClassInspector; |
28 | |
29 | import org.apache.derby.iapi.services.monitor.ModuleControl; |
30 | import org.apache.derby.iapi.services.monitor.ModuleSupportable; |
31 | import org.apache.derby.iapi.services.monitor.Monitor; |
32 | |
33 | import org.apache.derby.iapi.error.StandardException; |
34 | import org.apache.derby.iapi.services.property.PropertyUtil; |
35 | |
36 | import org.apache.derby.iapi.services.stream.HeaderPrintWriter; |
37 | import org.apache.derby.iapi.services.monitor.Monitor; |
38 | |
39 | import org.apache.derby.iapi.services.compiler.*; |
40 | import java.lang.reflect.Modifier; |
41 | import org.apache.derby.iapi.sql.compile.CodeGeneration; |
42 | |
43 | import org.apache.derby.iapi.util.ByteArray; |
44 | import org.apache.derby.iapi.services.io.FileUtil; |
45 | import org.apache.derby.iapi.services.i18n.MessageService; |
46 | import org.apache.derby.iapi.reference.Property; |
47 | import org.apache.derby.iapi.reference.SQLState; |
48 | import org.apache.derby.iapi.reference.MessageId; |
49 | import org.apache.derby.iapi.reference.ClassName; |
50 | |
51 | import java.util.Properties; |
52 | import java.util.Hashtable; |
53 | |
54 | import java.io.ObjectStreamClass; |
55 | import java.io.File; |
56 | import java.io.FileOutputStream; |
57 | import java.io.IOException; |
58 | import java.io.Serializable; |
59 | |
60 | /** |
61 | |
62 | An abstract implementation of the ClassFactory. This package can |
63 | be extended to fully implement a ClassFactory. Implementations can |
64 | differ in two areas, how they load a class and how they invoke methods |
65 | of the generated class. |
66 | |
67 | <P> |
68 | This class manages a hash table of loaded generated classes and |
69 | their GeneratedClass objects. A loaded class may be referenced |
70 | multiple times -- each class has a reference count associated |
71 | with it. When a load request arrives, if the class has already |
72 | been loaded, its ref count is incremented. For a remove request, |
73 | the ref count is decremented unless it is the last reference, |
74 | in which case the class is removed. This is transparent to users. |
75 | |
76 | @see org.apache.derby.iapi.services.loader.ClassFactory |
77 | */ |
78 | |
79 | abstract class DatabaseClasses |
80 | implements ClassFactory, ModuleControl |
81 | { |
82 | /* |
83 | ** Fields |
84 | */ |
85 | |
86 | private ClassInspector classInspector; |
87 | private JavaFactory javaFactory; |
88 | |
89 | private UpdateLoader applicationLoader; |
90 | |
91 | /* |
92 | ** Constructor |
93 | */ |
94 | |
95 | DatabaseClasses() { |
96 | } |
97 | |
98 | /* |
99 | ** Public methods of ModuleControl |
100 | */ |
101 | |
102 | public void boot(boolean create, Properties startParams) |
103 | throws StandardException |
104 | { |
105 | |
106 | classInspector = new ClassInspector(this); |
107 | |
108 | // |
109 | //The ClassFactory runs per service (database) mode (booted as a service module after AccessFactory). |
110 | //If the code that booted |
111 | //us needs a per-database classpath then they pass in the classpath using |
112 | //the runtime property BOOT_DB_CLASSPATH in startParams |
113 | |
114 | |
115 | String classpath = null; |
116 | if (startParams != null) { |
117 | classpath = startParams.getProperty(Property.BOOT_DB_CLASSPATH); |
118 | } |
119 | |
120 | if (classpath != null) { |
121 | applicationLoader = new UpdateLoader(classpath, this, true, |
122 | true); |
123 | } |
124 | |
125 | javaFactory = (JavaFactory) org.apache.derby.iapi.services.monitor.Monitor.startSystemModule(org.apache.derby.iapi.reference.Module.JavaFactory); |
126 | } |
127 | |
128 | |
129 | |
130 | public void stop() { |
131 | if (applicationLoader != null) |
132 | applicationLoader.close(); |
133 | } |
134 | |
135 | /* |
136 | ** Public methods of ClassFactory |
137 | */ |
138 | |
139 | /** |
140 | Here we load the newly added class now, rather than waiting for the |
141 | findGeneratedClass(). Thus we are assuming that the class is going |
142 | to be used sometime soon. Delaying the load would mean storing the class |
143 | data in a file, this wastes cycles and compilcates the cleanup. |
144 | |
145 | @see ClassFactory#loadGeneratedClass |
146 | |
147 | @exception StandardException Class format is bad. |
148 | */ |
149 | public final GeneratedClass loadGeneratedClass(String fullyQualifiedName, ByteArray classDump) |
150 | throws StandardException { |
151 | |
152 | |
153 | try { |
154 | |
155 | |
156 | return loadGeneratedClassFromData(fullyQualifiedName, classDump); |
157 | |
158 | } catch (LinkageError le) { |
159 | |
160 | WriteClassFile(fullyQualifiedName, classDump, le); |
161 | |
162 | throw StandardException.newException(SQLState.GENERATED_CLASS_LINKAGE_ERROR, |
163 | le, fullyQualifiedName); |
164 | |
165 | } catch (VirtualMachineError vme) { // these may be beyond saving, but fwiw |
166 | |
167 | WriteClassFile(fullyQualifiedName, classDump, vme); |
168 | |
169 | throw vme; |
170 | } |
171 | |
172 | } |
173 | |
174 | private static void WriteClassFile(String fullyQualifiedName, ByteArray bytecode, Throwable t) { |
175 | |
176 | // get the un-qualified name and add the extension |
177 | int lastDot = fullyQualifiedName.lastIndexOf((int)'.'); |
178 | String filename = fullyQualifiedName.substring(lastDot+1,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(), |
191 | bytecode.getOffset(), bytecode.getLength()); |
192 | fis.flush(); |
193 | if (t!=null) { |
194 | errorStream.printlnWithHeader(MessageService.getTextMessage(MessageId.CM_WROTE_CLASS_FILE, fullyQualifiedName, classFile, t)); |
195 | } |
196 | fis.close(); |
197 | } catch (IOException e) { |
198 | if (SanityManager.DEBUG) |
199 | SanityManager.THROWASSERT("Unable to write .class file"); |
200 | } |
201 | } |
202 | |
203 | public ClassInspector getClassInspector() { |
204 | return classInspector; |
205 | } |
206 | |
207 | |
208 | public final Class loadApplicationClass(String className) |
209 | throws ClassNotFoundException { |
210 | |
211 | if (className.startsWith("org.apache.derby.")) { |
212 | // Assume this is an engine class, if so |
213 | // try to load from this class loader, |
214 | // this ensures in strange class loader |
215 | // environments we do not get ClassCastExceptions |
216 | // when an engine class is loaded through a different |
217 | // class loader to the rest of the engine. |
218 | try { |
219 | return Class.forName(className); |
220 | } catch (ClassNotFoundException cnfe) |
221 | { |
222 | // fall through to the code below, |
223 | // could be client or tools class |
224 | // in a different loader. |
225 | } |
226 | } |
227 | |
228 | Throwable loadError; |
229 | try { |
230 | try { |
231 | return loadClassNotInDatabaseJar(className); |
232 | } catch (ClassNotFoundException cnfe) { |
233 | if (applicationLoader == null) |
234 | throw cnfe; |
235 | Class c = applicationLoader.loadClass(className, true); |
236 | if (c == null) |
237 | throw cnfe; |
238 | return c; |
239 | } |
240 | } |
241 | catch (SecurityException se) |
242 | { |
243 | // Thrown if the class has been comprimised in some |
244 | // way, e.g. modified in a signed jar. |
245 | loadError = se; |
246 | } |
247 | catch (LinkageError le) |
248 | { |
249 | // some error linking the jar, again could |
250 | // be malicious code inserted into a jar. |
251 | loadError = le; |
252 | } |
253 | throw new ClassNotFoundException(className + " : " + loadError.getMessage()); |
254 | } |
255 | |
256 | abstract Class loadClassNotInDatabaseJar(String className) |
257 | throws ClassNotFoundException; |
258 | |
259 | public final Class loadApplicationClass(ObjectStreamClass classDescriptor) |
260 | throws ClassNotFoundException { |
261 | return loadApplicationClass(classDescriptor.getName()); |
262 | } |
263 | |
264 | public boolean isApplicationClass(Class theClass) { |
265 | |
266 | return theClass.getClassLoader() |
267 | instanceof JarLoader; |
268 | } |
269 | |
270 | public void notifyModifyJar(boolean reload) throws StandardException { |
271 | if (applicationLoader != null) { |
272 | applicationLoader.modifyJar(reload); |
273 | } |
274 | } |
275 | |
276 | /** |
277 | Notify the class manager that the classpath has been modified. |
278 | |
279 | @exception StandardException thrown on error |
280 | */ |
281 | public void notifyModifyClasspath(String classpath) throws StandardException { |
282 | |
283 | if (applicationLoader != null) { |
284 | applicationLoader.modifyClasspath(classpath); |
285 | } |
286 | } |
287 | |
288 | |
289 | public int getClassLoaderVersion() { |
290 | if (applicationLoader != null) { |
291 | return applicationLoader.getClassLoaderVersion(); |
292 | } |
293 | |
294 | return -1; |
295 | } |
296 | |
297 | public ByteArray buildSpecificFactory(String className, String factoryName) |
298 | throws StandardException { |
299 | |
300 | ClassBuilder cb = javaFactory.newClassBuilder(this, CodeGeneration.GENERATED_PACKAGE_PREFIX, |
301 | Modifier.PUBLIC | Modifier.FINAL, factoryName, "org.apache.derby.impl.services.reflect.GCInstanceFactory"); |
302 | |
303 | MethodBuilder constructor = cb.newConstructorBuilder(Modifier.PUBLIC); |
304 | |
305 | constructor.callSuper(); |
306 | constructor.methodReturn(); |
307 | constructor.complete(); |
308 | constructor = null; |
309 | |
310 | MethodBuilder noArg = cb.newMethodBuilder(Modifier.PUBLIC, ClassName.GeneratedByteCode, "getNewInstance"); |
311 | noArg.pushNewStart(className); |
312 | noArg.pushNewComplete(0); |
313 | noArg.methodReturn(); |
314 | noArg.complete(); |
315 | noArg = null; |
316 | |
317 | return cb.getClassBytecode(); |
318 | } |
319 | |
320 | /* |
321 | ** Class specific methods |
322 | */ |
323 | |
324 | /* |
325 | ** Keep track of loaded generated classes and their GeneratedClass objects. |
326 | */ |
327 | |
328 | abstract LoadedGeneratedClass loadGeneratedClassFromData(String fullyQualifiedName, ByteArray classDump); |
329 | } |