@Override
 public DatastoreTable getDatastoreClass(String className, ClassLoaderResolver clr) {
   try {
     // We see the occasional race condition when multiple threads concurrently
     // perform an operation using a persistence-capable class for which DataNucleus
     // has not yet generated the meta-data.  The result is usually
     // AbstractMemberMetaData objects with the same column listed twice in the meta-data.
     // Locking at this level is a bit more coarse-grained than I'd like but once the
     // meta-data has been built this will return super fast so it shouldn't be an issue.
     synchronized (this) {
       return (DatastoreTable) super.getDatastoreClass(className, clr);
     }
   } catch (NoTableManagedException e) {
     // Our parent class throws this when the class isn't PersistenceCapable also.
     Class cls = clr.classForName(className);
     ApiAdapter api = getApiAdapter();
     if (cls != null && !cls.isInterface() && !api.isPersistable(cls)) {
       throw new NoTableManagedException(
           "Class "
               + className
               + " does not seem to have been enhanced. You may want to rerun "
               + "the enhancer and check for errors in the output.");
       // Suggest you address why this method is being called before any check on whether it is
       // persistable
       // then you can remove this error message
     }
     throw e;
   }
 }
  /**
   * Method to validate an element against the accepted type.
   *
   * @param clr The ClassLoaderResolver
   * @param element The element to validate
   * @return Whether it is valid.
   */
  protected boolean validateElementType(ClassLoaderResolver clr, Object element) {
    if (element == null) {
      return true;
    }

    Class primitiveElementClass = ClassUtils.getPrimitiveTypeForType(element.getClass());
    if (primitiveElementClass != null) {
      // Allow for the element type being primitive, and the user wanting to store its wrapper
      String elementTypeWrapper = elementType;
      Class elementTypeClass = clr.classForName(elementType);
      if (elementTypeClass.isPrimitive()) {
        elementTypeWrapper = ClassUtils.getWrapperTypeForPrimitiveType(elementTypeClass).getName();
      }
      return clr.isAssignableFrom(elementTypeWrapper, element.getClass());
    }

    String elementType = null;
    if (ownerMemberMetaData.hasCollection()) {
      elementType = ownerMemberMetaData.getCollection().getElementType();
    } else if (ownerMemberMetaData.hasArray()) {
      elementType = ownerMemberMetaData.getArray().getElementType();
    } else {
      elementType = this.elementType;
    }

    Class elementCls = clr.classForName(elementType);
    if (!storeMgr.getNucleusContext().getMetaDataManager().isPersistentInterface(elementType)
        && elementCls.isInterface()) {
      // Collection of interface types, so check against the available implementations TODO Check
      // against allowed implementation in metadata
      String[] clsNames =
          storeMgr
              .getNucleusContext()
              .getMetaDataManager()
              .getClassesImplementingInterface(elementType, clr);
      if (clsNames != null && clsNames.length > 0) {
        for (int i = 0; i < clsNames.length; i++) {
          if (clsNames[i].equals(element.getClass().getName())) {
            return true;
          }
        }
        return false;
      }
    }
    return clr.isAssignableFrom(elementType, element.getClass());
  }
  /**
   * Method to validate an element against the accepted type.
   *
   * @param clr The ClassLoaderResolver
   * @param element The element to validate
   * @return Whether it is valid.
   */
  protected boolean validateElementType(ClassLoaderResolver clr, Object element) {
    if (element == null) {
      return true;
    }

    Class primitiveElementClass = ClassUtils.getPrimitiveTypeForType(element.getClass());
    if (primitiveElementClass != null) {

      // Allow for the element type being primitive, and the user wanting to store its wrapper
      String elementTypeWrapper = elementType;
      Class elementTypeClass = clr.classForName(elementType);
      if (elementTypeClass.isPrimitive()) {
        elementTypeWrapper = ClassUtils.getWrapperTypeForPrimitiveType(elementTypeClass).getName();
      }
      return clr.isAssignableFrom(elementTypeWrapper, element.getClass());
    }
    return clr.isAssignableFrom(elementType, element.getClass());
  }
 /* (non-Javadoc)
  * @see org.datanucleus.jta.TransactionManagerLocator#getTransactionManager(org.datanucleus.ClassLoaderResolver)
  */
 public TransactionManager getTransactionManager(ClassLoaderResolver clr) {
   Class cls = clr.classForName("com.atomikos.icatch.jta.UserTransactionManager");
   try {
     return (TransactionManager) cls.newInstance();
   } catch (Exception e) {
     NucleusLogger.TRANSACTION.debug(
         "Exception obtaining Atomikos transaction manager " + e.getMessage());
     return null;
   }
 }
  /**
   * Utility that does a discriminator candidate query for the specified candidate and subclasses
   * and returns the class name of the instance that has the specified identity (if any).
   *
   * @param storeMgr RDBMS StoreManager
   * @param ec execution context
   * @param id The id
   * @param cmd Metadata for the root candidate class
   * @return Name of the class with this identity (or null if none found)
   */
  public static String getClassNameForIdUsingDiscriminator(
      RDBMSStoreManager storeMgr, ExecutionContext ec, Object id, AbstractClassMetaData cmd) {
    // Check for input error
    if (cmd == null || id == null) {
      return null;
    }

    SQLExpressionFactory exprFactory = storeMgr.getSQLExpressionFactory();
    ClassLoaderResolver clr = ec.getClassLoaderResolver();
    DatastoreClass primaryTable = storeMgr.getDatastoreClass(cmd.getFullClassName(), clr);

    // Form the query to find which one of these classes has the instance with this id
    DiscriminatorStatementGenerator stmtGen =
        new DiscriminatorStatementGenerator(
            storeMgr, clr, clr.classForName(cmd.getFullClassName()), true, null, null);
    stmtGen.setOption(SelectStatementGenerator.OPTION_RESTRICT_DISCRIM);
    SelectStatement sqlStmt = stmtGen.getStatement();

    // Select the discriminator
    JavaTypeMapping discrimMapping = primaryTable.getDiscriminatorMapping(true);
    SQLTable discrimSqlTbl =
        SQLStatementHelper.getSQLTableForMappingOfTable(
            sqlStmt, sqlStmt.getPrimaryTable(), discrimMapping);
    sqlStmt.select(discrimSqlTbl, discrimMapping, null);

    // Restrict to this id
    JavaTypeMapping idMapping = primaryTable.getIdMapping();
    JavaTypeMapping idParamMapping = new PersistableIdMapping((PersistableMapping) idMapping);
    SQLExpression sqlFldExpr =
        exprFactory.newExpression(sqlStmt, sqlStmt.getPrimaryTable(), idMapping);
    SQLExpression sqlFldVal = exprFactory.newLiteralParameter(sqlStmt, idParamMapping, id, "ID");
    sqlStmt.whereAnd(sqlFldExpr.eq(sqlFldVal), true);

    // Perform the query
    try {
      ManagedConnection mconn = storeMgr.getConnection(ec);
      SQLController sqlControl = storeMgr.getSQLController();
      if (ec.getSerializeReadForClass(cmd.getFullClassName())) {
        sqlStmt.addExtension(SQLStatement.EXTENSION_LOCK_FOR_UPDATE, true);
      }

      try {
        PreparedStatement ps =
            SQLStatementHelper.getPreparedStatementForSQLStatement(sqlStmt, ec, mconn, null, null);
        String statement = sqlStmt.getSQLText().toSQL();
        try {
          ResultSet rs = sqlControl.executeStatementQuery(ec, mconn, statement, ps);
          try {
            if (rs != null) {
              while (rs.next()) {
                DiscriminatorMetaData dismd = discrimMapping.getTable().getDiscriminatorMetaData();
                return RDBMSQueryUtils.getClassNameFromDiscriminatorResultSetRow(
                    discrimMapping, dismd, rs, ec);
              }
            }
          } finally {
            rs.close();
          }
        } finally {
          sqlControl.closeStatement(mconn, ps);
        }
      } finally {
        mconn.release();
      }
    } catch (SQLException sqe) {
      NucleusLogger.DATASTORE.error("Exception thrown on querying of discriminator for id", sqe);
      throw new NucleusDataStoreException(sqe.toString(), sqe);
    }

    return null;
  }
  /**
   * Utility that does a union candidate query for the specified candidate(s) and subclasses and
   * returns the class name of the instance that has the specified identity (if any).
   *
   * @param storeMgr RDBMS StoreManager
   * @param ec execution context
   * @param id The id
   * @param rootCmds Metadata for the classes at the root
   * @return Name of the class with this identity (or null if none found)
   */
  public static String getClassNameForIdUsingUnion(
      RDBMSStoreManager storeMgr,
      ExecutionContext ec,
      Object id,
      List<AbstractClassMetaData> rootCmds) {
    // Check for input error
    if (rootCmds == null || rootCmds.isEmpty() || id == null) {
      return null;
    }

    SQLExpressionFactory exprFactory = storeMgr.getSQLExpressionFactory();
    ClassLoaderResolver clr = ec.getClassLoaderResolver();

    // Form a query UNIONing all possible root candidates (and their subclasses)
    Iterator<AbstractClassMetaData> rootCmdIter = rootCmds.iterator();

    AbstractClassMetaData sampleCmd =
        null; // Metadata for sample class in the tree so we can check if needs locking
    SelectStatement sqlStmtMain = null;
    while (rootCmdIter.hasNext()) {
      AbstractClassMetaData rootCmd = rootCmdIter.next();
      DatastoreClass rootTbl = storeMgr.getDatastoreClass(rootCmd.getFullClassName(), clr);
      if (rootTbl == null) {
        // Class must be using "subclass-table" (no table of its own) so find where it is
        AbstractClassMetaData[] subcmds = storeMgr.getClassesManagingTableForClass(rootCmd, clr);
        if (subcmds == null || subcmds.length == 0) {
          // No table for this class so ignore
        } else {
          for (int i = 0; i < subcmds.length; i++) {
            UnionStatementGenerator stmtGen =
                new UnionStatementGenerator(
                    storeMgr,
                    clr,
                    clr.classForName(subcmds[i].getFullClassName()),
                    true,
                    null,
                    null);
            stmtGen.setOption(SelectStatementGenerator.OPTION_SELECT_NUCLEUS_TYPE);
            if (sqlStmtMain == null) {
              sampleCmd = subcmds[i];
              sqlStmtMain = stmtGen.getStatement();

              // WHERE (object id) = ?
              JavaTypeMapping idMapping = sqlStmtMain.getPrimaryTable().getTable().getIdMapping();
              JavaTypeMapping idParamMapping =
                  new PersistableIdMapping((PersistableMapping) idMapping);
              SQLExpression fieldExpr =
                  exprFactory.newExpression(sqlStmtMain, sqlStmtMain.getPrimaryTable(), idMapping);
              SQLExpression fieldVal =
                  exprFactory.newLiteralParameter(sqlStmtMain, idParamMapping, id, "ID");
              sqlStmtMain.whereAnd(fieldExpr.eq(fieldVal), true);
            } else {
              SelectStatement sqlStmt = stmtGen.getStatement();

              // WHERE (object id) = ?
              JavaTypeMapping idMapping = sqlStmt.getPrimaryTable().getTable().getIdMapping();
              JavaTypeMapping idParamMapping =
                  new PersistableIdMapping((PersistableMapping) idMapping);
              SQLExpression fieldExpr =
                  exprFactory.newExpression(sqlStmt, sqlStmt.getPrimaryTable(), idMapping);
              SQLExpression fieldVal =
                  exprFactory.newLiteralParameter(sqlStmt, idParamMapping, id, "ID");
              sqlStmt.whereAnd(fieldExpr.eq(fieldVal), true);

              sqlStmtMain.union(sqlStmt);
            }
          }
        }
      } else {
        UnionStatementGenerator stmtGen =
            new UnionStatementGenerator(
                storeMgr, clr, clr.classForName(rootCmd.getFullClassName()), true, null, null);
        stmtGen.setOption(SelectStatementGenerator.OPTION_SELECT_NUCLEUS_TYPE);
        if (sqlStmtMain == null) {
          sampleCmd = rootCmd;
          sqlStmtMain = stmtGen.getStatement();

          // WHERE (object id) = ?
          JavaTypeMapping idMapping = sqlStmtMain.getPrimaryTable().getTable().getIdMapping();
          JavaTypeMapping idParamMapping = new PersistableIdMapping((PersistableMapping) idMapping);
          SQLExpression fieldExpr =
              exprFactory.newExpression(sqlStmtMain, sqlStmtMain.getPrimaryTable(), idMapping);
          SQLExpression fieldVal =
              exprFactory.newLiteralParameter(sqlStmtMain, idParamMapping, id, "ID");
          sqlStmtMain.whereAnd(fieldExpr.eq(fieldVal), true);
        } else {
          SelectStatement sqlStmt = stmtGen.getStatement();

          // WHERE (object id) = ?
          JavaTypeMapping idMapping = sqlStmt.getPrimaryTable().getTable().getIdMapping();
          JavaTypeMapping idParamMapping = new PersistableIdMapping((PersistableMapping) idMapping);
          SQLExpression fieldExpr =
              exprFactory.newExpression(sqlStmt, sqlStmt.getPrimaryTable(), idMapping);
          SQLExpression fieldVal =
              exprFactory.newLiteralParameter(sqlStmt, idParamMapping, id, "ID");
          sqlStmt.whereAnd(fieldExpr.eq(fieldVal), true);

          sqlStmtMain.union(sqlStmt);
        }
      }
    }

    // Perform the query
    try {
      ManagedConnection mconn = storeMgr.getConnection(ec);
      SQLController sqlControl = storeMgr.getSQLController();
      if (ec.getSerializeReadForClass(sampleCmd.getFullClassName())) {
        sqlStmtMain.addExtension(SQLStatement.EXTENSION_LOCK_FOR_UPDATE, true);
      }

      try {
        PreparedStatement ps =
            SQLStatementHelper.getPreparedStatementForSQLStatement(
                sqlStmtMain, ec, mconn, null, null);
        String statement = sqlStmtMain.getSQLText().toSQL();
        try {
          ResultSet rs = sqlControl.executeStatementQuery(ec, mconn, statement, ps);
          try {
            if (rs != null) {
              while (rs.next()) {
                try {
                  return rs.getString(UnionStatementGenerator.NUC_TYPE_COLUMN).trim();
                } catch (SQLException sqle) {
                }
              }
            }
          } finally {
            rs.close();
          }
        } finally {
          sqlControl.closeStatement(mconn, ps);
        }
      } finally {
        mconn.release();
      }
    } catch (SQLException sqe) {
      NucleusLogger.DATASTORE.error("Exception with UNION statement", sqe);
      throw new NucleusDataStoreException(sqe.toString());
    }

    return null;
  }
  /**
   * Method to initialise the table definition.
   *
   * @param clr The ClassLoaderResolver
   */
  public void initialize(ClassLoaderResolver clr) {
    assertIsUninitialized();

    MapMetaData mapmd = mmd.getMap();
    if (mapmd == null) {
      throw new NucleusUserException(Localiser.msg("057017", mmd));
    }

    PrimaryKeyMetaData pkmd =
        (mmd.getJoinMetaData() != null ? mmd.getJoinMetaData().getPrimaryKeyMetaData() : null);
    boolean pkColsSpecified = (pkmd != null && pkmd.getColumnMetaData() != null);
    boolean pkRequired = requiresPrimaryKey();

    // Add owner mapping
    ColumnMetaData[] ownerColmd = null;
    if (mmd.getJoinMetaData() != null
        && mmd.getJoinMetaData().getColumnMetaData() != null
        && mmd.getJoinMetaData().getColumnMetaData().length > 0) {
      // Column mappings defined at this side (1-N, M-N)
      // When specified at this side they use the <join> tag
      ownerColmd = mmd.getJoinMetaData().getColumnMetaData();
    }
    ownerMapping =
        ColumnCreator.createColumnsForJoinTables(
            clr.classForName(ownerType),
            mmd,
            ownerColmd,
            storeMgr,
            this,
            pkRequired,
            false,
            FieldRole.ROLE_OWNER,
            clr);
    if (NucleusLogger.DATASTORE.isDebugEnabled()) {
      logMapping(mmd.getFullFieldName() + ".[OWNER]", ownerMapping);
    }

    String keyValueFieldName =
        (mmd.getKeyMetaData() != null ? mmd.getKeyMetaData().getMappedBy() : null);
    String valueKeyFieldName =
        (mmd.getValueMetaData() != null ? mmd.getValueMetaData().getMappedBy() : null);

    // Add key mapping
    boolean keyPC = (mmd.hasMap() && mmd.getMap().keyIsPersistent());
    Class keyCls = clr.classForName(mapmd.getKeyType());
    if (keyValueFieldName != null && isEmbeddedValuePC()) {
      // Added in value code
    } else if (isSerialisedKey()
        || isEmbeddedKeyPC()
        || (isEmbeddedKey() && !keyPC)
        || ClassUtils.isReferenceType(keyCls)) {
      // Key = PC(embedded), PC(serialised), Non-PC(serialised), Non-PC(embedded), Reference
      keyMapping = storeMgr.getMappingManager().getMapping(this, mmd, clr, FieldRole.ROLE_MAP_KEY);
      if (Boolean.TRUE.equals(mmd.getContainer().allowNulls())) {
        // Make all key col(s) nullable so we can store null elements
        for (int i = 0; i < keyMapping.getNumberOfDatastoreMappings(); i++) {
          Column elementCol = keyMapping.getDatastoreMapping(i).getColumn();
          elementCol.setNullable(true);
        }
      }
      if (NucleusLogger.DATASTORE.isDebugEnabled()) {
        logMapping(mmd.getFullFieldName() + ".[KEY]", keyMapping);
      }
      if (valueKeyFieldName != null && isEmbeddedKeyPC()) {
        // Key (PC) is embedded and value is a field of the key
        EmbeddedKeyPCMapping embMapping = (EmbeddedKeyPCMapping) keyMapping;
        valueMapping = embMapping.getJavaTypeMapping(valueKeyFieldName);
      }
    } else {
      // Key = PC
      ColumnMetaData[] keyColmd = null;
      KeyMetaData keymd = mmd.getKeyMetaData();
      if (keymd != null
          && keymd.getColumnMetaData() != null
          && keymd.getColumnMetaData().length > 0) {
        // Column mappings defined at this side (1-N, M-N)
        keyColmd = keymd.getColumnMetaData();
      }
      keyMapping =
          ColumnCreator.createColumnsForJoinTables(
              keyCls, mmd, keyColmd, storeMgr, this, false, false, FieldRole.ROLE_MAP_KEY, clr);
      if (mmd.getContainer().allowNulls() == Boolean.TRUE) {
        // Make all key col(s) nullable so we can store null elements
        for (int i = 0; i < keyMapping.getNumberOfDatastoreMappings(); i++) {
          Column elementCol = keyMapping.getDatastoreMapping(i).getColumn();
          elementCol.setNullable(true);
        }
      }
      if (NucleusLogger.DATASTORE.isDebugEnabled()) {
        logMapping(mmd.getFullFieldName() + ".[KEY]", keyMapping);
      }
    }

    // Add value mapping
    boolean valuePC = (mmd.hasMap() && mmd.getMap().valueIsPersistent());
    Class valueCls = clr.classForName(mapmd.getValueType());
    if (valueKeyFieldName != null && isEmbeddedKeyPC()) {
      // Added in key code
    } else if (isSerialisedValue()
        || isEmbeddedValuePC()
        || (isEmbeddedValue() && !valuePC)
        || ClassUtils.isReferenceType(valueCls)) {
      // Value = PC(embedded), PC(serialised), Non-PC(serialised), Non-PC(embedded), Reference
      valueMapping =
          storeMgr.getMappingManager().getMapping(this, mmd, clr, FieldRole.ROLE_MAP_VALUE);
      if (mmd.getContainer().allowNulls() == Boolean.TRUE) {
        // Make all value col(s) nullable so we can store null elements
        for (int i = 0; i < valueMapping.getNumberOfDatastoreMappings(); i++) {
          Column elementCol = valueMapping.getDatastoreMapping(i).getColumn();
          elementCol.setNullable(true);
        }
      }
      if (NucleusLogger.DATASTORE.isDebugEnabled()) {
        logMapping(mmd.getFullFieldName() + ".[VALUE]", valueMapping);
      }
      if (keyValueFieldName != null && isEmbeddedValuePC()) {
        // Value (PC) is embedded and key is a field of the value
        EmbeddedValuePCMapping embMapping = (EmbeddedValuePCMapping) valueMapping;
        keyMapping = embMapping.getJavaTypeMapping(keyValueFieldName);
      }
    } else {
      // Value = PC
      ColumnMetaData[] valueColmd = null;
      ValueMetaData valuemd = mmd.getValueMetaData();
      if (valuemd != null
          && valuemd.getColumnMetaData() != null
          && valuemd.getColumnMetaData().length > 0) {
        // Column mappings defined at this side (1-N, M-N)
        valueColmd = valuemd.getColumnMetaData();
      }
      valueMapping =
          ColumnCreator.createColumnsForJoinTables(
              clr.classForName(mapmd.getValueType()),
              mmd,
              valueColmd,
              storeMgr,
              this,
              false,
              true,
              FieldRole.ROLE_MAP_VALUE,
              clr);
      if (mmd.getContainer().allowNulls() == Boolean.TRUE) {
        // Make all value col(s) nullable so we can store null elements
        for (int i = 0; i < valueMapping.getNumberOfDatastoreMappings(); i++) {
          Column elementCol = valueMapping.getDatastoreMapping(i).getColumn();
          elementCol.setNullable(true);
        }
      }
      if (NucleusLogger.DATASTORE.isDebugEnabled()) {
        logMapping(mmd.getFullFieldName() + ".[VALUE]", valueMapping);
      }
    }

    // Add order mapping if required
    boolean orderRequired = false;
    if (mmd.getOrderMetaData() != null) {
      // User requested order column so add one
      orderRequired = true;
    } else if (requiresPrimaryKey() && !pkColsSpecified) {
      // PK is required so maybe need to add an index to form the PK
      if (isEmbeddedKeyPC()) {
        if (mmd.getMap().getKeyClassMetaData(clr, storeMgr.getMetaDataManager()).getIdentityType()
            != IdentityType.APPLICATION) {
          // Embedded key PC with datastore id so we need an index to form the PK
          orderRequired = true;
        }
      } else if (isSerialisedKey()) {
        // Serialised key, so need an index to form the PK
        orderRequired = true;
      } else if (keyMapping instanceof ReferenceMapping) {
        // ReferenceMapping, so have order if more than 1 implementation
        ReferenceMapping refMapping = (ReferenceMapping) keyMapping;
        if (refMapping.getJavaTypeMapping().length > 1) {
          orderRequired = true;
        }
      } else if (!(keyMapping instanceof PersistableMapping)) {
        // Non-PC, so depends if the key column can be used as part of a PK
        // TODO This assumes the keyMapping has a single column but what if it is Color with 4 cols?
        Column elementCol = keyMapping.getDatastoreMapping(0).getColumn();
        if (!storeMgr.getDatastoreAdapter().isValidPrimaryKeyType(elementCol.getJdbcType())) {
          // Not possible to use this Non-PC type as part of the PK
          orderRequired = true;
        }
      }
    }
    if (orderRequired) {
      // Order/Adapter (index) column is required (integer based)
      ColumnMetaData orderColmd = null;
      if (mmd.getOrderMetaData() != null
          && mmd.getOrderMetaData().getColumnMetaData() != null
          && mmd.getOrderMetaData().getColumnMetaData().length > 0) {
        // Specified "order" column info
        orderColmd = mmd.getOrderMetaData().getColumnMetaData()[0];
        if (orderColmd.getName() == null) {
          // No column name so use default
          orderColmd = new ColumnMetaData(orderColmd);
          DatastoreIdentifier id = storeMgr.getIdentifierFactory().newIndexFieldIdentifier(mmd);
          orderColmd.setName(id.getName());
        }
      } else {
        // No column name so use default
        DatastoreIdentifier id = storeMgr.getIdentifierFactory().newIndexFieldIdentifier(mmd);
        orderColmd = new ColumnMetaData();
        orderColmd.setName(id.getName());
      }
      orderMapping =
          storeMgr
              .getMappingManager()
              .getMapping(int.class); // JDO2 spec [18.5] order column is assumed to be "int"
      ColumnCreator.createIndexColumn(
          orderMapping, storeMgr, clr, this, orderColmd, pkRequired && !pkColsSpecified);
      if (NucleusLogger.DATASTORE.isDebugEnabled()) {
        logMapping(mmd.getFullFieldName() + ".[ORDER]", orderMapping);
      }
    }

    // Define primary key of the join table (if any)
    if (pkRequired) {
      if (pkColsSpecified) {
        // Apply the users PK specification
        applyUserPrimaryKeySpecification(pkmd);
      } else {
        // Define PK using internal rules
        if (orderRequired) {
          // Order column specified so owner+order are the PK
          orderMapping.getDatastoreMapping(0).getColumn().setPrimaryKey();
        } else {
          // No order column specified so owner+key are the PK
          for (int i = 0; i < keyMapping.getNumberOfDatastoreMappings(); i++) {
            keyMapping.getDatastoreMapping(i).getColumn().setPrimaryKey();
          }
        }
      }
    }

    if (NucleusLogger.DATASTORE_SCHEMA.isDebugEnabled()) {
      NucleusLogger.DATASTORE_SCHEMA.debug(Localiser.msg("057023", this));
    }
    storeMgr.registerTableInitialized(this);
    state = TABLE_STATE_INITIALIZED;
  }
Example #8
0
  /**
   * Method to populate any defaults, and check the validity of the MetaData.
   *
   * @param clr ClassLoaderResolver to use for loading any key/value types
   * @param primary the primary ClassLoader to use (or null)
   * @param mmgr MetaData manager
   */
  public void populate(ClassLoaderResolver clr, ClassLoader primary, MetaDataManager mmgr) {
    AbstractMemberMetaData mmd = (AbstractMemberMetaData) parent;
    if (!StringUtils.isWhitespace(key.type) && key.type.indexOf(',') > 0) {
      throw new InvalidMetaDataException(LOCALISER, "044143", mmd.getName(), mmd.getClassName());
    }
    if (!StringUtils.isWhitespace(value.type) && value.type.indexOf(',') > 0) {
      throw new InvalidMetaDataException(LOCALISER, "044144", mmd.getName(), mmd.getClassName());
    }

    ApiAdapter api = mmgr.getApiAdapter();

    // Make sure the type in "key", "value" is set
    key.populate(
        ((AbstractMemberMetaData) parent).getAbstractClassMetaData().getPackageName(),
        clr,
        primary,
        mmgr);
    value.populate(
        ((AbstractMemberMetaData) parent).getAbstractClassMetaData().getPackageName(),
        clr,
        primary,
        mmgr);

    // Check the field type and see if it is castable to a Map
    Class field_type = getMemberMetaData().getType();
    if (!java.util.Map.class.isAssignableFrom(field_type)) {
      throw new InvalidMetaDataException(
          LOCALISER, "044145", getFieldName(), getMemberMetaData().getClassName(false));
    }

    if (java.util.Properties.class.isAssignableFrom(field_type)) {
      // Properties defaults to <String, String>
      if (key.type == null) {
        key.type = String.class.getName();
      }
      if (value.type == null) {
        value.type = String.class.getName();
      }
    }

    // "key-type"
    if (key.type == null) {
      throw new InvalidMetaDataException(
          LOCALISER, "044146", getFieldName(), getMemberMetaData().getClassName(false));
    }

    // Check that the key type exists
    Class keyTypeClass = null;
    try {
      keyTypeClass = clr.classForName(key.type, primary);
    } catch (ClassNotResolvedException cnre) {
      try {
        // Maybe the user specified a java.lang class without fully-qualifying it
        // This is beyond the scope of the JDO spec which expects java.lang cases to be
        // fully-qualified
        keyTypeClass = clr.classForName(ClassUtils.getJavaLangClassForType(key.type), primary);
      } catch (ClassNotResolvedException cnre2) {
        throw new InvalidMetaDataException(
            LOCALISER, "044147", getFieldName(), getMemberMetaData().getClassName(false), key.type);
      }
    }

    if (!keyTypeClass.getName().equals(key.type)) {
      // The value-type has been resolved from what was specified in the MetaData - update to the
      // fully-qualified name
      NucleusLogger.METADATA.info(
          LOCALISER.msg(
              "044148",
              getFieldName(),
              getMemberMetaData().getClassName(false),
              key.type,
              keyTypeClass.getName()));
      key.type = keyTypeClass.getName();
    }

    // "embedded-key"
    if (key.embedded == null) {
      // Assign default for "embedded-key" based on 18.13.2 of JDO 2 spec
      if (mmgr.getNucleusContext().getTypeManager().isDefaultEmbeddedType(keyTypeClass)) {
        key.embedded = Boolean.TRUE;
      } else if (api.isPersistable(keyTypeClass)
          || Object.class.isAssignableFrom(keyTypeClass)
          || keyTypeClass.isInterface()) {
        key.embedded = Boolean.FALSE;
      } else {
        key.embedded = Boolean.TRUE;
      }
    }
    if (Boolean.FALSE.equals(key.embedded)) {
      // If the user has set a non-PC/non-Interface as not embedded, correct it since not supported.
      // Note : this fails when using in the enhancer since not yet PC
      if (!api.isPersistable(keyTypeClass)
          && !keyTypeClass.isInterface()
          && keyTypeClass != java.lang.Object.class) {
        key.embedded = Boolean.TRUE;
      }
    }
    KeyMetaData keymd = ((AbstractMemberMetaData) parent).getKeyMetaData();
    if (keymd != null && keymd.getEmbeddedMetaData() != null) {
      // If the user has specified <embedded>, set to true
      key.embedded = Boolean.TRUE;
    }

    // "value-type"
    if (value.type == null) {
      throw new InvalidMetaDataException(
          LOCALISER, "044149", getFieldName(), getMemberMetaData().getClassName(false));
    }

    // Check that the value-type exists
    Class valueTypeClass = null;
    try {
      valueTypeClass = clr.classForName(value.type);
    } catch (ClassNotResolvedException cnre) {
      try {
        // Maybe the user specified a java.lang class without fully-qualifying it
        // This is beyond the scope of the JDO spec which expects java.lang cases to be
        // fully-qualified
        valueTypeClass = clr.classForName(ClassUtils.getJavaLangClassForType(value.type));
      } catch (ClassNotResolvedException cnre2) {
        throw new InvalidMetaDataException(
            LOCALISER,
            "044150",
            getFieldName(),
            getMemberMetaData().getClassName(false),
            value.type);
      }
    }

    if (!valueTypeClass.getName().equals(value.type)) {
      // The value-type has been resolved from what was specified in the MetaData - update to the
      // fully-qualified name
      NucleusLogger.METADATA.info(
          LOCALISER.msg(
              "044151",
              getFieldName(),
              getMemberMetaData().getClassName(false),
              value.type,
              valueTypeClass.getName()));
      value.type = valueTypeClass.getName();
    }

    // "embedded-value"
    if (value.embedded == null) {
      // Assign default for "embedded-value" based on 18.13.2 of JDO 2 spec
      if (mmgr.getNucleusContext().getTypeManager().isDefaultEmbeddedType(valueTypeClass)) {
        value.embedded = Boolean.TRUE;
      } else if (api.isPersistable(valueTypeClass)
          || Object.class.isAssignableFrom(valueTypeClass)
          || valueTypeClass.isInterface()) {
        value.embedded = Boolean.FALSE;
      } else {
        value.embedded = Boolean.TRUE;
      }
    }
    if (value.embedded == Boolean.FALSE) {
      // If the user has set a non-PC/non-Interface as not embedded, correct it since not supported.
      // Note : this fails when using in the enhancer since not yet PC
      if (!api.isPersistable(valueTypeClass)
          && !valueTypeClass.isInterface()
          && valueTypeClass != java.lang.Object.class) {
        value.embedded = Boolean.TRUE;
      }
    }
    ValueMetaData valuemd = ((AbstractMemberMetaData) parent).getValueMetaData();
    if (valuemd != null && valuemd.getEmbeddedMetaData() != null) {
      // If the user has specified <embedded>, set to true
      value.embedded = Boolean.TRUE;
    }

    key.classMetaData = mmgr.getMetaDataForClassInternal(keyTypeClass, clr);
    value.classMetaData = mmgr.getMetaDataForClassInternal(valueTypeClass, clr);

    // Cater for Key with mapped-by needing to be PK (for JPA)
    if (keymd != null
        && keymd.mappedBy != null
        && keymd.mappedBy.equals("#PK")) // Special value set by JPAMetaDataHandler
    {
      // Need to set the mapped-by of <key> to be the PK of the <value>
      if (value.classMetaData.getNoOfPrimaryKeyMembers() != 1) {
        // TODO Localise this
        throw new NucleusUserException(
            "DataNucleus does not support use of <map-key> with no name field when the"
                + " value class has a composite primary key");
      }
      int[] valuePkFieldNums = value.classMetaData.getPKMemberPositions();
      keymd.mappedBy =
          value.classMetaData.getMetaDataForManagedMemberAtAbsolutePosition(valuePkFieldNums[0])
              .name;
    }

    // Make sure anything in the superclass is populated too
    super.populate(clr, primary, mmgr);

    setPopulated();
  }
  /**
   * Utility to create the application identity columns and mapping. Uses the id mapping of the
   * specified class table and copies the mappings and columns, whilst retaining the passed
   * preferences for column namings. This is used to copy the PK mappings of a superclass table so
   * we have the same PK.
   *
   * @param columnContainer The container of column MetaData with any namings
   * @param refTable The table that we use as reference
   * @param clr The ClassLoaderResolver
   * @param cmd The ClassMetaData
   */
  final void addApplicationIdUsingClassTableId(
      ColumnMetaDataContainer columnContainer,
      DatastoreClass refTable,
      ClassLoaderResolver clr,
      AbstractClassMetaData cmd) {
    ColumnMetaData[] userdefinedCols = null;
    int nextUserdefinedCol = 0;
    if (columnContainer != null) {
      userdefinedCols = columnContainer.getColumnMetaData();
    }

    pkMappings = new JavaTypeMapping[cmd.getPKMemberPositions().length];
    for (int i = 0; i < cmd.getPKMemberPositions().length; i++) {
      AbstractMemberMetaData mmd =
          cmd.getMetaDataForManagedMemberAtAbsolutePosition(cmd.getPKMemberPositions()[i]);
      JavaTypeMapping mapping = refTable.getMemberMapping(mmd);
      if (mapping == null) {
        // probably due to invalid metadata defined by the user
        throw new NucleusUserException(
            "Cannot find mapping for field "
                + mmd.getFullFieldName()
                + " in table "
                + refTable.toString()
                + " "
                + StringUtils.collectionToString(refTable.getColumns()));
      }

      JavaTypeMapping masterMapping =
          storeMgr.getMappingManager().getMapping(clr.classForName(mapping.getType()));
      masterMapping.setMemberMetaData(mmd); // Update field info in mapping
      masterMapping.setTable(this);
      pkMappings[i] = masterMapping;

      // Loop through each id column in the reference table and add the same here
      // applying the required names from the columnContainer
      for (int j = 0; j < mapping.getNumberOfDatastoreMappings(); j++) {
        JavaTypeMapping m = masterMapping;
        Column refColumn = mapping.getDatastoreMapping(j).getColumn();
        if (mapping instanceof PersistableMapping) {
          m =
              storeMgr
                  .getMappingManager()
                  .getMapping(clr.classForName(refColumn.getJavaTypeMapping().getType()));
          ((PersistableMapping) masterMapping).addJavaTypeMapping(m);
        }

        ColumnMetaData userdefinedColumn = null;
        if (userdefinedCols != null) {
          for (int k = 0; k < userdefinedCols.length; k++) {
            if (refColumn.getIdentifier().toString().equals(userdefinedCols[k].getTarget())) {
              userdefinedColumn = userdefinedCols[k];
              break;
            }
          }
          if (userdefinedColumn == null && nextUserdefinedCol < userdefinedCols.length) {
            userdefinedColumn = userdefinedCols[nextUserdefinedCol++];
          }
        }

        // Add this application identity column
        Column idColumn = null;
        if (userdefinedColumn != null) {
          // User has provided a name for this column
          // Currently we only use the column namings from the users definition but we could easily
          // take more of their details.
          idColumn =
              addColumn(
                  refColumn.getStoredJavaType(),
                  storeMgr
                      .getIdentifierFactory()
                      .newIdentifier(IdentifierType.COLUMN, userdefinedColumn.getName()),
                  m,
                  refColumn.getColumnMetaData());
        } else {
          // No name provided so take same as superclass
          idColumn =
              addColumn(
                  refColumn.getStoredJavaType(),
                  refColumn.getIdentifier(),
                  m,
                  refColumn.getColumnMetaData());
        }
        if (mapping.getDatastoreMapping(j).getColumn().getColumnMetaData() != null) {
          refColumn.copyConfigurationTo(idColumn);
        }
        idColumn.setPrimaryKey();

        // Set the column type based on the field.getType()
        getStoreManager()
            .getMappingManager()
            .createDatastoreMapping(m, idColumn, refColumn.getJavaTypeMapping().getType());
      }

      // Update highest field number if this is higher
      int absoluteFieldNumber = mmd.getAbsoluteFieldNumber();
      if (absoluteFieldNumber > highestMemberNumber) {
        highestMemberNumber = absoluteFieldNumber;
      }
    }
  }
  /**
   * Method to return a statement selecting the candidate table(s) required to cover all possible
   * types for this candidates inheritance strategy.
   *
   * @param storeMgr RDBMS StoreManager
   * @param parentStmt Parent statement (if there is one)
   * @param cmd Metadata for the class
   * @param clsMapping Mapping for the results of the statement
   * @param ec ExecutionContext
   * @param candidateCls Candidate class
   * @param subclasses Whether to create a statement for subclasses of the candidate too
   * @param result The result clause
   * @param candidateAlias alias for the candidate (if any)
   * @param candidateTableGroupName TableGroup name for the candidate (if any)
   * @return The SQLStatement
   * @throws NucleusException if there are no tables for concrete classes in this query (hence would
   *     return null)
   */
  public static SQLStatement getStatementForCandidates(
      RDBMSStoreManager storeMgr,
      SQLStatement parentStmt,
      AbstractClassMetaData cmd,
      StatementClassMapping clsMapping,
      ExecutionContext ec,
      Class candidateCls,
      boolean subclasses,
      String result,
      String candidateAlias,
      String candidateTableGroupName) {
    SQLStatement stmt = null;

    DatastoreIdentifier candidateAliasId = null;
    if (candidateAlias != null) {
      candidateAliasId = storeMgr.getIdentifierFactory().newTableIdentifier(candidateAlias);
    }

    ClassLoaderResolver clr = ec.getClassLoaderResolver();
    List<DatastoreClass> candidateTables = new ArrayList<DatastoreClass>();
    if (cmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.COMPLETE_TABLE) {
      DatastoreClass candidateTable = storeMgr.getDatastoreClass(cmd.getFullClassName(), clr);
      if (candidateTable != null) {
        candidateTables.add(candidateTable);
      }
      if (subclasses) {
        Collection<String> subclassNames =
            storeMgr.getSubClassesForClass(cmd.getFullClassName(), subclasses, clr);
        if (subclassNames != null) {
          Iterator<String> subclassIter = subclassNames.iterator();
          while (subclassIter.hasNext()) {
            String subclassName = subclassIter.next();
            DatastoreClass tbl = storeMgr.getDatastoreClass(subclassName, clr);
            if (tbl != null) {
              candidateTables.add(tbl);
            }
          }
        }
      }

      Iterator<DatastoreClass> iter = candidateTables.iterator();
      int maxClassNameLength = cmd.getFullClassName().length();
      while (iter.hasNext()) {
        DatastoreClass cls = iter.next();
        String className = cls.getType();
        if (className.length() > maxClassNameLength) {
          maxClassNameLength = className.length();
        }
      }

      iter = candidateTables.iterator();
      while (iter.hasNext()) {
        DatastoreClass cls = iter.next();

        SQLStatement tblStmt =
            new SQLStatement(parentStmt, storeMgr, cls, candidateAliasId, candidateTableGroupName);
        tblStmt.setClassLoaderResolver(clr);
        tblStmt.setCandidateClassName(cls.getType());

        // Add SELECT of dummy column accessible as "NUCLEUS_TYPE" containing the classname
        JavaTypeMapping m = storeMgr.getMappingManager().getMapping(String.class);
        String nuctypeName = cls.getType();
        if (maxClassNameLength > nuctypeName.length()) {
          nuctypeName = StringUtils.leftAlignedPaddedString(nuctypeName, maxClassNameLength);
        }
        StringLiteral lit = new StringLiteral(tblStmt, m, nuctypeName, null);
        tblStmt.select(lit, UnionStatementGenerator.NUC_TYPE_COLUMN);

        if (stmt == null) {
          stmt = tblStmt;
        } else {
          stmt.union(tblStmt);
        }
      }
      if (clsMapping != null) {
        clsMapping.setNucleusTypeColumnName(UnionStatementGenerator.NUC_TYPE_COLUMN);
      }
    } else {
      // "new-table", "superclass-table", "subclass-table"
      List<Class> candidateClasses = new ArrayList<Class>();
      if (ClassUtils.isReferenceType(candidateCls)) {
        // Persistent interface, so find all persistent implementations
        String[] clsNames =
            storeMgr
                .getNucleusContext()
                .getMetaDataManager()
                .getClassesImplementingInterface(candidateCls.getName(), clr);
        for (int i = 0; i < clsNames.length; i++) {
          Class cls = clr.classForName(clsNames[i]);
          DatastoreClass table = storeMgr.getDatastoreClass(clsNames[i], clr);
          candidateClasses.add(cls);
          candidateTables.add(table);
          AbstractClassMetaData implCmd =
              storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForClass(cls, clr);
          if (implCmd.getIdentityType() != cmd.getIdentityType()) {
            throw new NucleusUserException(
                "You are querying an interface ("
                    + cmd.getFullClassName()
                    + ") "
                    + "yet one of its implementations ("
                    + implCmd.getFullClassName()
                    + ") "
                    + " uses a different identity type!");
          } else if (cmd.getIdentityType() == IdentityType.APPLICATION) {
            if (cmd.getPKMemberPositions().length != implCmd.getPKMemberPositions().length) {
              throw new NucleusUserException(
                  "You are querying an interface ("
                      + cmd.getFullClassName()
                      + ") "
                      + "yet one of its implementations ("
                      + implCmd.getFullClassName()
                      + ") "
                      + " has a different number of PK members!");
            }
          }
        }
      } else {
        DatastoreClass candidateTable = storeMgr.getDatastoreClass(cmd.getFullClassName(), clr);
        if (candidateTable != null) {
          // Candidate has own table
          candidateClasses.add(candidateCls);
          candidateTables.add(candidateTable);
        } else {
          // Candidate stored in subclass tables
          AbstractClassMetaData[] cmds = storeMgr.getClassesManagingTableForClass(cmd, clr);
          if (cmds != null && cmds.length > 0) {
            for (int i = 0; i < cmds.length; i++) {
              DatastoreClass table = storeMgr.getDatastoreClass(cmds[i].getFullClassName(), clr);
              Class cls = clr.classForName(cmds[i].getFullClassName());
              candidateClasses.add(cls);
              candidateTables.add(table);
            }
          } else {
            throw new UnsupportedOperationException(
                "No tables for query of " + cmd.getFullClassName());
          }
        }
      }

      for (int i = 0; i < candidateTables.size(); i++) {
        DatastoreClass tbl = candidateTables.get(i);
        Class cls = candidateClasses.get(i);
        StatementGenerator stmtGen = null;
        if (tbl.getDiscriminatorMapping(true) != null
            || QueryUtils.resultHasOnlyAggregates(result)) {
          // Either has a discriminator, or only selecting aggregates so need single select
          stmtGen =
              new DiscriminatorStatementGenerator(
                  storeMgr, clr, cls, subclasses, candidateAliasId, candidateTableGroupName);
          stmtGen.setOption(StatementGenerator.OPTION_RESTRICT_DISCRIM);
        } else {
          stmtGen =
              new UnionStatementGenerator(
                  storeMgr, clr, cls, subclasses, candidateAliasId, candidateTableGroupName);
          if (result == null) {
            // Returning one row per candidate so include distinguisher column
            stmtGen.setOption(StatementGenerator.OPTION_SELECT_NUCLEUS_TYPE);
            clsMapping.setNucleusTypeColumnName(UnionStatementGenerator.NUC_TYPE_COLUMN);
          }
        }
        stmtGen.setParentStatement(parentStmt);
        SQLStatement tblStmt = stmtGen.getStatement();

        if (stmt == null) {
          stmt = tblStmt;
        } else {
          stmt.union(tblStmt);
        }
      }
    }

    return stmt;
  }
Example #11
0
  /**
   * Method to populate any defaults, and check the validity of the MetaData.
   *
   * @param clr ClassLoaderResolver to use for any loading operations
   * @param primary the primary ClassLoader to use (or null)
   * @param mmgr MetaData manager
   */
  public void populate(ClassLoaderResolver clr, ClassLoader primary, MetaDataManager mmgr) {
    AbstractMemberMetaData mmd = (AbstractMemberMetaData) parent;
    if (!StringUtils.isWhitespace(element.type) && element.type.indexOf(',') > 0) {
      throw new InvalidMetaDataException(LOCALISER, "044131", mmd.getName(), mmd.getClassName());
    }

    // Make sure the type in "element" is set
    element.populate(
        ((AbstractMemberMetaData) parent).getAbstractClassMetaData().getPackageName(),
        clr,
        primary,
        mmgr);

    // Check the field type and see if it is castable to a Collection
    Class field_type = getMemberMetaData().getType();
    if (!java.util.Collection.class.isAssignableFrom(field_type)) {
      throw new InvalidMetaDataException(
          LOCALISER, "044132", getFieldName(), getMemberMetaData().getClassName(false));
    }

    // "element-type"
    if (element.type == null) {
      throw new InvalidMetaDataException(
          LOCALISER, "044133", getFieldName(), getMemberMetaData().getClassName(false));
    }

    // Check that the element type exists
    Class elementTypeClass = null;
    try {
      elementTypeClass = clr.classForName(element.type, primary);
    } catch (ClassNotResolvedException cnre) {
      throw new InvalidMetaDataException(
          LOCALISER,
          "044134",
          getFieldName(),
          getMemberMetaData().getClassName(false),
          element.type);
    }

    if (!elementTypeClass.getName().equals(element.type)) {
      // The element-type has been resolved from what was specified in the MetaData - update to the
      // fully-qualified name
      NucleusLogger.METADATA.info(
          LOCALISER.msg(
              "044135",
              getFieldName(),
              getMemberMetaData().getClassName(false),
              element.type,
              elementTypeClass.getName()));
      element.type = elementTypeClass.getName();
    }

    // "embedded-element"
    ApiAdapter api = mmgr.getApiAdapter();
    if (element.embedded == null) {
      // Assign default for "embedded-element" based on 18.13.1 of JDO 2 spec
      // Note : this fails when using in the enhancer since not yet PC
      if (mmgr.getNucleusContext().getTypeManager().isDefaultEmbeddedType(elementTypeClass)) {
        element.embedded = Boolean.TRUE;
      } else if (api.isPersistable(elementTypeClass)
          || Object.class.isAssignableFrom(elementTypeClass)
          || elementTypeClass.isInterface()) {
        element.embedded = Boolean.FALSE;
      } else {
        element.embedded = Boolean.TRUE;
      }
    }
    if (Boolean.FALSE.equals(element.embedded)) {
      // If the user has set a non-PC/non-Interface as not embedded, correct it since not supported.
      // Note : this fails when using in the enhancer since not yet PC
      if (!api.isPersistable(elementTypeClass)
          && !elementTypeClass.isInterface()
          && elementTypeClass != java.lang.Object.class) {
        element.embedded = Boolean.TRUE;
      }
    }

    ElementMetaData elemmd = ((AbstractMemberMetaData) parent).getElementMetaData();
    if (elemmd != null && elemmd.getEmbeddedMetaData() != null) {
      element.embedded = Boolean.TRUE;
    }

    if (Boolean.TRUE.equals(element.dependent)) {
      // If the user has set a non-PC/non-reference as dependent, correct it since not valid.
      // Note : this fails when using in the enhancer since not yet PC
      if (!api.isPersistable(elementTypeClass)
          && !elementTypeClass.isInterface()
          && elementTypeClass != java.lang.Object.class) {
        element.dependent = Boolean.FALSE;
      }
    }

    // Keep a reference to the MetaData for the element
    element.classMetaData = mmgr.getMetaDataForClassInternal(elementTypeClass, clr);

    // Make sure anything in the superclass is populated too
    super.populate(clr, primary, mmgr);

    setPopulated();
  }