KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > versant > core > storagemanager > StorageManagerFactoryBuilder


1
2 /*
3  * Copyright (c) 1998 - 2005 Versant Corporation
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  * Versant Corporation - initial API and implementation
11  */

12 package com.versant.core.storagemanager;
13
14 import com.versant.core.common.config.ConfigInfo;
15 import com.versant.core.common.config.ConfigParser;
16 import com.versant.core.metadata.parser.MetaDataParser;
17 import com.versant.core.metadata.ModelMetaData;
18 import com.versant.core.common.BindingSupportImpl;
19 import com.versant.core.common.Utils;
20 import com.versant.core.util.BeanUtils;
21 import com.versant.core.util.PropertiesLoader;
22 import com.versant.core.util.classhelper.ClassHelper;
23 import com.versant.core.logging.LogEventStore;
24 import com.versant.core.storagemanager.logging.LoggingStorageManagerFactory;
25
26 import com.versant.core.compiler.ClassCompiler;
27 import com.versant.core.compiler.PizzaClassCompiler;
28 import com.versant.core.compiler.ClassSpec;
29
30 import com.versant.core.server.CompiledQueryCache;
31
32 import java.util.*;
33 import java.util.zip.Deflater JavaDoc;
34 import java.lang.reflect.Method JavaDoc;
35 import java.lang.reflect.InvocationTargetException JavaDoc;
36 import java.lang.reflect.Constructor JavaDoc;
37 import java.io.*;
38
39 /**
40  * Bean to create StorageManagerFactory's. This needs some more work to avoid
41  * referencing the SMF implementation classes directly as it does now.
42  */

43 public class StorageManagerFactoryBuilder {
44
45     private ConfigInfo config;
46     private ClassLoader JavaDoc loader;
47     private LogEventStore pes;
48     private StorageCache cache;
49     private boolean onlyMetaData;
50     private boolean fullInit = true;
51     private boolean continueAfterMetaDataError;
52
53     private ClassCompiler classCompiler;
54
55     private HashMap classSpecs;
56     private CompiledQueryCache compiledQueryCache;
57     private boolean keepHyperdriveBytecode;
58     private Map hyperdriveBytecode;
59     private int hyperdriveBytecodeMaxSize;
60
61     public StorageManagerFactoryBuilder() {
62     }
63
64     /**
65      * Create a SMF based on our properties.
66      */

67     public StorageManagerFactory createStorageManagerFactory() {
68
69         if (config == null) {
70             throw BindingSupportImpl.getInstance().internal(
71                     "config property not set");
72         }
73
74
75         if (loader == null) {
76             throw BindingSupportImpl.getInstance().internal(
77                     "loader property not set");
78         }
79         if (Utils.isStringEmpty(config.url)) {
80             if (Utils.isStringEmpty(config.props.getProperty(
81                     ConfigParser.OPTION_CONNECTION_FACTORY_NAME)) &&
82                     !Utils.isDataSource(config.props.getProperty(
83                             ConfigParser.STD_CON_DRIVER_NAME), loader)) {
84
85                 throw BindingSupportImpl.getInstance().internal(
86                         "javax.jdo.option.ConnectionURL property is required " +
87                         "if using a JDBC Driver");
88
89             } else if (Utils.isStringEmpty(config.db)){
90                 throw BindingSupportImpl.getInstance().internal(
91                         ConfigParser.STORE_DB +
92                         " property is required if using a JDBC DataSource");
93             }
94
95         }
96
97
98         if (cache == null) {
99             cache = new NOPStorageCache();
100         }
101         if (pes == null) {
102             pes = new LogEventStore();
103             BeanUtils.setProperties(pes, config.perfProps);
104         }
105         if (config.jdoMetaData == null) {
106             MetaDataParser p = new MetaDataParser();
107             config.jdoMetaData = p.parse(config.jdoResources, loader);
108         }
109         if (config.hyperdrive) {
110             classSpecs = new HashMap();
111         } else {
112             classSpecs = null;
113         }
114         compiledQueryCache = new CompiledQueryCache(
115                 config.compiledQueryCacheSize);
116         StorageManagerFactory smf = createSmfForURL();
117         ModelMetaData jmd = smf.getModelMetaData();
118         jmd.forceClassRegistration();
119         cache.setJDOMetaData(jmd);
120         smf = new LoggingStorageManagerFactory(smf, pes);
121
122
123         if (config.hyperdrive && !classSpecs.isEmpty()) {
124             if (config.hyperdriveSrcDir != null) {
125                 writeGeneratedSourceCode(classSpecs, config.hyperdriveSrcDir);
126             }
127             if (!onlyMetaData || config.hyperdriveClassDir != null) {
128                 compileAndInitGeneratedClasses(config.hyperdriveClassDir, jmd);
129             }
130         }
131
132
133         if (!onlyMetaData) {
134             smf.init(fullInit, loader);
135         }
136         return smf;
137     }
138
139     private StorageManagerFactory createSmfForURL() {
140         StorageManagerFactory smf;
141         Properties p;
142
143         try {
144             if (!Utils.isStringEmpty(config.db)){
145                 p = PropertiesLoader.loadPropertiesForDB(loader, "openaccess",
146                         config.db);
147             } else {
148                 p = PropertiesLoader.loadPropertiesForURL(loader, "openaccess",
149                     config.url);
150             }
151         } catch (IOException e) {
152             throw BindingSupportImpl.getInstance().invalidOperation(
153                     e.toString(), e);
154         }
155
156
157         String JavaDoc resName = p.getProperty(PropertiesLoader.RES_NAME_PROP);
158         String JavaDoc smfClassName = p.getProperty("smf");
159         if (smfClassName == null) {
160             throw BindingSupportImpl.getInstance().internal(
161                     "No 'smf' property in " + resName);
162         }
163         try {
164             Class JavaDoc cls = ClassHelper.get().classForName(smfClassName, true, loader);
165             Constructor JavaDoc cons = cls.getConstructor(
166                 new Class JavaDoc[]{StorageManagerFactoryBuilder.class});
167             smf = (StorageManagerFactory)cons.newInstance(new Object JavaDoc[]{this});
168         } catch (Throwable JavaDoc e) {
169             if (BindingSupportImpl.getInstance().isError(e) && !BindingSupportImpl.getInstance().isOutOfMemoryError(e)) {
170                 throw (Error JavaDoc)e;
171             }
172             if (BindingSupportImpl.getInstance().isOwnException(e)) {
173                 throw (RuntimeException JavaDoc)e;
174             }
175             if( e instanceof InvocationTargetException JavaDoc && ((InvocationTargetException JavaDoc)e).getTargetException() != null )
176             {
177                 Throwable JavaDoc inner = ((InvocationTargetException JavaDoc)e).getTargetException();
178                 if (BindingSupportImpl.getInstance().isOwnException(inner))
179                     throw (RuntimeException JavaDoc)inner;
180                 else
181                     throw BindingSupportImpl.getInstance().internal( inner.toString(),
182                                                                      inner );
183             }
184             else
185             {
186                 throw BindingSupportImpl.getInstance().internal(e.toString(), e);
187             }
188         }
189         return smf;
190     }
191
192     /**
193      * Compile, load and init all of the dynamically generated classes. This
194      * will only compile classes that cannot be loaded from our classloader.
195      */

196
197     private void compileAndInitGeneratedClasses(String JavaDoc hyperdriveClassDir,
198             ModelMetaData jmd) {
199         // attempt to load each class and create a single String of source
200
// code for those which cannot be loaded
201
ArrayList toInit = new ArrayList();
202         ArrayList notGenerated = new ArrayList();
203         Map toCompile = new HashMap();
204         for (Iterator i = classSpecs.entrySet().iterator(); i.hasNext(); ) {
205             Map.Entry e = (Map.Entry)i.next();
206             String JavaDoc name = (String JavaDoc)e.getKey();
207             ClassSpec spec = (ClassSpec)e.getValue();
208             try {
209                 toInit.add(Class.forName(name, false, loader));
210                 notGenerated.add(name);
211             } catch (ClassNotFoundException JavaDoc x) {
212                 toCompile.put(name, spec.toSrcCode());
213             }
214             i.remove(); // remove one at a time to reduce max mem usage
215
}
216         classSpecs = null;
217
218         // compile classes we could not load
219
if (!toCompile.isEmpty()) {
220             if (classCompiler == null) {
221                 classCompiler = new PizzaClassCompiler();
222             }
223             Map compiled = classCompiler.compile(toCompile, loader);
224             classCompiler = null;
225             if (hyperdriveClassDir != null) {
226                 writeClassFiles(compiled, hyperdriveClassDir);
227             }
228             loadCompiledClasses(compiled, toInit);
229             if (keepHyperdriveBytecode) {
230                 hyperdriveBytecode = compiled;
231             }
232         }
233
234         initHyperdriveClasses(toInit, jmd);
235
236         if (keepHyperdriveBytecode) {
237             // put in null bytecode for all classes loaded and not generated
238
if (hyperdriveBytecode == null) {
239                 hyperdriveBytecode = new HashMap();
240             }
241             for (int i = notGenerated.size() - 1; i >= 0; i--) {
242                 hyperdriveBytecode.put(notGenerated.get(i), null);
243             }
244         }
245     }
246
247
248     /**
249      * Invoke the initStatics method on each Class in toInit that has one and
250      * check the return value see if the class has been previously initialized.
251      * If it has then throw an exception.
252      */

253
254     public static void initHyperdriveClasses(List toInit, ModelMetaData jmd) {
255         for (Iterator i = toInit.iterator(); i.hasNext(); ) {
256             Class JavaDoc cls = (Class JavaDoc)i.next();
257             Object JavaDoc res = null;
258             try {
259                 Method JavaDoc m = cls.getMethod("initStatics",
260                         new Class JavaDoc[]{ModelMetaData.class});
261                 res = m.invoke(null, new Object JavaDoc[]{jmd});
262             } catch (NoSuchMethodException JavaDoc e) {
263                 // ignore
264
} catch (InvocationTargetException JavaDoc e) {
265                 Throwable JavaDoc t = e.getTargetException();
266                 throw BindingSupportImpl.getInstance().internal(t.toString(), t);
267             } catch (Exception JavaDoc x) {
268                 throw BindingSupportImpl.getInstance().internal(x.toString(), x);
269             }
270             if (res instanceof Boolean JavaDoc) {
271                 if (!((Boolean JavaDoc)res).booleanValue()) {
272                     throw BindingSupportImpl.getInstance().internal(
273                         cls.getName() + " is in use (try versant.useClassloader=true)");
274                 }
275             }
276         }
277     }
278
279
280     /**
281      * Load and init the dynamically compiled classes. This is done in
282      * alphabetical order to make sure that any class load problems are
283      * detirministic. If keepHyperdriveBytecode is true then the bytecode
284      * for each class is compressed and kept in the map, otherwise it is
285      * discarded.
286      */

287
288     private void loadCompiledClasses(Map map, Collection toInit) {
289         ArrayList names = new ArrayList(map.keySet());
290         Collections.sort(names);
291         Method JavaDoc defineClass = null;
292         try {
293             defineClass = ClassLoader JavaDoc.class.getDeclaredMethod("defineClass",
294                     new Class JavaDoc[]{String JavaDoc.class, byte[].class, Integer.TYPE,
295                                 Integer.TYPE});
296         } catch (NoSuchMethodException JavaDoc e) {
297             // not possible really
298
throw BindingSupportImpl.getInstance().internal(e.toString(), e);
299         }
300         defineClass.setAccessible(true);
301         Deflater JavaDoc def = null;
302         byte[] outbuf = null;
303         if (keepHyperdriveBytecode) {
304             def = new Deflater JavaDoc(9);
305             hyperdriveBytecodeMaxSize = 0;
306             for (Iterator i = map.entrySet().iterator(); i.hasNext(); ) {
307                 byte[] bytecode = (byte[])((Map.Entry)i.next()).getValue();
308                 if (bytecode.length > hyperdriveBytecodeMaxSize) {
309                     hyperdriveBytecodeMaxSize = bytecode.length;
310                 }
311             }
312             outbuf = new byte[hyperdriveBytecodeMaxSize + 128];
313         }
314         for (Iterator i = names.iterator(); i.hasNext(); ) {
315             Object JavaDoc className = i.next();
316             byte[] bytecode = (byte[])map.get(className);
317             try {
318                 Class JavaDoc cls = (Class JavaDoc)defineClass.invoke(loader, new Object JavaDoc[]{null,
319                         bytecode, new Integer JavaDoc(0), new Integer JavaDoc(bytecode.length)});
320                 toInit.add(cls);
321             } catch (InvocationTargetException JavaDoc e) {
322                 Throwable JavaDoc t = e.getTargetException();
323                 throw BindingSupportImpl.getInstance().internal(t.toString(), t);
324             } catch (Exception JavaDoc x) {
325                 throw BindingSupportImpl.getInstance().internal(x.toString(), x);
326             }
327             if (keepHyperdriveBytecode) {
328                 def.reset();
329                 def.setInput(bytecode);
330                 def.finish();
331                 int sz = def.deflate(outbuf);
332                 bytecode = new byte[sz];
333                 System.arraycopy(outbuf, 0, bytecode, 0, sz);
334                 map.put(className, bytecode);
335             } else {
336                 map.remove(className);
337             }
338         }
339     }
340
341
342     /**
343      * Write .class files for all classes in c to dir.
344      */

345
346     private void writeClassFiles(Map map, String JavaDoc dir) {
347         for (Iterator i = map.entrySet().iterator(); i.hasNext(); ) {
348             Map.Entry e = (Map.Entry)i.next();
349             String JavaDoc className = (String JavaDoc)e.getKey();
350             byte[] bytecode = (byte[])e.getValue();
351             File f = new File(dir, className + ".class");
352             try {
353                 FileOutputStream o = new FileOutputStream(f);
354                 o.write(bytecode, 0, bytecode.length);
355                 o.close();
356             } catch (IOException x) {
357                 throw BindingSupportImpl.getInstance().runtime(
358                         "Error writing to " + f + ": " + x, x);
359             }
360         }
361     }
362
363
364     /**
365      * Write out src for all generated classes to dir.
366      */

367
368     private void writeGeneratedSourceCode(HashMap classSpecs, String JavaDoc dir) {
369         for (Iterator i = classSpecs.entrySet().iterator(); i.hasNext(); ) {
370             Map.Entry e = (Map.Entry)i.next();
371             ClassSpec spec = (ClassSpec)e.getValue();
372             File f = new File(dir, spec.getName() + ".java");
373             try {
374                 FileWriter o = new FileWriter(f);
375                 o.write(spec.toSrcCode());
376                 o.close();
377             } catch (IOException x) {
378                 throw BindingSupportImpl.getInstance().runtime(
379                         "Error writing to " + f + ": " + x, x);
380             }
381         }
382     }
383
384
385     public ConfigInfo getConfig() {
386         return config;
387     }
388
389     public void setConfig(ConfigInfo config) {
390         this.config = config;
391     }
392
393     public ClassLoader JavaDoc getLoader() {
394         return loader;
395     }
396
397     public void setLoader(ClassLoader JavaDoc loader) {
398         this.loader = loader;
399     }
400
401     public LogEventStore getLogEventStore() {
402         return pes;
403     }
404
405     public void setLogEventStore(LogEventStore logEventStore) {
406         this.pes = logEventStore;
407     }
408
409     public StorageCache getCache() {
410         return cache;
411     }
412
413     public void setCache(StorageCache cache) {
414         this.cache = cache;
415     }
416
417     public boolean isOnlyMetaData() {
418         return onlyMetaData;
419     }
420
421     /**
422      * If this flag is true then the SMF is created just to access the
423      * complete JDO meta data. It will not attempt to connect to a datastore.
424      */

425     public void setOnlyMetaData(boolean onlyMetaData) {
426         this.onlyMetaData = onlyMetaData;
427     }
428
429     public boolean isFullInit() {
430         return fullInit;
431     }
432
433     /**
434      * If this flag is true then the SMF is completely initialized ready for
435      * use e.g. the JDBC store will populate the keygen tables. This is ignored
436      * if the onlyMetaData flag is set.
437      */

438     public void setFullInit(boolean fullInit) {
439         this.fullInit = fullInit;
440     }
441
442
443     public ClassCompiler getClassCompiler() {
444         return classCompiler;
445     }
446
447     public void setClassCompiler(ClassCompiler classCompiler) {
448         this.classCompiler = classCompiler;
449     }
450
451
452     public boolean isContinueAfterMetaDataError() {
453         return continueAfterMetaDataError;
454     }
455
456     /**
457      * If this flag is set then the store should attempt to recover from
458      * meta data errors and continue instead of throwing an exception. The
459      * errors must be added to the meta data. This is to support the
460      * Workbench and other tools.
461      */

462     public void setContinueAfterMetaDataError(boolean continueAfterMetaDataError) {
463         this.continueAfterMetaDataError = continueAfterMetaDataError;
464     }
465
466     /**
467      * StorageManagerFactory's supporting hyperdrive code generation must add
468      * dynamically generated classes to this map during construction. This
469      * maps class name -> ClassSpec.
470      */

471     public HashMap getClassSpecs() {
472         return classSpecs;
473     }
474
475     /**
476      * StorageManager's must store CompiledQueries in this cache.
477      */

478     public CompiledQueryCache getCompiledQueryCache() {
479         return compiledQueryCache;
480     }
481
482     /**
483      * If this property is true then the bytecode for generated hyperdrive
484      * classes is avilable via {@link #getHyperdriveBytecode()} after the
485      * call to {@link #createStorageManagerFactory()} .
486      */

487     public void setKeepHyperdriveBytecode(boolean keepHyperdriveBytecode) {
488         this.keepHyperdriveBytecode = keepHyperdriveBytecode;
489     }
490
491     public boolean isKeepHyperdriveBytecode() {
492         return keepHyperdriveBytecode;
493     }
494
495     /**
496      * If keepHyperdriveBytecode is true and hyperdrive classes were
497      * generated then this maps each class name to its compressed byte[]
498      * bytecode or null if the class was loaded from our classloader and not
499      * compiled at runtime. Otherwise it is null.
500      *
501      * @see #setKeepHyperdriveBytecode(boolean)
502      * @see #getHyperdriveBytecodeMaxSize
503      */

504     public Map getHyperdriveBytecode() {
505         return hyperdriveBytecode;
506     }
507
508     /**
509      * If keepHyperdriveBytecode is true and hyperdrive classes were
510      * generated then this is the length in bytes of the uncompressed
511      * bytecode for the biggest class. This is useful for sizing
512      * decompression buffers.
513      *
514      * @see #setKeepHyperdriveBytecode(boolean)
515      */

516     public int getHyperdriveBytecodeMaxSize() {
517         return hyperdriveBytecodeMaxSize;
518     }
519
520 }
521
522
Popular Tags