KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > persist > impl > PersistCatalog


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: PersistCatalog.java,v 1.33 2006/12/04 18:47:43 cwl Exp $
7  */

8
9 package com.sleepycat.persist.impl;
10
11 import java.io.ByteArrayInputStream JavaDoc;
12 import java.io.ByteArrayOutputStream JavaDoc;
13 import java.io.IOException JavaDoc;
14 import java.io.ObjectInputStream JavaDoc;
15 import java.io.ObjectOutputStream JavaDoc;
16 import java.io.Serializable JavaDoc;
17 import java.util.ArrayList JavaDoc;
18 import java.util.Collection JavaDoc;
19 import java.util.HashMap JavaDoc;
20 import java.util.HashSet JavaDoc;
21 import java.util.IdentityHashMap JavaDoc;
22 import java.util.List JavaDoc;
23 import java.util.Map JavaDoc;
24 import java.util.NoSuchElementException JavaDoc;
25 import java.util.Set JavaDoc;
26
27 import com.sleepycat.bind.tuple.IntegerBinding;
28 import com.sleepycat.je.Database;
29 import com.sleepycat.je.DatabaseConfig;
30 import com.sleepycat.je.DatabaseEntry;
31 import com.sleepycat.je.DatabaseException;
32 import com.sleepycat.je.Environment;
33 import com.sleepycat.je.OperationStatus;
34 import com.sleepycat.je.Transaction;
35 import com.sleepycat.persist.evolve.DeletedClassException;
36 import com.sleepycat.persist.evolve.IncompatibleClassException;
37 import com.sleepycat.persist.evolve.Mutations;
38 import com.sleepycat.persist.evolve.Renamer;
39 import com.sleepycat.persist.model.AnnotationModel;
40 import com.sleepycat.persist.model.ClassMetadata;
41 import com.sleepycat.persist.model.EntityMetadata;
42 import com.sleepycat.persist.model.EntityModel;
43 import com.sleepycat.persist.raw.RawObject;
44 import com.sleepycat.util.RuntimeExceptionWrapper;
45
46 /**
47  * The catalog of class formats for a store, along with its associated model
48  * and mutations.
49  *
50  * @author Mark Hayes
51  */

52 public class PersistCatalog implements Catalog {
53
54     /**
55      * Key to Data record in the catalog database. In the JE 3.0.12 beta
56      * version the formatList record is stored under this key and is converted
57      * to a Data object when it is read.
58      */

59     private static final byte[] DATA_KEY = getIntBytes(-1);
60
61     /**
62      * Key to a JE 3.0.12 beta version mutations record in the catalog
63      * database. This record is no longer used because mutations are stored in
64      * the Data record and is deleted when the beta version is detected.
65      */

66     private static final byte[] BETA_MUTATIONS_KEY = getIntBytes(-2);
67
68     private static byte[] getIntBytes(int val) {
69         DatabaseEntry entry = new DatabaseEntry();
70         IntegerBinding.intToEntry(val, entry);
71         assert entry.getSize() == 4 && entry.getData().length == 4;
72         return entry.getData();
73     }
74
75     /**
76      * Set by unit tests.
77      */

78     public static boolean expectNoClassChanges;
79
80     /**
81      * The object stored under DATA_KEY in the catalog database.
82      */

83     private static class Data implements Serializable JavaDoc {
84         List JavaDoc<Format> formatList;
85         Mutations mutations;
86         transient boolean betaVersion;
87     }
88
89     /**
90      * A list of all formats indexed by formatId. Element zero is unused and
91      * null, since IDs start at one; this avoids adjusting the ID to index the
92      * list. Some elements are null to account for predefined IDs that are not
93      * used.
94      *
95      * <p>This field, like formatMap, is volatile because it is reassigned
96      * when dynamically adding new formats. See {@link getFormat(Class)}.</p>
97      */

98     private volatile List JavaDoc<Format> formatList;
99
100     /**
101      * A map of the current/live formats in formatList, indexed by class name.
102      *
103      * <p>This field, like formatList, is volatile because it is reassigned
104      * when dynamically adding new formats. See {@link getFormat(Class)}.</p>
105      */

106     private volatile Map JavaDoc<String JavaDoc,Format> formatMap;
107
108     /**
109      * A map of the latest formats (includes deleted formats) in formatList,
110      * indexed by class name.
111      *
112      * <p>This field, like formatMap, is volatile because it is reassigned
113      * when dynamically adding new formats. See {@link getFormat(Class)}.</p>
114      */

115     private volatile Map JavaDoc<String JavaDoc,Format> latestFormatMap;
116
117     /**
118      * A temporary map of proxied class name to proxy class name. Used during
119      * catalog creation, and then set to null. This map is used to force proxy
120      * formats to be created prior to proxied formats. [#14665]
121      */

122     private Map JavaDoc<String JavaDoc,String JavaDoc> proxyClassMap;
123
124     private boolean rawAccess;
125     private EntityModel model;
126     private Mutations mutations;
127     private Database db;
128     private int openCount;
129
130     /**
131      * The Store is normally present but may be null in unit tests (for
132      * example, BindingTest).
133      */

134     private Store store;
135
136     /**
137      * Creates a new catalog, opening the database and reading it from a given
138      * catalog database if it already exists. All predefined formats and
139      * formats for the given model are added. For modified classes, old
140      * formats are defined based on the rules for compatible class changes and
141      * the given mutations. If any format is changed or added, and the
142      * database is not read-only, write the initialized catalog to the
143      * database.
144      */

145     public PersistCatalog(Transaction txn,
146                           Environment env,
147                           String JavaDoc storePrefix,
148                           String JavaDoc dbName,
149                           DatabaseConfig dbConfig,
150                           EntityModel modelParam,
151                           Mutations mutationsParam,
152                           boolean rawAccess,
153                           Store store)
154         throws DatabaseException {
155
156         this.rawAccess = rawAccess;
157         this.store = store;
158         db = env.openDatabase(txn, dbName, dbConfig);
159         openCount = 1;
160         boolean success = false;
161         try {
162             Data catalogData = readData(txn);
163             mutations = catalogData.mutations;
164             if (mutations == null) {
165                 mutations = new Mutations();
166             }
167
168             /*
169              * When the beta version is detected, force a re-write of the
170              * catalog and disallow class changes. This brings the catalog up
171              * to date so that evolution can proceed correctly from then on.
172              */

173             boolean forceWriteData = catalogData.betaVersion;
174             boolean disallowClassChanges = catalogData.betaVersion;
175
176             /*
177              * Store the given mutations if they are different from the stored
178              * mutations, and force evolution to apply the new mutations.
179              */

180             boolean forceEvolution = false;
181             if (mutationsParam != null &&
182                 !mutations.equals(mutationsParam)) {
183                 mutations = mutationsParam;
184                 forceWriteData = true;
185                 forceEvolution = true;
186             }
187
188             /* Get the existing format list, or copy it from SimpleCatalog. */
189             formatList = catalogData.formatList;
190             if (formatList == null) {
191                 formatList = SimpleCatalog.copyFormatList();
192
193                 /*
194                  * Special cases: Object and Number are predefined but are not
195                  * simple types.
196                  */

197                 Format format = new NonPersistentFormat(Object JavaDoc.class);
198                 format.setId(Format.ID_OBJECT);
199                 formatList.set(Format.ID_OBJECT, format);
200                 format = new NonPersistentFormat(Number JavaDoc.class);
201                 format.setId(Format.ID_NUMBER);
202                 formatList.set(Format.ID_NUMBER, format);
203             } else {
204                 if (SimpleCatalog.copyMissingFormats(formatList)) {
205                     forceWriteData = true;
206                 }
207             }
208
209             /* Special handling for JE 3.0.12 beta formats. */
210             if (catalogData.betaVersion) {
211                 Map JavaDoc<String JavaDoc,Format> formatMap = new HashMap JavaDoc<String JavaDoc,Format>();
212                 for (Format format : formatList) {
213                     if (format != null) {
214                         formatMap.put(format.getClassName(), format);
215                     }
216                 }
217                 for (Format format : formatList) {
218                     if (format != null) {
219                         format.migrateFromBeta(formatMap);
220                     }
221                 }
222             }
223
224             /*
225              * If we should not use the current model, initialize the stored
226              * model and return.
227              */

228             formatMap = new HashMap JavaDoc<String JavaDoc,Format>(formatList.size());
229             latestFormatMap = new HashMap JavaDoc<String JavaDoc,Format>(formatList.size());
230             if (rawAccess) {
231                 for (Format format : formatList) {
232                     if (format != null) {
233                         String JavaDoc name = format.getClassName();
234                         if (format.isCurrentVersion()) {
235                             formatMap.put(name, format);
236                         }
237                         if (format == format.getLatestVersion()) {
238                             latestFormatMap.put(name, format);
239                         }
240                     }
241                 }
242                 for (Format format : formatList) {
243                     if (format != null) {
244                         format.initializeIfNeeded(this);
245                     }
246                 }
247                 model = new StoredModel(this);
248                 success = true;
249                 return;
250             }
251
252             /*
253              * We are opening a store that uses the current model. Default to
254              * the AnnotationModel if no model is specified.
255              */

256             if (modelParam != null) {
257                 model = modelParam;
258             } else {
259                 model = new AnnotationModel();
260             }
261
262             /*
263              * Add all predefined (simple) formats to the format map. The
264              * current version of other formats will be added below.
265              */

266             for (int i = 0; i <= Format.ID_PREDEFINED; i += 1) {
267                 Format simpleFormat = formatList.get(i);
268                 if (simpleFormat != null) {
269                     formatMap.put(simpleFormat.getClassName(), simpleFormat);
270                 }
271             }
272
273             /*
274              * Known classes are those explicitly registered by the user via
275              * the model, plus the predefined proxy classes.
276              */

277             List JavaDoc<String JavaDoc> knownClasses =
278                 new ArrayList JavaDoc<String JavaDoc>(model.getKnownClasses());
279             addPredefinedProxies(knownClasses);
280
281             /*
282              * Create a temporary map of proxied class name to proxy class
283              * name, using all known formats and classes. This map is used to
284              * force proxy formats to be created prior to proxied formats.
285              * [#14665]
286              */

287             proxyClassMap = new HashMap JavaDoc<String JavaDoc,String JavaDoc>();
288             for (Format oldFormat : formatList) {
289                 if (oldFormat == null || Format.isPredefined(oldFormat)) {
290                     continue;
291                 }
292                 String JavaDoc oldName = oldFormat.getClassName();
293                 Renamer renamer = mutations.getRenamer
294                     (oldName, oldFormat.getVersion(), null);
295                 String JavaDoc newName =
296                     (renamer != null) ? renamer.getNewName() : oldName;
297                 addProxiedClass(newName);
298             }
299             for (String JavaDoc className : knownClasses) {
300                 addProxiedClass(className);
301             }
302
303             /*
304              * Add known formats from the model and the predefined proxies.
305              * In general, classes will not be present in an AnnotationModel
306              * until an instance is stored, in which case an old format exists.
307              * However, registered proxy classes are an exception and must be
308              * added in advance. And the user may choose to register new
309              * classes in advance. The more formats we define in advance, the
310              * less times we have to write to the catalog database.
311              */

312             Map JavaDoc<String JavaDoc,Format> newFormats = new HashMap JavaDoc<String JavaDoc,Format>();
313             for (String JavaDoc className : knownClasses) {
314                 createFormat(className, newFormats);
315             }
316
317             /*
318              * Perform class evolution for all old formats, and throw an
319              * exception that contains the messages for all of the errors in
320              * mutations or in the definition of new classes.
321              */

322             Evolver evolver = new Evolver
323                 (this, storePrefix, mutations, newFormats, forceEvolution,
324                  disallowClassChanges);
325             for (Format oldFormat : formatList) {
326                 if (oldFormat == null || Format.isPredefined(oldFormat)) {
327                     continue;
328                 }
329                 if (oldFormat.isEntity()) {
330                     evolver.evolveFormat(oldFormat);
331                 } else {
332                     evolver.addNonEntityFormat(oldFormat);
333                 }
334             }
335             evolver.finishEvolution();
336             String JavaDoc errors = evolver.getErrors();
337             if (errors != null) {
338                 throw new IncompatibleClassException(errors);
339             }
340
341             /*
342              * Add the new formats remaining. New formats that are equal to
343              * old formats were removed from the newFormats map above.
344              */

345             for (Format newFormat : newFormats.values()) {
346                 addFormat(newFormat);
347             }
348
349             /* Initialize all formats. */
350             for (Format format : formatList) {
351                 if (format != null) {
352                     format.initializeIfNeeded(this);
353                     if (format == format.getLatestVersion()) {
354                         latestFormatMap.put(format.getClassName(), format);
355                     }
356                 }
357             }
358
359             boolean needWrite =
360                  newFormats.size() > 0 ||
361                  evolver.areFormatsChanged();
362
363             /* For unit testing. */
364             if (expectNoClassChanges && needWrite) {
365                 throw new IllegalStateException JavaDoc
366                     ("Unexpected changes " +
367                      " newFormats.size=" + newFormats.size() +
368                      " areFormatsChanged=" + evolver.areFormatsChanged());
369             }
370
371             /* Write the catalog if anything changed. */
372             if ((needWrite || forceWriteData) &&
373                 !db.getConfig().getReadOnly()) {
374
375                 /*
376                  * Only rename/remove databases if we are going to update the
377                  * catalog to reflect those class changes.
378                  */

379                 evolver.renameAndRemoveDatabases(env, txn);
380
381                 /*
382                  * Note that we use the Data object that was read above, and
383                  * the Data.betaVersion field determines whether to delete the
384                  * old mutations record.
385                  */

386                 catalogData.formatList = formatList;
387                 catalogData.mutations = mutations;
388                 writeData(txn, catalogData);
389             } else if (forceWriteData) {
390                 throw new IllegalArgumentException JavaDoc
391                     ("When an upgrade is required the store may not be " +
392                      "opened read-only");
393             }
394
395             /* proxyClassMap was only needed for the duration of this ctor. */
396             proxyClassMap = null;
397
398             success = true;
399         } finally {
400             if (!success) {
401                 close();
402             }
403         }
404     }
405
406     public void getEntityFormats(Collection JavaDoc<Format> entityFormats) {
407         for (Format format : formatMap.values()) {
408             if (format.isEntity()) {
409                 entityFormats.add(format);
410             }
411         }
412     }
413
414     private void addProxiedClass(String JavaDoc className) {
415         ClassMetadata metadata = model.getClassMetadata(className);
416         if (metadata != null) {
417             String JavaDoc proxiedClassName = metadata.getProxiedClassName();
418             if (proxiedClassName != null) {
419                 proxyClassMap.put(proxiedClassName, className);
420             }
421         }
422     }
423
424     private void addPredefinedProxies(List JavaDoc<String JavaDoc> knownClasses) {
425         knownClasses.add(CollectionProxy.ArrayListProxy.class.getName());
426         knownClasses.add(CollectionProxy.LinkedListProxy.class.getName());
427         knownClasses.add(CollectionProxy.HashSetProxy.class.getName());
428         knownClasses.add(CollectionProxy.TreeSetProxy.class.getName());
429         knownClasses.add(MapProxy.HashMapProxy.class.getName());
430         knownClasses.add(MapProxy.TreeMapProxy.class.getName());
431     }
432
433     /**
434      * Returns a map from format to a set of its superclass formats. The
435      * format for simple types, enums and class Object are not included. Only
436      * complex types have superclass formats as defined by
437      * Format.getSuperFormat.
438      */

439     Map JavaDoc<Format,Set JavaDoc<Format>> getSubclassMap() {
440         Map JavaDoc<Format,Set JavaDoc<Format>> subclassMap =
441             new HashMap JavaDoc<Format,Set JavaDoc<Format>>();
442         for (Format format : formatList) {
443             if (format == null || Format.isPredefined(format)) {
444                 continue;
445             }
446             Format superFormat = format.getSuperFormat();
447             if (superFormat != null) {
448                 Set JavaDoc<Format> subclass = subclassMap.get(superFormat);
449                 if (subclass == null) {
450                     subclass = new HashSet JavaDoc<Format>();
451                     subclassMap.put(superFormat, subclass);
452                 }
453                 subclass.add(format);
454             }
455         }
456         return subclassMap;
457     }
458
459     /**
460      * Returns the model parameter, default model or stored model.
461      */

462     public EntityModel getResolvedModel() {
463         return model;
464     }
465
466     /**
467      * Increments the reference count for a catalog that is already open.
468      */

469     public void openExisting() {
470         openCount += 1;
471     }
472
473     /**
474      * Decrements the reference count and closes the catalog DB when it reaches
475      * zero. Returns true if the database was closed or false if the reference
476      * count is still non-zero and the database was left open.
477      */

478     public boolean close()
479         throws DatabaseException {
480
481         if (openCount == 0) {
482             throw new IllegalStateException JavaDoc("Catalog is not open");
483         } else {
484             openCount -= 1;
485             if (openCount == 0) {
486                 Database dbToClose = db;
487                 db = null;
488                 dbToClose.close();
489                 return true;
490             } else {
491                 return false;
492             }
493         }
494     }
495
496     /**
497      * Returns the current merged mutations.
498      */

499     public Mutations getMutations() {
500         return mutations;
501     }
502
503     /**
504      * Convenience method that gets the class for the given class name and
505      * calls createFormat with the class object.
506      */

507     public Format createFormat(String JavaDoc clsName, Map JavaDoc<String JavaDoc,Format> newFormats) {
508         Class JavaDoc type;
509         try {
510             type = SimpleCatalog.classForName(clsName);
511         } catch (ClassNotFoundException JavaDoc e) {
512             throw new IllegalStateException JavaDoc
513                 ("Class does not exist: " + clsName);
514         }
515         return createFormat(type, newFormats);
516     }
517
518     /**
519      * If the given class format is not already present in the given map,
520      * creates an uninitialized format, adds it to the map, and also collects
521      * related formats in the map.
522      */

523     public Format createFormat(Class JavaDoc type, Map JavaDoc<String JavaDoc,Format> newFormats) {
524         /* Return a new or existing format for this class. */
525         String JavaDoc className = type.getName();
526         Format format = newFormats.get(className);
527         if (format != null) {
528             return format;
529         }
530         format = formatMap.get(className);
531         if (format != null) {
532             return format;
533         }
534         /* Simple types are predefined. */
535         assert !SimpleCatalog.isSimpleType(type) : className;
536         /* Create format of the appropriate type. */
537         String JavaDoc proxyClassName = null;
538         if (proxyClassMap != null) {
539             proxyClassName = proxyClassMap.get(className);
540         }
541         if (proxyClassName != null) {
542             format = new ProxiedFormat(type, proxyClassName);
543         } else if (type.isArray()) {
544             format = type.getComponentType().isPrimitive() ?
545                 (new PrimitiveArrayFormat(type)) :
546                 (new ObjectArrayFormat(type));
547         } else if (type.isEnum()) {
548             format = new EnumFormat(type);
549         } else if (type == Object JavaDoc.class || type.isInterface()) {
550             format = new NonPersistentFormat(type);
551         } else {
552             ClassMetadata metadata = model.getClassMetadata(className);
553             if (metadata == null) {
554                 throw new IllegalArgumentException JavaDoc
555                     ("Class is not persistent: " + className);
556             }
557             if (metadata.getCompositeKeyFields() != null &&
558                 (metadata.getPrimaryKey() != null ||
559                  metadata.getSecondaryKeys() != null)) {
560                 throw new IllegalArgumentException JavaDoc
561                     ("A composite key class may not have primary or" +
562                      " secondary key fields: " + type.getName());
563             }
564             try {
565                 type.getDeclaredConstructor();
566             } catch (NoSuchMethodException JavaDoc e) {
567                 throw new IllegalArgumentException JavaDoc
568                     ("No default constructor: " + type.getName(), e);
569             }
570             if (metadata.getCompositeKeyFields() != null) {
571                 format = new CompositeKeyFormat
572                     (type, metadata, metadata.getCompositeKeyFields());
573             } else {
574                 EntityMetadata entityMetadata =
575                     model.getEntityMetadata(className);
576                 format = new ComplexFormat(type, metadata, entityMetadata);
577             }
578         }
579         /* Collect new format along with any related new formats. */
580         newFormats.put(className, format);
581         format.collectRelatedFormats(this, newFormats);
582
583         return format;
584     }
585
586     /**
587      * Adds a format and makes it the current format for the class.
588      */

589     private void addFormat(Format format) {
590         addFormat(format, formatList, formatMap);
591     }
592
593     /**
594      * Adds a format to the given the format collections, for use when
595      * dynamically adding formats.
596      */

597     private void addFormat(Format format,
598                            List JavaDoc<Format> list,
599                            Map JavaDoc<String JavaDoc,Format> map) {
600         format.setId(list.size());
601         list.add(format);
602         map.put(format.getClassName(), format);
603     }
604     
605     /**
606      * Installs an existing format when no evolution is needed, i.e, when the
607      * new and old formats are identical.
608      */

609     void useExistingFormat(Format oldFormat) {
610         assert oldFormat.isCurrentVersion();
611         formatMap.put(oldFormat.getClassName(), oldFormat);
612     }
613
614     /**
615      * Returns a set of all persistent (non-simple type) class names.
616      */

617     Set JavaDoc<String JavaDoc> getModelClasses() {
618         Set JavaDoc<String JavaDoc> classes = new HashSet JavaDoc<String JavaDoc>();
619         for (Format format : formatMap.values()) {
620             if (format.isModelClass()) {
621                 classes.add(format.getClassName());
622             }
623         }
624         return classes;
625     }
626
627     public Format getFormat(int formatId) {
628         try {
629             Format format = formatList.get(formatId);
630             if (format == null) {
631                 throw new DeletedClassException
632                     ("Format does not exist: " + formatId);
633             }
634             return format;
635         } catch (NoSuchElementException JavaDoc e) {
636             throw new DeletedClassException
637                 ("Format does not exist: " + formatId);
638         }
639     }
640
641
642     /**
643      * Get a format for a given class, creating it if it does not exist.
644      *
645      * <p>This method is called for top level entity instances by
646      * PersistEntityBinding. When a new entity subclass format is added we
647      * call Store.openSecondaryIndexes so that previously unknown secondary
648      * databases can be created, before storing the entity. We do this here
649      * while not holding a synchronization mutex, not in addNewFormat, to avoid
650      * deadlocks. openSecondaryIndexes synchronizes on the Store. [#15247]</p>
651      */

652     public Format getFormat(Class JavaDoc cls) {
653         Format format = formatMap.get(cls.getName());
654         if (format == null) {
655             if (model != null) {
656                 format = addNewFormat(cls);
657                 /* Detect and handle new entity subclass. [#15247] */
658                 if (store != null) {
659                     Format entityFormat = format.getEntityFormat();
660                     if (entityFormat != null && entityFormat != format) {
661                         try {
662                             store.openSecondaryIndexes
663                                 (entityFormat.getEntityMetadata());
664                         } catch (DatabaseException e) {
665                             throw new RuntimeExceptionWrapper(e);
666                         }
667                     }
668                 }
669             }
670             if (format == null) {
671                 throw new IllegalArgumentException JavaDoc
672                     ("Class is not persistent: " + cls.getName());
673             }
674         }
675         return format;
676     }
677
678     public Format getFormat(String JavaDoc className) {
679         return formatMap.get(className);
680     }
681
682     public Format getLatestVersion(String JavaDoc className) {
683         return latestFormatMap.get(className);
684     }
685
686     /**
687      * Adds a format for a new class. Returns the format added for the given
688      * class, or throws an exception if the given class is not persistent.
689      *
690      * <p>This method uses a copy-on-write technique to add new formats without
691      * impacting other threads.</p>
692      */

693     private synchronized Format addNewFormat(Class JavaDoc cls) {
694
695         /*
696          * After synchronizing, check whether another thread has added the
697          * format needed. Note that this is not the double-check technique
698          * because the formatMap field is volatile and is not itself checked
699          * for null. (The double-check technique is known to be flawed in
700          * Java.)
701          */

702         Format format = formatMap.get(cls.getName());
703         if (format != null) {
704             return format;
705         }
706
707         /* Copy the read-only format collections. */
708         List JavaDoc<Format> newFormatList = new ArrayList JavaDoc<Format>(formatList);
709         Map JavaDoc<String JavaDoc,Format> newFormatMap =
710             new HashMap JavaDoc<String JavaDoc,Format>(formatMap);
711         Map JavaDoc<String JavaDoc,Format> newLatestFormatMap =
712             new HashMap JavaDoc<String JavaDoc,Format>(latestFormatMap);
713
714         /* Add the new format and all related new formats. */
715         Map JavaDoc<String JavaDoc,Format> newFormats = new HashMap JavaDoc<String JavaDoc,Format>();
716         format = createFormat(cls, newFormats);
717         for (Format newFormat : newFormats.values()) {
718             addFormat(newFormat, newFormatList, newFormatMap);
719         }
720
721         /*
722          * Initialize new formats using a read-only catalog because we can't
723          * update this catalog until after we store it (below).
724          */

725         Catalog newFormatCatalog =
726             new ReadOnlyCatalog(newFormatList, newFormatMap);
727         for (Format newFormat : newFormats.values()) {
728             newFormat.initializeIfNeeded(newFormatCatalog);
729             newLatestFormatMap.put(newFormat.getClassName(), newFormat);
730         }
731
732         /*
733          * Write the updated catalog using auto-commit, then assign the new
734          * collections. The database write must occur before the collections
735          * are used, since a format must be persistent before it can be
736          * referenced by a data record.
737          */

738         try {
739             Data catalogData = new Data();
740             catalogData.formatList = newFormatList;
741             catalogData.mutations = mutations;
742             writeData(null, catalogData);
743         } catch (DatabaseException e) {
744             throw new RuntimeExceptionWrapper(e);
745         }
746         formatList = newFormatList;
747         formatMap = newFormatMap;
748         latestFormatMap = newLatestFormatMap;
749
750         return format;
751     }
752
753     /**
754      * Used to write the catalog when a format has been changed, for example,
755      * when Store.evolve has updated a Format's EvolveNeeded property. Uses
756      * auto-commit.
757      */

758     public synchronized void flush()
759         throws DatabaseException {
760
761         Data catalogData = new Data();
762         catalogData.formatList = formatList;
763         catalogData.mutations = mutations;
764         writeData(null, catalogData);
765     }
766
767     /**
768      * Reads catalog Data, converting old versions as necessary. An empty
769      * Data object is returned if no catalog data currently exists. Null is
770      * never returned.
771      */

772     private Data readData(Transaction txn)
773         throws DatabaseException {
774         
775         Data catalogData;
776         DatabaseEntry key = new DatabaseEntry(DATA_KEY);
777         DatabaseEntry data = new DatabaseEntry();
778         OperationStatus status = db.get(txn, key, data, null);
779         if (status == OperationStatus.SUCCESS) {
780             ByteArrayInputStream JavaDoc bais = new ByteArrayInputStream JavaDoc
781                 (data.getData(), data.getOffset(), data.getSize());
782             try {
783                 ObjectInputStream JavaDoc ois = new ObjectInputStream JavaDoc(bais);
784                 Object JavaDoc object = ois.readObject();
785                 assert ois.available() == 0;
786                 if (object instanceof Data) {
787                     catalogData = (Data) object;
788                 } else {
789                     if (!(object instanceof List JavaDoc)) {
790                         throw new IllegalStateException JavaDoc
791                             (object.getClass().getName());
792                     }
793                     catalogData = new Data();
794                     catalogData.formatList = (List JavaDoc) object;
795                     catalogData.betaVersion = true;
796                 }
797                 return catalogData;
798             } catch (ClassNotFoundException JavaDoc e) {
799                 throw new DatabaseException(e);
800             } catch (IOException JavaDoc e) {
801                 throw new DatabaseException(e);
802             }
803         } else {
804             catalogData = new Data();
805         }
806         return catalogData;
807     }
808
809     /**
810      * Writes catalog Data. If txn is null, auto-commit is used.
811      */

812     private void writeData(Transaction txn, Data catalogData)
813         throws DatabaseException {
814
815         ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
816         try {
817             ObjectOutputStream JavaDoc oos = new ObjectOutputStream JavaDoc(baos);
818             oos.writeObject(catalogData);
819         } catch (IOException JavaDoc e) {
820             throw new DatabaseException(e);
821         }
822         DatabaseEntry key = new DatabaseEntry(DATA_KEY);
823         DatabaseEntry data = new DatabaseEntry(baos.toByteArray());
824         db.put(txn, key, data);
825
826         /*
827          * Delete the unused beta mutations record if we read the beta version
828          * record earlier.
829          */

830         if (catalogData.betaVersion) {
831             key.setData(BETA_MUTATIONS_KEY);
832             db.delete(txn, key);
833             catalogData.betaVersion = false;
834         }
835     }
836
837     public boolean isRawAccess() {
838         return rawAccess;
839     }
840
841     public Object JavaDoc convertRawObject(RawObject o, IdentityHashMap JavaDoc converted) {
842         Format format = (Format) o.getType();
843         if (this != format.getCatalog()) {
844         String JavaDoc className = format.getClassName();
845             format = getFormat(className);
846             if (format == null) {
847                 throw new IllegalArgumentException JavaDoc
848                     ("External raw type not found: " + className);
849             }
850         }
851         Format proxiedFormat = format.getProxiedFormat();
852         if (proxiedFormat != null) {
853             format = proxiedFormat;
854         }
855         if (converted == null) {
856             converted = new IdentityHashMap JavaDoc();
857         }
858         return format.convertRawObject(this, false, o, converted);
859     }
860
861     public void dump() {
862         System.out.println("--- Begin formats ---");
863         for (Format format : formatList) {
864             if (format != null) {
865                 System.out.println
866                     ("ID: " + format.getId() +
867                      " class: " + format.getClassName() +
868                      " version: " + format.getVersion() +
869                      " current: " +
870                      (format == formatMap.get(format.getClassName())));
871             }
872         }
873         System.out.println("--- End formats ---");
874     }
875 }
876
Popular Tags