Exemple #1
0
  public synchronized void closeClass(Class entityClass) throws DatabaseException {

    checkOpen();
    String clsName = entityClass.getName();
    EntityMetadata entityMeta = checkEntityClass(clsName);

    PrimaryIndex priIndex = priIndexMap.get(clsName);
    if (priIndex != null) {
      /* Close the secondaries first. */
      DatabaseException firstException = null;
      for (SecondaryKeyMetadata keyMeta : entityMeta.getSecondaryKeys().values()) {

        String secName = makeSecName(clsName, keyMeta.getKeyName());
        SecondaryIndex secIndex = secIndexMap.get(secName);
        if (secIndex != null) {
          Database db = secIndex.getDatabase();
          firstException = closeDb(db, firstException);
          firstException = closeDb(secIndex.getKeysDatabase(), firstException);
          secIndexMap.remove(secName);
          deferredWriteDatabases.remove(db);
        }
      }
      /* Close the primary last. */
      Database db = priIndex.getDatabase();
      firstException = closeDb(db, firstException);
      priIndexMap.remove(clsName);
      deferredWriteDatabases.remove(db);

      /* Throw the first exception encountered. */
      if (firstException != null) {
        throw firstException;
      }
    }
  }
Exemple #2
0
  /**
   * Opens any secondary indexes defined in the given entity metadata that are not already open.
   * This method is called when a new entity subclass is encountered when an instance of that class
   * is stored, and the EntityStore.getSubclassIndex has not been previously called for that class.
   * [#15247]
   */
  synchronized void openSecondaryIndexes(
      Transaction txn, EntityMetadata entityMeta, PrimaryOpenState priOpenState)
      throws DatabaseException {

    String entityClassName = entityMeta.getClassName();
    PrimaryIndex<Object, Object> priIndex = priIndexMap.get(entityClassName);
    assert priIndex != null;
    Class<Object> entityClass = priIndex.getEntityClass();

    for (SecondaryKeyMetadata secKeyMeta : entityMeta.getSecondaryKeys().values()) {
      String keyName = secKeyMeta.getKeyName();
      String secName = makeSecName(entityClassName, keyName);
      SecondaryIndex<Object, Object, Object> secIndex = secIndexMap.get(secName);
      if (secIndex == null) {
        String keyClassName = getSecKeyClass(secKeyMeta);
        /* RawMode: should not require class. */
        Class keyClass = SimpleCatalog.keyClassForName(keyClassName);
        openSecondaryIndex(
            txn,
            priIndex,
            entityClass,
            entityMeta,
            keyClass,
            keyClassName,
            secKeyMeta,
            makeSecName(entityClassName, secKeyMeta.getKeyName()),
            storeConfig.getSecondaryBulkLoad() /*doNotCreate*/,
            priOpenState);
      }
    }
  }
Exemple #3
0
 private String getSecKeyClass(SecondaryKeyMetadata secKeyMeta) {
   String clsName = secKeyMeta.getElementClassName();
   if (clsName == null) {
     clsName = secKeyMeta.getClassName();
   }
   return SimpleCatalog.keyClassName(clsName);
 }
Exemple #4
0
 private SecondaryConfig getSecondaryConfig(
     String secName,
     EntityMetadata entityMeta,
     String keyClassName,
     SecondaryKeyMetadata secKeyMeta) {
   SecondaryConfig config = secConfigMap.get(secName);
   if (config == null) {
     /* Set common properties to match the primary DB. */
     DatabaseConfig priConfig = getPrimaryConfig(entityMeta);
     config = new SecondaryConfig();
     config.setTransactional(priConfig.getTransactional());
     config.setAllowCreate(!priConfig.getReadOnly());
     config.setReadOnly(priConfig.getReadOnly());
     DbCompat.setDeferredWrite(config, DbCompat.getDeferredWrite(priConfig));
     /* Set secondary properties based on metadata. */
     config.setAllowPopulate(true);
     Relationship rel = secKeyMeta.getRelationship();
     config.setSortedDuplicates(
         rel == Relationship.MANY_TO_ONE || rel == Relationship.MANY_TO_MANY);
     setBtreeComparator(config, secKeyMeta.getClassName());
     PersistKeyCreator keyCreator =
         new PersistKeyCreator(catalog, entityMeta, keyClassName, secKeyMeta);
     if (rel == Relationship.ONE_TO_MANY || rel == Relationship.MANY_TO_MANY) {
       config.setMultiKeyCreator(keyCreator);
     } else {
       config.setKeyCreator(keyCreator);
     }
     DeleteAction deleteAction = secKeyMeta.getDeleteAction();
     if (deleteAction != null) {
       ForeignKeyDeleteAction baseDeleteAction;
       switch (deleteAction) {
         case ABORT:
           baseDeleteAction = ForeignKeyDeleteAction.ABORT;
           break;
         case CASCADE:
           baseDeleteAction = ForeignKeyDeleteAction.CASCADE;
           break;
         case NULLIFY:
           baseDeleteAction = ForeignKeyDeleteAction.NULLIFY;
           break;
         default:
           throw new IllegalStateException(deleteAction.toString());
       }
       config.setForeignKeyDeleteAction(baseDeleteAction);
       if (deleteAction == DeleteAction.NULLIFY) {
         config.setForeignMultiKeyNullifier(keyCreator);
       }
     }
     secConfigMap.put(secName, config);
   }
   return config;
 }
Exemple #5
0
  public synchronized void truncateClass(Transaction txn, Class entityClass)
      throws DatabaseException {

    checkOpen();

    /* Close primary and secondary databases. */
    closeClass(entityClass);

    String clsName = entityClass.getName();
    EntityMetadata entityMeta = checkEntityClass(clsName);

    /*
     * Truncate the primary first and let any exceptions propogate
     * upwards.  Then truncate each secondary, only throwing the first
     * exception.
     */
    String dbName = storePrefix + clsName;
    boolean primaryExists = true;
    try {
      env.truncateDatabase(txn, dbName, false);
    } catch (DatabaseNotFoundException ignored) {
      primaryExists = false;
    }
    if (primaryExists) {
      DatabaseException firstException = null;
      for (SecondaryKeyMetadata keyMeta : entityMeta.getSecondaryKeys().values()) {
        try {
          env.truncateDatabase(
              txn, storePrefix + makeSecName(clsName, keyMeta.getKeyName()), false);
        } catch (DatabaseNotFoundException ignored) {
          /* Ignore secondaries that do not exist. */
        } catch (DatabaseException e) {
          if (firstException == null) {
            firstException = e;
          }
        }
      }
      if (firstException != null) {
        throw firstException;
      }
    }
  }
Exemple #6
0
  public Store(Environment env, String storeName, StoreConfig config, boolean rawAccess)
      throws DatabaseException {

    this.env = env;
    this.storeName = storeName;
    this.rawAccess = rawAccess;

    if (env == null || storeName == null) {
      throw new NullPointerException("env and storeName parameters must not be null");
    }
    if (config != null) {
      model = config.getModel();
      mutations = config.getMutations();
    }
    if (config == null) {
      storeConfig = StoreConfig.DEFAULT;
    } else {
      storeConfig = config.cloneConfig();
    }

    storePrefix = NAME_PREFIX + storeName + NAME_SEPARATOR;
    priIndexMap = new HashMap<String, PrimaryIndex>();
    secIndexMap = new HashMap<String, SecondaryIndex>();
    priConfigMap = new HashMap<String, DatabaseConfig>();
    secConfigMap = new HashMap<String, SecondaryConfig>();
    keyBindingMap = new HashMap<String, PersistKeyBinding>();
    sequenceMap = new HashMap<String, Sequence>();
    sequenceConfigMap = new HashMap<String, SequenceConfig>();
    deferredWriteDatabases = new IdentityHashMap<Database, Object>();

    if (rawAccess) {
      /* Open a read-only catalog that uses the stored model. */
      if (model != null) {
        throw new IllegalArgumentException("A model may not be specified when opening a RawStore");
      }
      DatabaseConfig dbConfig = new DatabaseConfig();
      dbConfig.setReadOnly(true);
      dbConfig.setTransactional(storeConfig.getTransactional());
      catalog =
          new PersistCatalog(
              null,
              env,
              storePrefix,
              storePrefix + CATALOG_DB,
              dbConfig,
              model,
              mutations,
              rawAccess,
              this);
    } else {
      /* Open the shared catalog that uses the current model. */
      synchronized (catalogPool) {
        Map<String, PersistCatalog> catalogMap = catalogPool.get(env);
        if (catalogMap == null) {
          catalogMap = new HashMap<String, PersistCatalog>();
          catalogPool.put(env, catalogMap);
        }
        catalog = catalogMap.get(storeName);
        if (catalog != null) {
          catalog.openExisting();
        } else {
          Transaction txn = null;
          if (storeConfig.getTransactional() && env.getThreadTransaction() == null) {
            txn = env.beginTransaction(null, null);
          }
          boolean success = false;
          try {
            DatabaseConfig dbConfig = new DatabaseConfig();
            dbConfig.setAllowCreate(storeConfig.getAllowCreate());
            dbConfig.setReadOnly(storeConfig.getReadOnly());
            dbConfig.setTransactional(storeConfig.getTransactional());
            catalog =
                new PersistCatalog(
                    txn,
                    env,
                    storePrefix,
                    storePrefix + CATALOG_DB,
                    dbConfig,
                    model,
                    mutations,
                    rawAccess,
                    this);
            catalogMap.put(storeName, catalog);
            success = true;
          } finally {
            if (txn != null) {
              if (success) {
                txn.commit();
              } else {
                txn.abort();
              }
            }
          }
        }
      }
    }

    /* Get the merged mutations from the catalog. */
    mutations = catalog.getMutations();

    /*
     * If there is no model parameter, use the default or stored model
     * obtained from the catalog.
     */
    model = catalog.getResolvedModel();

    /*
     * Give the model a reference to the catalog to fully initialize the
     * model.  Only then may we initialize the Converter mutations, which
     * themselves may call model methods and expect the model to be fully
     * initialized.
     */
    ModelInternal.setCatalog(model, catalog);
    for (Converter converter : mutations.getConverters()) {
      converter.getConversion().initialize(model);
    }

    /*
     * 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]
     */
    inverseRelatedEntityMap = new HashMap<String, Set<String>>();
    List<Format> entityFormats = new ArrayList<Format>();
    catalog.getEntityFormats(entityFormats);
    for (Format entityFormat : entityFormats) {
      EntityMetadata entityMeta = entityFormat.getEntityMetadata();
      for (SecondaryKeyMetadata secKeyMeta : entityMeta.getSecondaryKeys().values()) {
        String relatedClsName = secKeyMeta.getRelatedEntity();
        if (relatedClsName != null) {
          Set<String> inverseClassNames = inverseRelatedEntityMap.get(relatedClsName);
          if (inverseClassNames == null) {
            inverseClassNames = new HashSet<String>();
            inverseRelatedEntityMap.put(relatedClsName, inverseClassNames);
          }
          inverseClassNames.add(entityMeta.getClassName());
        }
      }
    }
  }
Exemple #7
0
  /**
   * 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> SecondaryIndex<SK, PK, E2> openSecondaryIndex(
      Transaction txn,
      PrimaryIndex<PK, E1> primaryIndex,
      Class<E2> entityClass,
      EntityMetadata entityMeta,
      Class<SK> keyClass,
      String keyClassName,
      SecondaryKeyMetadata secKeyMeta,
      String secName,
      boolean doNotCreate,
      PrimaryOpenState priOpenState)
      throws DatabaseException {

    assert !secIndexMap.containsKey(secName);
    String dbName = storePrefix + secName;
    SecondaryConfig config = getSecondaryConfig(secName, entityMeta, keyClassName, secKeyMeta);
    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);

    /*
     * 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 DatabaseNotFoundException and return null.  When
     * getSecondaryIndex is subsequently called, the secondary database
     * will be created and populated from the primary -- a bulk load.
     */
    SecondaryDatabase db;
    boolean saveAllowCreate = config.getAllowCreate();
    try {
      if (doNotCreate) {
        config.setAllowCreate(false);
      }
      db = env.openSecondaryDatabase(txn, dbName, priDb, config);
    } catch (DatabaseNotFoundException e) {
      if (doNotCreate) {
        return null;
      } else {
        throw e;
      }
    } finally {
      if (doNotCreate) {
        config.setAllowCreate(saveAllowCreate);
      }
    }
    SecondaryIndex<SK, PK, E2> secIndex =
        new SecondaryIndex(db, null, primaryIndex, keyClass, keyBinding);

    /* Update index and database maps. */
    secIndexMap.put(secName, secIndex);
    if (DbCompat.getDeferredWrite(config)) {
      deferredWriteDatabases.put(db, null);
    }
    if (priOpenState != null) {
      priOpenState.addDatabase(db);
      priOpenState.addSecondaryName(secName);
    }
    return secIndex;
  }
Exemple #8
0
  /**
   * 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 DatabaseException {

    assert (rawAccess && keyClassName == null) || (!rawAccess && keyClassName != null);

    checkOpen();

    EntityMetadata entityMeta = null;
    SecondaryKeyMetadata secKeyMeta = null;

    /* Validate the subclass for a subclass index. */
    if (entityClass != primaryIndex.getEntityClass()) {
      entityMeta = model.getEntityMetadata(entityClassName);
      assert entityMeta != null;
      secKeyMeta = checkSecKey(entityMeta, keyName);
      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(declaringClassName, keyName));
      }
    }

    /*
     * 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(entityClassName, keyName);
    SecondaryIndex<SK, PK, E2> secIndex = secIndexMap.get(secName);
    if (secIndex == null) {
      if (entityMeta == null) {
        entityMeta = model.getEntityMetadata(entityClassName);
        assert entityMeta != null;
      }
      if (secKeyMeta == null) {
        secKeyMeta = checkSecKey(entityMeta, keyName);
      }

      /* 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);
        }
      }

      secIndex =
          openSecondaryIndex(
              null,
              primaryIndex,
              entityClass,
              entityMeta,
              keyClass,
              keyClassName,
              secKeyMeta,
              secName,
              false /*doNotCreate*/,
              null /*priOpenState*/);
    }
    return secIndex;
  }