Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*-
    * See the file LICENSE for redistribution information.
    *
    * Copyright (c) 2002, 2013 Oracle and/or its affiliates.  All rights reserved.
    *
    */
   
   package com.sleepycat.persist.impl;
   
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  /* <!-- begin JE only --> */
  /* <!-- end JE only --> */
  /* <!-- begin JE only --> */
  /* <!-- end JE only --> */
  /* <!-- begin JE only --> */
  /* <!-- end JE only --> */
  /* <!-- begin JE only --> */
  /* <!-- end JE only --> */
Base implementation for EntityStore and RawStore. The methods here correspond directly to those in EntityStore; see EntityStore documentation for details.

Author(s):
Mark Hayes
  
  public class Store {
  
      public static final String NAME_SEPARATOR = "#";
      private static final String NAME_PREFIX = "persist" + ;
      private static final String DB_NAME_PREFIX = "com.sleepycat.persist.";
      private static final String CATALOG_DB =  + "formats";
      private static final String SEQUENCE_DB =  + "sequences";
  
      private static Map<EnvironmentMap<StringPersistCatalog>> catalogPool =
          new WeakHashMap<EnvironmentMap<StringPersistCatalog>>();
  
      /* For unit testing. */
      private static SyncHook syncHook;
      public static boolean expectFlush;
  
      private final Environment env;
      private final boolean rawAccess;
     private volatile PersistCatalog catalog;
     private EntityModel model;
     private final StoreConfig storeConfig;
     private final String storeName;
     private final String storePrefix;
     private final Map<StringInternalPrimaryIndexpriIndexMap;
     private final Map<StringInternalSecondaryIndexsecIndexMap;
     private final Map<StringDatabaseConfigpriConfigMap;
     private final Map<StringSecondaryConfigsecConfigMap;
     private final Map<StringPersistKeyBindingkeyBindingMap;
     private Database sequenceDb;
     private final Map<StringSequencesequenceMap;
     private final Map<StringSequenceConfigsequenceConfigMap;
     private final Map<StringSet<String>> inverseRelatedEntityMap;
     private final TransactionConfig autoCommitTxnConfig;
 
     public Store(Environment env,
                  String storeName,
                  StoreConfig config,
                  boolean rawAccess)
         throws StoreExistsException,
                StoreNotFoundException,
                IncompatibleClassException,
                DatabaseException {
 
         this. = env;
         this. = storeName;
         this. = rawAccess;
 
         if (env == null || storeName == null) {
             throw new NullPointerException
                 ("env and storeName parameters must not be null");
         }
 
          = (config != null) ?
             config.clone() :
             .;
 
          = new TransactionConfig();
          = new TransactionConfig();
         .setNoWait(true);
         /* <!-- begin JE only --> */
         if (!.getReplicated()) {
             final Durability envDurability = env.getConfig().getDurability();
             configForNonRepDb(envDurability);
             configForNonRepDb(envDurability);
         }
         /* <!-- end JE only --> */
 
          = config.getModel();
 
          =  + storeName + ;
          = new HashMap<StringInternalPrimaryIndex>();
          = new HashMap<StringInternalSecondaryIndex>();
          = new HashMap<StringDatabaseConfig>();
          = new HashMap<StringSecondaryConfig>();
          = new HashMap<StringPersistKeyBinding>();
          = new HashMap<StringSequence>();
          = new HashMap<StringSequenceConfig>();
          = new IdentityHashMap<DatabaseObject>();
 
         if (rawAccess) {
             /* Open a read-only catalog that uses the stored model. */
             if ( != null) {
                 throw new IllegalArgumentException
                     ("A model may not be specified when opening a RawStore");
             }
             DatabaseConfig dbConfig = new DatabaseConfig();
             /* <!-- begin JE only --> */
             DbInternal.setReplicated(dbConfig.getReplicated());
             /* <!-- end JE only --> */
             dbConfig.setReadOnly(true);
             dbConfig.setTransactional
                 (.getTransactional());
              = new PersistCatalog
                 (env + dbConfig,
                  null /*model*/config.getMutations(), rawAccessthis);
         } else {
             /* Open the shared catalog that uses the current model. */
             synchronized () {
                 Map<StringPersistCatalogcatalogMap = .get(env);
                 if (catalogMap == null) {
                     catalogMap = new HashMap<StringPersistCatalog>();
                     .put(envcatalogMap);
                 }
                  = catalogMap.get(storeName);
                 if ( != null) {
                     .openExisting();
                 } else {
                     DatabaseConfig dbConfig = new DatabaseConfig();
                     dbConfig.setAllowCreate(.getAllowCreate());
                     dbConfig.setExclusiveCreate
                         (.getExclusiveCreate());
                     /* <!-- begin JE only --> */
                     dbConfig.setTemporary(.getTemporary());
                     DbInternal.setReplicated(dbConfig,
                                              .getReplicated());
                     /* <!-- end JE only --> */
                     dbConfig.setReadOnly(.getReadOnly());
                     dbConfig.setTransactional
                         (.getTransactional());
                     DbCompat.setTypeBtree(dbConfig);
                      = new PersistCatalog
                         (env + dbConfig,
                          config.getMutations(), rawAccessthis);
                     catalogMap.put(storeName);
                 }
             }
         }
 
         /*
          * If there is no model parameter, use the default or stored model
          * obtained from the catalog.
          */
          = .getResolvedModel();
 
         /*
          * For each existing entity with a relatedEntity reference, create an
          * inverse map (back pointer) from the class named in the relatedEntity
          * to the class containing the secondary key.  This is used to open the
          * class containing the secondary key whenever we open the
          * relatedEntity class, to configure foreign key constraints. Note that
          * we do not need to update this map as new primary indexes are
          * created, because opening the new index will setup the foreign key
          * constraints. [#15358]
          */
          = new HashMap<StringSet<String>>();
         List<FormatentityFormats = new ArrayList<Format>();
         .getEntityFormats(entityFormats);
         for (Format entityFormat : entityFormats) {
             EntityMetadata entityMeta = entityFormat.getEntityMetadata();
             for (SecondaryKeyMetadata secKeyMeta :
                  entityMeta.getSecondaryKeys().values()) {
                 String relatedClsName = secKeyMeta.getRelatedEntity();
                 if (relatedClsName != null) {
                     Set<StringinverseClassNames =
                         .get(relatedClsName);
                     if (inverseClassNames == null) {
                         inverseClassNames = new HashSet<String>();
                         .put
                             (relatedClsNameinverseClassNames);
                     }
                     inverseClassNames.add(entityMeta.getClassName());
                 }
             }
         }
     }
 
     public Environment getEnvironment() {
         return ;
     }
 
     public StoreConfig getConfig() {
         return .clone();
     }
 
     public String getStoreName() {
         return ;
     }
 
     /* <!-- begin JE only --> */
     public static Set<StringgetStoreNames(Environment env)
         throws DatabaseException {
 
         Set<Stringset = new HashSet<String>();
         for (Object o : env.getDatabaseNames()) {
             String s = (Stringo;
             if (s.startsWith()) {
                 int start = .length();
                 int end = s.indexOf(start);
                 set.add(s.substring(startend));
             }
         }
         return set;
     }
     /* <!-- end JE only --> */

    
For unit testing.
 
     public boolean isReplicaUpgradeMode() {
         return .isReplicaUpgradeMode();
     }
 
     public EntityModel getModel() {
         return ;
     }
 
     public Mutations getMutations() {
         return .getMutations();
     }

    
A getPrimaryIndex with extra parameters for opening a raw store. primaryKeyClass and entityClass are used for generic typing; for a raw store, these should always be Object.class and RawObject.class. primaryKeyClassName is used for consistency checking and should be null for a raw store only. entityClassName is used to identify the store and may not be null.
 
     public synchronized <PK, E> PrimaryIndex<PK, E>
         getPrimaryIndex(Class<PK> primaryKeyClass,
                         String primaryKeyClassName,
                         Class<E> entityClass,
                         String entityClassName)
         throws DatabaseExceptionIndexNotAvailableException {
 
         assert ( && entityClass == RawObject.class) ||
               (! && entityClass != RawObject.class);
         assert ( && primaryKeyClassName == null) ||
               (! && primaryKeyClassName != null);
 
         checkOpen();
 
         InternalPrimaryIndex<PK, E> priIndex =
             .get(entityClassName);
         if (priIndex == null) {
 
             /* Check metadata. */
             EntityMetadata entityMeta = checkEntityClass(entityClassName);
             PrimaryKeyMetadata priKeyMeta = entityMeta.getPrimaryKey();
             if (primaryKeyClassName == null) {
                 primaryKeyClassName = priKeyMeta.getClassName();
             } else {
                 String expectClsName =
                     SimpleCatalog.keyClassName(priKeyMeta.getClassName());
                 if (!primaryKeyClassName.equals(expectClsName)) {
                     throw new IllegalArgumentException
                         ("Wrong primary key class: " + primaryKeyClassName +
                          " Correct class is: " + expectClsName);
                 }
             }
 
             /* Create bindings. */
             PersistEntityBinding entityBinding =
                 new PersistEntityBinding(entityClassName);
             PersistKeyBinding keyBinding = getKeyBinding(primaryKeyClassName);
 
             /* If not read-only, get the primary key sequence. */
             String seqName = priKeyMeta.getSequenceName();
             if (!.getReadOnly() && seqName != null) {
                 entityBinding.keyAssigner = new PersistKeyAssigner
                     (keyBindingentityBindinggetSequence(seqName));
             }
 
             /*
              * Use a single transaction for opening the primary DB and its
              * secondaries.  If opening any secondary fails, abort the
              * transaction and undo the changes to the state of the store.
              * Also support undo if the store is non-transactional.
              *
              * Use a no-wait transaction to avoid blocking on a Replica while
              * attempting to open an index that is currently being populated
              * via the replication stream from the Master.
              */
             Transaction txn = null;
             DatabaseConfig dbConfig = getPrimaryConfig(entityMeta);
             if (dbConfig.getTransactional() &&
                 DbCompat.getThreadTransaction() == null) {
                 txn = .beginTransaction(null);
             }
             PrimaryOpenState priOpenState =
                 new PrimaryOpenState(entityClassName);
             final boolean saveAllowCreate = dbConfig.getAllowCreate();
             boolean success = false;
             try {
 
                 /*
                  * The AllowCreate setting is false in read-only / Replica
                  * upgrade mode. In this mode new primaries are not available.
                  * They can be opened later when the upgrade is complete on the
                  * Master, by calling getSecondaryIndex.  [#16655]
                  */
                 if (.isReadOnly()) {
                     dbConfig.setAllowCreate(false);
                 }
 
                 /*
                  * Open the primary database.  Account for database renaming
                  * by calling getDatabaseClassName.  The dbClassName will be
                  * null if the format has not yet been stored. [#16655].
                  */
                 Database db = null;
                 final String dbClassName =
                     .getDatabaseClassName(entityClassName);
                 if (dbClassName != null) {
                     final String[] fileAndDbNames =
                         parseDbName( + dbClassName);
                     /* <!-- begin JE only --> */
                     try {
                     /* <!-- end JE only --> */
                         db = DbCompat.openDatabase(txnfileAndDbNames[0],
                                                    fileAndDbNames[1],
                                                    dbConfig);
                     /* <!-- begin JE only --> */
                     } catch (LockConflictException e) {
                         /* Treat this as if the database does not exist. */
                     }
                     /* <!-- end JE only --> */
                 }
                 if (db == null) {
                     throw new IndexNotAvailableException
                         ("PrimaryIndex not yet available on this Replica, " +
                          "entity class: " + entityClassName);
                 }
 
                 priOpenState.addDatabase(db);
 
                 /* Create index object. */
                 priIndex = new InternalPrimaryIndex(dbprimaryKeyClass,
                                                     keyBindingentityClass,
                                                     entityBinding);
 
                 /* Update index and database maps. */
                 .put(entityClassNamepriIndex);
                 if (DbCompat.getDeferredWrite(dbConfig)) {
                     .put(dbnull);
                 }
 
                 /* If not read-only, open all associated secondaries. */
                 if (!dbConfig.getReadOnly()) {
                     openSecondaryIndexes(txnentityMetapriOpenState);
 
                     /*
                      * To enable foreign key contraints, also open all primary
                      * indexes referring to this class via a relatedEntity
                      * property in another entity. [#15358]
                      */
                     Set<StringinverseClassNames =
                         .get(entityClassName);
                     if (inverseClassNames != null) {
                         for (String relatedClsName : inverseClassNames) {
                             getRelatedIndex(relatedClsName);
                         }
                     }
                 }
                 success = true;
             } finally {
                 dbConfig.setAllowCreate(saveAllowCreate);
                 if (success) {
                     if (txn != null) {
                         txn.commit();
                     }
                 } else {
                     if (txn != null) {
                         txn.abort();
                     }
                     priOpenState.undoState();
                 }
             }
         }
         return priIndex;
     }

    
Holds state information about opening a primary index and its secondary indexes. Used to undo the state of this object if the transaction opening the primary and secondaries aborts. Also used to close all databases opened during this process for a non-transactional store.
 
     private class PrimaryOpenState {
 
         private String entityClassName;
         private IdentityHashMap<DatabaseObjectdatabases;
         private Set<StringsecNames;
 
         PrimaryOpenState(String entityClassName) {
             this. = entityClassName;
              = new IdentityHashMap<DatabaseObject>();
              = new HashSet<String>();
         }

        
Save a database that was opening during this operation.
 
         void addDatabase(Database db) {
             .put(dbnull);
         }

        
Save the name of a secondary index that was opening during this operation.
 
         void addSecondaryName(String secName) {
             .add(secName);
         }

        
Reset all state information and closes any databases opened, when this operation fails. This method should be called for both transactional and non-transsactional operation. For transactional operations on JE, we don't strictly need to close the databases since the transaction abort will do that. However, closing them is harmless on JE, and required for DB core.
 
         void undoState() {
             for (Database db : .keySet()) {
                 try {
                     db.close();
                 } catch (Exception ignored) {
                 }
             }
             .remove();
             for (String secName : ) {
                 .remove(secName);
             }
             for (Database db : .keySet()) {
                 .remove(db);
             }
         }
     }

    
Opens a primary index related via a foreign key (relatedEntity). Related indexes are not opened in the same transaction used by the caller to open a primary or secondary. It is OK to leave the related index open when the caller's transaction aborts. It is only important to open a primary and its secondaries atomically.
 
     private PrimaryIndex getRelatedIndex(String relatedClsName)
         throws DatabaseException {
 
         PrimaryIndex relatedIndex = .get(relatedClsName);
         if (relatedIndex == null) {
             EntityMetadata relatedEntityMeta =
                 checkEntityClass(relatedClsName);
             Class relatedKeyCls;
             String relatedKeyClsName;
             Class relatedCls;
             if () {
                 relatedCls = RawObject.class;
                 relatedKeyCls = Object.class;
                 relatedKeyClsName = null;
             } else {
                 try {
                     relatedCls = .resolveClass(relatedClsName);
                 } catch (ClassNotFoundException e) {
                     throw new IllegalArgumentException
                         ("Related entity class not found: " +
                          relatedClsName);
                 }
                 relatedKeyClsName = SimpleCatalog.keyClassName
                     (relatedEntityMeta.getPrimaryKey().getClassName());
                 relatedKeyCls = .resolveKeyClass(relatedKeyClsName);
             }
 
             /*
              * Cycles are prevented here by adding primary indexes to the
              * priIndexMap as soon as they are created, before opening related
              * indexes.
              */
             relatedIndex = getPrimaryIndex
                 (relatedKeyClsrelatedKeyClsName,
                  relatedClsrelatedClsName);
         }
         return relatedIndex;
     }

    
A getSecondaryIndex with extra parameters for opening a raw store. keyClassName is used for consistency checking and should be null for a raw store only.
 
     public synchronized <SK, PK, E1, E2 extends E1> SecondaryIndex<SK, PK, E2>
         getSecondaryIndex(PrimaryIndex<PK, E1> primaryIndex,
                           Class<E2> entityClass,
                           String entityClassName,
                           Class<SK> keyClass,
                           String keyClassName,
                           String keyName)
         throws DatabaseExceptionIndexNotAvailableException {
 
         assert ( && keyClassName == null) ||
               (! && keyClassName != null);
 
         checkOpen();
 
         EntityMetadata entityMeta = null;
         SecondaryKeyMetadata secKeyMeta = null;
 
         /* Validate the subclass for a subclass index. */
         if (entityClass != primaryIndex.getEntityClass()) {
             entityMeta = .getEntityMetadata(entityClassName);
             assert entityMeta != null;
             secKeyMeta = checkSecKey(entityMetakeyName);
             String subclassName = entityClass.getName();
             String declaringClassName = secKeyMeta.getDeclaringClassName();
             if (!subclassName.equals(declaringClassName)) {
                 throw new IllegalArgumentException
                     ("Key for subclass " + subclassName +
                      " is declared in a different class: " +
                      makeSecName(declaringClassNamekeyName));
             }
 
             /*
              * Get/create the subclass format to ensure it is stored in the
              * catalog, even if no instances of the subclass are stored.
              * [#16399]
              */
             try {
                 .getFormat(entityClass,
                                   false /*checkEntitySubclassIndexes*/);
             } catch (RefreshException e) {
                 e.refresh();
                 try {
                     .getFormat(entityClass,
                                       false /*checkEntitySubclassIndexes*/);
                 } catch (RefreshException e2) {
                     throw DbCompat.unexpectedException(e2);
                 }
             }
         }
 
         /*
          * Even though the primary is already open, we can't assume the
          * secondary is open because we don't automatically open all
          * secondaries when the primary is read-only.  Use auto-commit (a null
          * transaction) since we're opening only one database.
          */
         String secName = makeSecName(entityClassNamekeyName);
         InternalSecondaryIndex<SK, PK, E2> secIndex = .get(secName);
         if (secIndex == null) {
             if (entityMeta == null) {
                 entityMeta = .getEntityMetadata(entityClassName);
                 assert entityMeta != null;
             }
             if (secKeyMeta == null) {
                 secKeyMeta = checkSecKey(entityMetakeyName);
             }
 
             /* Check metadata. */
             if (keyClassName == null) {
                 keyClassName = getSecKeyClass(secKeyMeta);
             } else {
                 String expectClsName = getSecKeyClass(secKeyMeta);
                 if (!keyClassName.equals(expectClsName)) {
                     throw new IllegalArgumentException
                         ("Wrong secondary key class: " + keyClassName +
                          " Correct class is: " + expectClsName);
                 }
             }
 
             /*
              * Account for database renaming.  The dbClassName or dbKeyName
              * will be null if the format has not yet been stored. [#16655]
              */
             final String dbClassName =
                 .getDatabaseClassName(entityClassName);
             final String dbKeyName =
                 .getDatabaseKeyName(entityClassNamekeyName);
             if (dbClassName != null && dbKeyName != null) {
 
                 /*
                  * Use a no-wait transaction to avoid blocking on a Replica
                  * while attempting to open an index that is currently being
                  * populated via the replication stream from the Master.
                  */
                 Transaction txn = null;
                 if (getPrimaryConfig(entityMeta).getTransactional() &&
                     DbCompat.getThreadTransaction() == null) {
                     txn = .beginTransaction(null,
                                                );
                 }
                 boolean success = false;
                 try {
 
                     /*
                      * The doNotCreate param is true below in read-only /
                      * Replica upgrade mode. In this mode new secondaries are
                      * not available.  They can be opened later when the
                      * upgrade is complete on the Master, by calling
                      * getSecondaryIndex.  [#16655]
                      */
                     secIndex = openSecondaryIndex
                         (txnprimaryIndexentityClassentityMeta,
                          keyClasskeyClassNamesecKeyMetasecName,
                          makeSecName(dbClassNamedbKeyName),
                          .isReadOnly() /*doNotCreate*/,
                          null /*priOpenState*/);
                     success = true;
                 /* <!-- begin JE only --> */
                 } catch (LockConflictException e) {
                     /* Treat this as if the database does not exist. */
                 /* <!-- end JE only --> */
                 } finally {
                     if (success) {
                         if (txn != null) {
                             txn.commit();
                         }
                     } else {
                         if (txn != null) {
                             txn.abort();
                         }
                     }
                 }
             }
             if (secIndex == null) {
                 throw new IndexNotAvailableException
                     ("SecondaryIndex not yet available on this Replica, " +
                      "entity class: " + entityClassName + ", key name: " +
                      keyName);
             }
         }
         return secIndex;
     }

    
Opens secondary indexes for a given primary index metadata.
 
     private void openSecondaryIndexes(Transaction txn,
                                       EntityMetadata entityMeta,
                                       PrimaryOpenState priOpenState)
         throws DatabaseException {
 
         String entityClassName = entityMeta.getClassName();
         PrimaryIndex<ObjectObjectpriIndex =
             .get(entityClassName);
         assert priIndex != null;
         Class<ObjectentityClass = priIndex.getEntityClass();
 
         for (SecondaryKeyMetadata secKeyMeta :
              entityMeta.getSecondaryKeys().values()) {
             String keyName = secKeyMeta.getKeyName();
             String secName = makeSecName(entityClassNamekeyName);
             SecondaryIndex<ObjectObjectObjectsecIndex =
                 .get(secName);
             if (secIndex == null) {
                 String keyClassName = getSecKeyClass(secKeyMeta);
                 /* RawMode: should not require class. */
                 Class keyClass = .resolveKeyClass(keyClassName);
 
                 /*
                  * Account for database renaming.  The dbClassName or dbKeyName
                  * will be null if the format has not yet been stored. [#16655]
                  */
                 final String dbClassName =
                     .getDatabaseClassName(entityClassName);
                 final String dbKeyName =
                     .getDatabaseKeyName(entityClassNamekeyName);
                 if (dbClassName != null && dbKeyName != null) {
 
                     /*
                      * The doNotCreate param is true below in two cases:
                      * 1- When SecondaryBulkLoad=true, new secondaries are not
                      *    created/populated until getSecondaryIndex is called.
                      * 2- In read-only / Replica upgrade mode, new secondaries
                      *    are not openeed when the primary is opened.  They can
                      *    be opened later when the upgrade is complete on the
                      *    Master, by calling getSecondaryIndex.  [#16655]
                      */
                     openSecondaryIndex
                         (txnpriIndexentityClassentityMeta,
                          keyClasskeyClassNamesecKeyMeta,
                          secNamemakeSecName(dbClassNamedbKeyName),
                          (.getSecondaryBulkLoad() ||
                           .isReadOnly()) /*doNotCreate*/,
                          priOpenState);
                 }
             }
         }
     }

    
Opens a secondary index with a given transaction and adds it to the secIndexMap. We assume that the index is not already open.
 
     private <SK, PK, E1, E2 extends E1> InternalSecondaryIndex<SK, PK, E2>
         openSecondaryIndex(Transaction txn,
                            PrimaryIndex<PK, E1> primaryIndex,
                            Class<E2> entityClass,
                            EntityMetadata entityMeta,
                            Class<SK> keyClass,
                            String keyClassName,
                            SecondaryKeyMetadata secKeyMeta,
                            String secName,
                            String dbSecName,
                            boolean doNotCreate,
                            PrimaryOpenState priOpenState)
         throws DatabaseException {
 
         assert !.containsKey(secName);
         String[] fileAndDbNames = parseDbName( + dbSecName);
         SecondaryConfig config =
             getSecondaryConfig(secNameentityMetakeyClassNamesecKeyMeta);
         Database priDb = primaryIndex.getDatabase();
         DatabaseConfig priConfig = priDb.getConfig();
 
         String relatedClsName = secKeyMeta.getRelatedEntity();
         if (relatedClsName != null) {
             PrimaryIndex relatedIndex = getRelatedIndex(relatedClsName);
             config.setForeignKeyDatabase(relatedIndex.getDatabase());
         }
 
         if (config.getTransactional() != priConfig.getTransactional() ||
             DbCompat.getDeferredWrite(config) !=
             DbCompat.getDeferredWrite(priConfig) ||
             config.getReadOnly() != priConfig.getReadOnly()) {
             throw new IllegalArgumentException
                 ("One of these properties was changed to be inconsistent" +
                  " with the associated primary database: " +
                  " Transactional, DeferredWrite, ReadOnly");
         }
 
         PersistKeyBinding keyBinding = getKeyBinding(keyClassName);
 
             (txnfileAndDbNamesprimaryIndex
              secKeyMeta.getKeyName(), configdoNotCreate);
         if (db == null) {
             assert doNotCreate;
             return null;
         }
 
         InternalSecondaryIndex<SK, PK, E2> secIndex =
             new InternalSecondaryIndex(dbprimaryIndexkeyClasskeyBinding,
                                        getKeyCreator(config));
 
         /* Update index and database maps. */
         .put(secNamesecIndex);
         if (DbCompat.getDeferredWrite(config)) {
             .put(dbnull);
         }
         if (priOpenState != null) {
             priOpenState.addDatabase(db);
             priOpenState.addSecondaryName(secName);
         }
         return secIndex;
     }

    
Open a secondary database, setting AllowCreate, ExclusiveCreate and AllowPopulate appropriately. We either set all three of these params to true or all to false. This ensures that we only populate a database when it is created, never if it just happens to be empty. [#16399] We also handle correction of a bug in duplicate ordering. See ComplexFormat.incorrectlyOrderedSecKeys.

Parameters:
doNotCreate is true when StoreConfig.getSecondaryBulkLoad is true and we are opening a secondary as a side effect of opening a primary, i.e., getSecondaryIndex is not being called. If doNotCreate is true and the database does not exist, we silently ignore the failure to create the DB and return null. When getSecondaryIndex is subsequently called, the secondary database will be created and populated from the primary -- a bulk load.
 
     private SecondaryDatabase
         openSecondaryDatabase(final Transaction txn,
                               final String[] fileAndDbNames,
                               final PrimaryIndex priIndex,
                               final String keyName,
                               final SecondaryConfig config,
                               final boolean doNotCreate)
         throws DatabaseException {
 
         assert config.getAllowPopulate();
         assert !config.getExclusiveCreate();
         final Database priDb = priIndex.getDatabase();
         final ComplexFormat entityFormat = (ComplexFormat)
             ((PersistEntityBindingpriIndex.getEntityBinding()).;
         final boolean saveAllowCreate = config.getAllowCreate();
         /* <!-- begin JE only --> */
         final boolean saveOverrideDuplicateComparator = 
             config.getOverrideDuplicateComparator();
         /* <!-- end JE only --> */
         final Comparator<byte[]> saveDupComparator = 
             config.getDuplicateComparator();
         try {
             if (doNotCreate) {
                 config.setAllowCreate(false);
             }
             /* First try creating a new database, populate if needed. */
             if (config.getAllowCreate()) {
                 config.setExclusiveCreate(true);
                 /* AllowPopulate is true; comparators are set. */
                 final SecondaryDatabase db = DbCompat.openSecondaryDatabase
                     (txnfileAndDbNames[0], fileAndDbNames[1], priDb,
                      config);
                 if (db != null) {
                     /* For unit testing. */ 
                     boolean doFlush = false;
                     /* Update dup ordering bug info. [#17252] */
                     if (config.getDuplicateComparator() != null &&
                         entityFormat.setSecKeyCorrectlyOrdered(keyName)) {
                         .flush(txn);
                         doFlush = true;
                     }
                     
                     /* 
                      * expectFlush is false except when set by
                      * SecondaryDupOrderTest.
                      */
                     assert ! || doFlush;
                     
                     return db;
                 }
             }
             /* Next try opening an existing database. */
             config.setAllowCreate(false);
             config.setAllowPopulate(false);
             config.setExclusiveCreate(false);
             
             /* Account for dup ordering bug. [#17252] */
             if (config.getDuplicateComparator() != null &&
                 entityFormat.isSecKeyIncorrectlyOrdered(keyName)) {
                 /* <!-- begin JE only --> */
                 config.setOverrideDuplicateComparator(false);
                 /* <!-- end JE only --> */
                 config.setDuplicateComparator((Comparator<byte[]>) null);
             }
             final SecondaryDatabase db = DbCompat.openSecondaryDatabase
                 (txnfileAndDbNames[0], fileAndDbNames[1], priDb,
                  config);
             return db;
         } finally {
             config.setAllowPopulate(true);
             config.setExclusiveCreate(false);
             config.setAllowCreate(saveAllowCreate);
             /* <!-- begin JE only --> */
             config.setOverrideBtreeComparator(saveOverrideDuplicateComparator);
             /* <!-- end JE only --> */
             config.setDuplicateComparator(saveDupComparator);
         }
     }

    
Checks that all secondary indexes defined in the given entity metadata are already open. This method is called when a new entity subclass is encountered when an instance of that class is stored. [#16399]

Throws:
java.lang.IllegalArgumentException if a secondary is not open.
 
     synchronized void
         checkEntitySubclassSecondaries(final EntityMetadata entityMeta,
                                        final String subclassName)
         throws DatabaseException {
 
         if (.getSecondaryBulkLoad()) {
             return;
         }
 
         final String entityClassName = entityMeta.getClassName();
 
         for (final SecondaryKeyMetadata secKeyMeta :
              entityMeta.getSecondaryKeys().values()) {
             final String keyName = secKeyMeta.getKeyName();
             final String secName = makeSecName(entityClassNamekeyName);
             if (!.containsKey(secName)) {
                 throw new IllegalArgumentException
                     ("Entity subclasses defining a secondary key must be " +
                      "registered by calling EntityModel.registerClass or " +
                      "EntityStore.getSubclassIndex before storing an " +
                      "instance of the subclass: " + subclassName);
             }
         }
     }
 
     /* <!-- begin JE only --> */
     public void sync()
         throws DatabaseException {
 
         List<Databasedbs = new ArrayList<Database>();
         synchronized (this) {
             dbs.addAll(.keySet());
         }
         for (Database db : dbs) {
             db.sync();
             /* Call hook for unit testing. */
             if ( != null) {
                 .onSync(db);
             }
         }
     }
     /* <!-- end JE only --> */
 
     public void truncateClass(Class entityClass)
         throws DatabaseException {
 
         truncateClass(nullentityClass);
     }
 
     public synchronized void truncateClass(Transaction txnClass entityClass)
         throws DatabaseException {
 
         checkOpen();
         checkWriteAllowed();
 
         /* Close primary and secondary databases. */
         closeClass(entityClass);
 
         String clsName = entityClass.getName();
         EntityMetadata entityMeta = checkEntityClass(clsName);
 
         boolean autoCommit = false;
         if (.getTransactional() &&
             txn == null &&
             DbCompat.getThreadTransaction() == null) {
            txn = .beginTransaction(null);
            autoCommit = true;
        }
        /*
         * Truncate the primary first and let any exceptions propogate
         * upwards.  Then remove each secondary, only throwing the first
         * exception.
         */
        boolean success = false;
        try {
            boolean primaryExists =
                truncateIfExists(txn + clsName);
            if (primaryExists) {
                DatabaseException firstException = null;
                for (SecondaryKeyMetadata keyMeta :
                     entityMeta.getSecondaryKeys().values()) {
                    /* Ignore secondaries that do not exist. */
                    removeIfExists
                        (txn,
                          +
                         makeSecName(clsNamekeyMeta.getKeyName()));
                }
                if (firstException != null) {
                    throw firstException;
                }
            }
            success = true;
        } finally {
            if (autoCommit) {
                if (success) {
                    txn.commit();
                } else {
                    txn.abort();
                }
            }
        }
    }
    private boolean truncateIfExists(Transaction txnString dbName)
        throws DatabaseException {
        String[] fileAndDbNames = parseDbName(dbName);
        return DbCompat.truncateDatabase
            (txnfileAndDbNames[0], fileAndDbNames[1]);
    }
    private boolean removeIfExists(Transaction txnString dbName)
        throws DatabaseException {
        String[] fileAndDbNames = parseDbName(dbName);
        return DbCompat.removeDatabase
            (txnfileAndDbNames[0], fileAndDbNames[1]);
    }
    public synchronized void closeClass(Class entityClass)
        throws DatabaseException {
        checkOpen();
        String clsName = entityClass.getName();
        EntityMetadata entityMeta = checkEntityClass(clsName);
        PrimaryIndex priIndex = .get(clsName);
        if (priIndex != null) {
            /* Close the secondaries first. */
            DatabaseException firstException = null;
            for (SecondaryKeyMetadata keyMeta :
                 entityMeta.getSecondaryKeys().values()) {
                String secName = makeSecName(clsNamekeyMeta.getKeyName());
                SecondaryIndex secIndex = .get(secName);
                if (secIndex != null) {
                    Database db = secIndex.getDatabase();
                    firstException = closeDb(dbfirstException);
                    firstException =
                        closeDb(secIndex.getKeysDatabase(), firstException);
                    .remove(secName);
                    .remove(db);
                }
            }
            /* Close the primary last. */
            Database db = priIndex.getDatabase();
            firstException = closeDb(dbfirstException);
            .remove(clsName);
            .remove(db);
            /* Throw the first exception encountered. */
            if (firstException != null) {
                throw firstException;
            }
        }
    }
    public synchronized void close()
        throws DatabaseException {
        if ( == null) {
            return;
        }
        DatabaseException firstException = null;
        try {
            if () {
                boolean allClosed = .close();
                assert allClosed;
            } else {
                synchronized () {
                    Map<StringPersistCatalogcatalogMap =
                        .get();
                    assert catalogMap != null;
                    if (.close()) {
                        /* Remove when the reference count goes to zero. */
                        catalogMap.remove();
                    }
                }
            }
             = null;
        } catch (DatabaseException e) {
            if (firstException == null) {
                firstException = e;
            }
        }
        for (Sequence seq : .values()) {
            try {
                seq.close();
            } catch (DatabaseException e) {
                if (firstException == null) {
                    firstException = e;