/src/com/google/appengine/datanucleus/DatastoreManager.java
http://datanucleus-appengine.googlecode.com/ · Java · 711 lines · 408 code · 89 blank · 214 comment · 43 complexity · 6d741ab7e3c12429441561bcf6bce138 MD5 · raw file
- /**********************************************************************
- Copyright (c) 2009 Google Inc.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- **********************************************************************/
- package com.google.appengine.datanucleus;
- import com.google.appengine.api.datastore.DatastoreService;
- import com.google.appengine.api.datastore.DatastoreServiceConfig;
- import com.google.appengine.api.datastore.ReadPolicy;
- import com.google.appengine.api.datastore.ReadPolicy.Consistency;
- import com.google.appengine.api.datastore.TransactionOptions;
- import com.google.appengine.datanucleus.mapping.DatastoreTable;
- import com.google.appengine.datanucleus.scostore.FKListStore;
- import com.google.appengine.datanucleus.scostore.FKSetStore;
- import org.datanucleus.ClassLoaderResolver;
- import org.datanucleus.flush.FlushOrdered;
- import org.datanucleus.NucleusContext;
- import org.datanucleus.PersistenceConfiguration;
- import org.datanucleus.PropertyNames;
- import org.datanucleus.api.ApiAdapter;
- import org.datanucleus.exceptions.NucleusFatalUserException;
- import org.datanucleus.metadata.AbstractClassMetaData;
- import org.datanucleus.metadata.AbstractMemberMetaData;
- import org.datanucleus.metadata.ClassMetaData;
- import org.datanucleus.metadata.InheritanceStrategy;
- import org.datanucleus.metadata.RelationType;
- import org.datanucleus.store.DefaultCandidateExtent;
- import org.datanucleus.ExecutionContext;
- import org.datanucleus.store.Extent;
- import org.datanucleus.store.NucleusConnection;
- import org.datanucleus.store.NucleusConnectionImpl;
- import org.datanucleus.state.ObjectProvider;
- import org.datanucleus.store.StoreData;
- import org.datanucleus.store.connection.ConnectionFactory;
- import org.datanucleus.store.connection.ManagedConnection;
- import org.datanucleus.store.exceptions.NoExtentException;
- import org.datanucleus.store.mapped.exceptions.NoTableManagedException;
- import org.datanucleus.store.fieldmanager.FieldManager;
- import org.datanucleus.store.mapped.DatastoreContainerObject;
- import org.datanucleus.store.mapped.MappedStoreData;
- import org.datanucleus.store.mapped.MappedStoreManager;
- import org.datanucleus.store.mapped.StatementClassMapping;
- import org.datanucleus.store.mapped.mapping.JavaTypeMapping;
- import org.datanucleus.store.scostore.ListStore;
- import org.datanucleus.store.scostore.SetStore;
- import org.datanucleus.util.ClassUtils;
- import org.datanucleus.util.Localiser;
- import org.datanucleus.util.NucleusLogger;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.HashSet;
- import java.util.Map;
- import java.util.Set;
- import java.util.concurrent.ConcurrentHashMap;
- /**
- * StoreManager for GAE/J with DataNucleus.
- * When the user specifies "datanucleus.ConnectionURL" as "appengine", an instance of this class is created.
- * <h3>Information stored for each object</h3>
- * <p>
- * When an object of a class is stored, we can store the following information as properties of the Entity.
- * <ul>
- * <li>Primary key (which includes parentKey where required)</li>
- * <li>Discriminator (if defined)</li>
- * <li>Version (if defined)</li>
- * <li>Each non-null field
- * <ul>
- * <li>Unowned relation fields will store the Key(s) of related objects (dependent on StorageVersion)</li>
- * <li>Owned relation fields at parent side will store the Key(s) of child objects (dependent on StorageVersion)</li>
- * <li>Owned relation fields at child side will not store anything about the parent</li>
- * <li>Non-relation fields store the value (as long as supported type).</li>
- * </ul></li>
- * <li>Field(s) of embedded object.</li>
- * </ul>
- * </p>
- * <h3>Transaction Restrictions</h3>
- * <p>
- * The GAE datastore has some restrictions to be aware of when inspecting the code in this plugin. These
- * restrictions impact on the design of the plugin.
- * <ul>
- * <li>When we PUT an object it must have its parent assigned then (create of Entity), and not later. In particular
- * see the handling of owned relations in DatastorePersistenceHandler, and the order of PUTs, making sure we have
- * the parent first before any child is PUT.</li>
- * <li>All objects have to be in the same entity-group, except when using XG transactions.</li>
- * </ul>
- * </p>
- *
- * @author Max Ross <maxr@google.com>
- */
- public class DatastoreManager extends MappedStoreManager {
- protected static final Localiser GAE_LOCALISER = Localiser.getInstance(
- "com.google.appengine.datanucleus.Localisation", DatastoreManager.class.getClassLoader());
- private static final String EXTENSION_PREFIX = "gae.";
- /** The name of the metadata extension that marks a field as a parent. */
- public static final String PARENT_PK = EXTENSION_PREFIX + "parent-pk";
- /** The name of the metadata extension that marks a field as an encoded pk. */
- public static final String ENCODED_PK = EXTENSION_PREFIX + "encoded-pk";
- /** The name of the metadata extension that marks a field as a primary key name. */
- public static final String PK_NAME = EXTENSION_PREFIX + "pk-name";
- /** The name of the metadata extension that marks a field as a primary key id. */
- public static final String PK_ID = EXTENSION_PREFIX + "pk-id";
- /** The name of the metadata extension that marks a field as unindexed. */
- public static final String UNINDEXED_PROPERTY = EXTENSION_PREFIX + "unindexed";
- public static final String DATASTORE_READ_CONSISTENCY_PROPERTY =
- "datanucleus.appengine.datastoreReadConsistency";
- public static final String DATASTORE_ENABLE_XG_TXNS_PROPERTY =
- "datanucleus.appengine.datastoreEnableXGTransactions";
- public static final String GET_EXTENT_CAN_RETURN_SUBCLASSES_PROPERTY =
- "datanucleus.appengine.getExtentCanReturnSubclasses";
- /** Property to set the default type of relations ("owned", "unowned") with default as "owned". */
- public static final String RELATION_DEFAULT_MODE = "datanucleus.appengine.relationDefault";
- /** Property allowing the user to turn off GAE/J-specific validation of metadata and assume its ok. */
- public static final String VALIDATE_METADATA = "datanucleus.appengine.validateMetaData";
- /**
- * A property that is expected to be set to either "String" or "Double". The
- * default is Double.
- *
- * Double indicates that BigDecimals should be stored as a double, and
- * therefore lose precision when stored.
- *
- * String indicates they should be serialized to a string using the
- * {@link BigDecimals} class. This will result in retaining their precision
- * when decoded. It is still possible to use inequality queries against them.
- * For this reason string is generally preferable.
- *
- *
- * Inequality queries for fields where some values are doubles and some are
- * encoded as strings will not work.
- */
- public static final String BIG_DECIMALS_ENCODEING = "datanucleus.appengine.BigDecimalsEncoding";
- /**
- * The name of the extension that indicates the return value of the batch
- * delete query should be as accurate as possible at the expense of
- * fulfilling the query less efficiently. If this extension is set and
- * {@code true}, the query will be fulfilled by first fetching the entities
- * and then deleting those that are returned. This involves an additional
- * roundtrip to the datastore but allows us to return a more accurate count
- * of the number of records that were deleted (we say more accurate as
- * opposed to accurate because it's possible that entity was deleted in
- * between the fetch and the delete and we wouldn't have any way of knowing).
- * If this extension is not set or is set with a value of {@code false},
- * we'll just execute a batch delete directly and use the number of entities
- * we were asked to delete as the return value.
- */
- public static final String QUERYEXT_SLOW_BUT_MORE_ACCURATE_JPQL_DELETE =
- EXTENSION_PREFIX + "slow-but-more-accurate-jpql-delete-query";
- /** Query extension that indicates the query should be excluded from the current transaction. */
- public static final String QUERYEXT_EXCLUDE_FROM_TXN = EXTENSION_PREFIX + "exclude-query-from-txn";
- /** Query extension to enable/disable use of in-memory evaluation when some syntax is unsupported in datastore. */
- public static final String QUERYEXT_INMEMORY_WHEN_UNSUPPORTED = EXTENSION_PREFIX + "inmemory-when-unsupported";
- /**
- * Classes whose metadata we've validated. This set gets hit on every
- * insert, update, and fetch. I don't expect it to be a bottleneck but
- * if we're seeing contention we should look here.
- */
- private final Set<String> validatedClasses = Collections.synchronizedSet(new HashSet<String>());
- /** Map of the metadata for the member of this class storing the parent ("gae.parent-pk") keyed by class name. */
- private final Map<String, AbstractMemberMetaData> parentMemberMetaDataByClass =
- new ConcurrentHashMap<String, AbstractMemberMetaData>();
- private final boolean defaultToOwnedRelations;
- private final TypeConversionUtils typeConversionUtils;
- private final StorageVersion storageVersion;
- private final DatastoreServiceConfig defaultDatastoreServiceConfigPrototypeForReads;
- private final DatastoreServiceConfig defaultDatastoreServiceConfigPrototypeForWrites;
- private final TransactionOptions defaultDatastoreTransactionOptionsPrototype;
- private final DatastoreService datastoreServiceForReads;
- protected SerializationManager serializationMgr = null;
- MetaDataValidator metadataValidator;
- /**
- * Construct a DatastoreManager.
- * @param clr The ClassLoaderResolver
- * @param nucContext The NucleusContext
- * @param props Properties to store on this StoreManager
- */
- public DatastoreManager(ClassLoaderResolver clr, NucleusContext nucContext, Map<String, Object> props)
- throws NoSuchFieldException, IllegalAccessException {
- super("appengine", clr, nucContext, props);
- // Override some of the default property values for AppEngine
- PersistenceConfiguration conf = nucContext.getPersistenceConfiguration();
- conf.setProperty("datanucleus.attachSameDatastore", Boolean.TRUE.toString()); // Always only one datastore
- // We'd like to respect the user's selection here, but the default value is 1.
- // This is problematic for us in the situation where, for example, an embedded object
- // gets updated more than once in a txn because we end up putting the same entity twice.
- // TODO(maxr) Remove this once we support multiple puts
- conf.setProperty("datanucleus.datastoreTransactionFlushLimit", Integer.MAX_VALUE);
- // Install our key translator
- conf.setProperty("datanucleus.identityKeyTranslatorType", "appengine");
- // Check if datastore api is in CLASSPATH. Don't let the hard-coded
- // jar name upset you, it's just used for error messages. The check will
- // succeed so long as the class is available on the classpath
- ClassUtils.assertClassForJarExistsInClasspath(
- clr, "com.google.appengine.api.datastore.DatastoreService", "appengine-api.jar");
- defaultDatastoreServiceConfigPrototypeForReads =
- createDatastoreServiceConfigPrototypeForReads(nucContext.getPersistenceConfiguration());
- defaultDatastoreServiceConfigPrototypeForWrites =
- createDatastoreServiceConfigPrototypeForWrites(nucContext.getPersistenceConfiguration());
- defaultDatastoreTransactionOptionsPrototype =
- createDatastoreTransactionOptionsPrototype(nucContext.getPersistenceConfiguration());
- // Handler for persistence process
- persistenceHandler = new DatastorePersistenceHandler(this);
- flushProcess = new FlushOrdered();
- dba = new DatastoreAdapter();
- initialiseIdentifierFactory(nucContext);
- storageVersion = StorageVersion.fromStoreManager(this);
- String defaultRelationMode = getStringProperty(RELATION_DEFAULT_MODE);
- defaultToOwnedRelations = defaultRelationMode.equalsIgnoreCase("unowned") ? false : true;
- String bigDecimalsEncoding = getStringProperty(BIG_DECIMALS_ENCODEING);
- typeConversionUtils =
- "String".equalsIgnoreCase(bigDecimalsEncoding) ? new TypeConversionUtils(true)
- : new TypeConversionUtils(false);
- // Add listener so we can check all metadata for unsupported features and required schema
- metadataValidator = new MetaDataValidator(this, getMetaDataManager(), clr);
- logConfiguration();
- datastoreServiceForReads = DatastoreServiceFactoryInternal.getDatastoreService(
- getDefaultDatastoreServiceConfigForReads());
- }
- @Override
- public void close() {
- validatedClasses.clear();
- super.close();
- }
- public SerializationManager getSerializationManager() {
- if (serializationMgr == null) {
- serializationMgr = new SerializationManager();
- }
- return serializationMgr;
- }
- public boolean isDefaultToOwnedRelations() {
- return defaultToOwnedRelations;
- }
- /**
- * Convenience method to log the configuration of this store manager.
- */
- protected void logConfiguration()
- {
- super.logConfiguration();
- if (NucleusLogger.DATASTORE.isDebugEnabled())
- {
- NucleusLogger.DATASTORE.debug("StorageVersion : " + storageVersion.toString());
- NucleusLogger.DATASTORE.debug("Default Relation Mode : " + getStringProperty(RELATION_DEFAULT_MODE));
- NucleusLogger.DATASTORE.debug("===========================================================");
- }
- }
- @Override
- public void transactionStarted(ExecutionContext ec) {
- if (connectionFactoryIsAutoCreateTransaction()) {
- // Obtain the ManagedConnection, triggering XAResource.start, so calling DatastoreService.beginTransaction()
- getConnection(ec);
- }
- }
- @Override
- public NucleusConnection getNucleusConnection(ExecutionContext ec) {
- ConnectionFactory cf = connectionMgr.lookupConnectionFactory(primaryConnectionFactoryName);
- final ManagedConnection mc;
- final boolean enlisted;
- enlisted = ec.getTransaction().isActive();
- mc = cf.getConnection(enlisted ? ec : null, ec.getTransaction(), null); // Will throw exception if already locked
- // Lock the connection now that it is in use by the user
- mc.lock();
- Runnable closeRunnable = new Runnable() {
- public void run() {
- // Unlock the connection now that the user has finished with it
- mc.unlock();
- if (!enlisted) {
- // TODO Anything to do here?
- }
- }
- };
- return new NucleusConnectionImpl(mc.getConnection(), closeRunnable);
- }
- // TODO Remove this when we support subclasses from a query
- @Override
- public Extent getExtent(ExecutionContext ec, Class c, boolean subclasses) {
- AbstractClassMetaData cmd = getMetaDataManager().getMetaDataForClass(c, ec.getClassLoaderResolver());
- validateMetaDataForClass(cmd);
- if (!cmd.isRequiresExtent()) {
- throw new NoExtentException(c.getName());
- }
- if (!getBooleanProperty(GET_EXTENT_CAN_RETURN_SUBCLASSES_PROPERTY, false)) {
- subclasses = false;
- }
- // In order to avoid breaking existing apps I'm hard-coding subclasses to
- // be false. This breaks spec compliance since the no-arg overload of
- // PersistenceManager.getExtent() is supposed to return subclasses.
- return new DefaultCandidateExtent(ec, c, subclasses, cmd);
- }
- /**
- * Method to return the default identifier case.
- * @return Identifier case to use if not specified by the user
- */
- public String getDefaultIdentifierCase() {
- return "PreserveCase";
- }
- @Override
- public Collection<String> getSupportedOptions() {
- Set<String> opts = new HashSet<String>();
- opts.add("TransactionIsolationLevel.read-committed");
- opts.add("BackedSCO"); // TODO Only use this when using original StorageVersion
- opts.add("ApplicationIdentity");
- opts.add("DatastoreIdentity");
- opts.add("OptimisticTransaction");
- opts.add("ORM");
- return opts;
- }
- @Override
- public DatastoreContainerObject newJoinDatastoreContainerObject(AbstractMemberMetaData fmd,
- ClassLoaderResolver clr) {
- return null;
- }
- /**
- * This method requires synchronization so that we don't end up registering
- * the same property more than once.
- */
- @Override
- protected synchronized StoreData newStoreData(ClassMetaData cmd, ClassLoaderResolver clr) {
- InheritanceStrategy strat = cmd.getInheritanceMetaData().getStrategy();
- // The overarching rule for supported inheritance strategies is that we
- // don't split the state of an object across multiple entities.
- // TODO This is all nonsense. This datastore only allows "COMPLETE_TABLE" really so ignore the inheritance
- // and remove the need for DatastoreClass
- if (strat == InheritanceStrategy.SUBCLASS_TABLE) {
- // Table mapped into the table(s) of subclass(es)
- // Just add the SchemaData entry with no table - managed by subclass
- return buildStoreDataWithNoTable(cmd);
- } else if (strat == InheritanceStrategy.COMPLETE_TABLE) {
- if (cmd.isAbstract()) {
- // Abstract class with "complete-table" so gets no table
- return buildStoreDataWithNoTable(cmd);
- }
- return buildStoreData(cmd, clr);
- } else if (strat == InheritanceStrategy.NEW_TABLE &&
- (cmd.getSuperAbstractClassMetaData() == null ||
- cmd.getSuperAbstractClassMetaData().getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUBCLASS_TABLE)) {
- // New Table means you store your fields and your fields only (no fields from superclasses).
- // This only ok if you don't have a persistent superclass or your persistent superclass has
- // delegated responsibility for storing its fields to you.
- return buildStoreData(cmd, clr);
- } else if (MetaDataUtils.isNewOrSuperclassTableInheritanceStrategy(cmd)) {
- // Table mapped into table of superclass
- // Find the superclass - should have been created first
- AbstractClassMetaData[] managingCmds = getClassesManagingTableForClass(cmd, clr);
- DatastoreTable superTable;
- if (managingCmds != null && managingCmds.length == 1) {
- MappedStoreData superData = (MappedStoreData) storeDataMgr.get(managingCmds[0].getFullClassName());
- if (superData != null) {
- // Specify the table if it already exists
- superTable = (DatastoreTable) superData.getDatastoreContainerObject();
- return buildStoreDataWithTable(cmd, superTable);
- }
- }
- }
- // TODO Don't do this. GAE basically supports "complete-table" always so we should just ignore any
- // inheritance metadata that implies otherwise.
- boolean jpa = getApiAdapter().getName().equalsIgnoreCase("JPA");
- String unsupportedMsg = GAE_LOCALISER.msg(jpa ? "AppEngine.BadInheritance.JPA" : "AppEngine.BadInheritance.JDO",
- cmd.getInheritanceMetaData().getStrategy().toString(), cmd.getFullClassName(), getApiAdapter().getName());
- throw new UnsupportedInheritanceStrategyException(unsupportedMsg);
- }
- private StoreData buildStoreDataWithNoTable(ClassMetaData cmd) {
- StoreData sdNew = new MappedStoreData(cmd, null, false);
- registerStoreData(sdNew);
- return sdNew;
- }
- private StoreData buildStoreData(ClassMetaData cmd, ClassLoaderResolver clr) {
- String kindName = EntityUtils.getKindName(getIdentifierFactory(), cmd);
- DatastoreTable table = new DatastoreTable(kindName, this, cmd, clr, dba);
- StoreData sd = new MappedStoreData(cmd, table, true);
- registerStoreData(sd);
- // needs to be called after we register the store data to avoid stack overflow
- table.buildMapping();
- return sd;
- }
-
- private StoreData buildStoreDataWithTable(ClassMetaData cmd, DatastoreTable table) {
- MappedStoreData sd = new MappedStoreData(cmd, table, false);
- registerStoreData(sd);
- sd.setDatastoreContainerObject(table);
- table.manageClass(cmd);
- return sd;
- }
- @Override
- public Object getResultValueAtPosition(Object key, JavaTypeMapping mapping, int position) {
- // this is the key, and we're only using this for keys, so just return it.
- return key;
- }
- @Override
- public boolean allowsBatching() {
- return false;
- }
- public FieldManager getFieldManagerForResultProcessing(ObjectProvider op, Object resultSet,
- StatementClassMapping resultMappings) {
- ExecutionContext ec = op.getExecutionContext();
- Class<?> cls = ec.getClassLoaderResolver().classForName(op.getClassMetaData().getFullClassName());
- Object internalKey = EntityUtils.idToInternalKey(ec, cls, resultSet, true);
- // Need to provide this to the field manager in the form of the pk
- // of the type: Key, Long, encoded String, or unencoded String
- return new KeyOnlyFieldManager(internalKey);
- }
- @Override
- public FieldManager getFieldManagerForResultProcessing(ExecutionContext ec, Object resultSet,
- StatementClassMapping resultMappings, AbstractClassMetaData cmd) {
- Class<?> cls = ec.getClassLoaderResolver().classForName(cmd.getFullClassName());
- Object internalKey = EntityUtils.idToInternalKey(ec, cls, resultSet, true);
- // Need to provide this to the field manager in the form of the pk
- // of the type: Key, Long, encoded String, or unencoded String
- return new KeyOnlyFieldManager(internalKey);
- }
- protected ListStore newFKListStore(AbstractMemberMetaData ammd, ClassLoaderResolver clr) {
- // TODO Delete this when we drop old storage version
- return new FKListStore(ammd, this, clr);
- }
- @Override
- protected SetStore newFKSetStore(AbstractMemberMetaData ammd, ClassLoaderResolver clr) {
- // TODO Delete this when we drop old storage version
- return new FKSetStore(ammd, this, clr);
- }
- public boolean usesBackedSCOWrappers() {
- // TODO Use simple SCO wrappers at some point in future since we have no need for the backed variants now
- /*if (storageVersionAtLeast(StorageVersion.READ_OWNED_CHILD_KEYS_FROM_PARENTS)) {
- return false;
- }*/
- return true;
- }
- public boolean useBackedSCOWrapperForMember(AbstractMemberMetaData mmd, ExecutionContext ec) {
- // Use backed SCO wrapper on relation field (to support legacy), and use simple SCO wrapper on others
- return (mmd.getRelationType(ec.getClassLoaderResolver()) == RelationType.NONE ? false : true);
- }
- public boolean storageVersionAtLeast(StorageVersion storageVersion) {
- return getStorageVersion().ordinal() >= storageVersion.ordinal();
- }
- public TransactionOptions getDefaultDatastoreTransactionOptions() {
- return copyTransactionOptions(defaultDatastoreTransactionOptionsPrototype);
- }
- @Override
- public DatastorePersistenceHandler getPersistenceHandler() {
- return (DatastorePersistenceHandler) super.getPersistenceHandler();
- }
- @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;
- }
- }
- /**
- * Perform appengine-specific validation on the provided meta data.
- * Also generates cached information that is needed by persistence.
- * @param cmd The metadata to validate.
- */
- public void validateMetaDataForClass(AbstractClassMetaData cmd) {
- // Only validate each meta data once
- if (validatedClasses.add(cmd.getFullClassName())) {
- if (getBooleanProperty(VALIDATE_METADATA, true)) {
- // Only do if the persistence property is not set to false
- metadataValidator.validate(cmd);
- }
- AbstractMemberMetaData parentPkMmd = MetaDataUtils.getParentPkMemberMetaDataForClass(cmd, getMetaDataManager(),
- getNucleusContext().getClassLoaderResolver(cmd.getClass().getClassLoader()));
- if (parentPkMmd != null) {
- parentMemberMetaDataByClass.put(cmd.getFullClassName(), parentPkMmd);
- }
- }
- }
- /**
- * Accessor for the metadata of the member of this class marked as "gae.parent-pk".
- * @param cmd Metadata for the class
- * @return The member marked as "gae.parent-pk".
- */
- public AbstractMemberMetaData getMetaDataForParentPK(AbstractClassMetaData cmd) {
- return parentMemberMetaDataByClass.get(cmd.getFullClassName());
- }
- public StorageVersion getStorageVersion() {
- return storageVersion;
- }
-
- public static final class UnsupportedInheritanceStrategyException extends NucleusFatalUserException {
- UnsupportedInheritanceStrategyException(String msg) {
- super(msg);
- }
- }
- /**
- * Accessor for the current DatastoreTransaction for this ExecutionContext.
- * Each PM/EM has its own DatastoreService, and consequently can have a current DatastoreTransaction.
- * @param ec ExecutionContext
- * @return The DatastoreTransaction if active, or null
- */
- public DatastoreTransaction getDatastoreTransaction(ExecutionContext ec) {
- ManagedConnection mconn = ec.getStoreManager().getConnection(ec);
- return ((EmulatedXAResource) mconn.getXAResource()).getCurrentTransaction();
- }
- /**
- * Accessor for the current DatastoreService for this ExecutionContext.
- * Each PM/EM has its own DatastoreService.
- * @param ec ExecutionContext
- * @return The DatastoreService
- */
- public DatastoreService getDatastoreService(ExecutionContext ec) {
- ManagedConnection mconn = ec.getStoreManager().getConnection(ec);
- return ((EmulatedXAResource) mconn.getXAResource()).getDatastoreService();
- }
- /**
- * Accessor for the DatastoreService to use for reads for this ExecutionContext.
- * We currently just use the same DatastoreService for all ExecutionContext, but parameter passed in so
- * we can extend in the future.
- * @param ec ExecutionContext
- * @return The DatastoreService
- */
- public DatastoreService getDatastoreServiceForReads(ExecutionContext ec) {
- return datastoreServiceForReads;
- }
- /**
- * Helper method to determine if the connection factory associated with this
- * manager is AutoCreateTransaction.
- */
- public boolean connectionFactoryIsAutoCreateTransaction() {
- DatastoreConnectionFactoryImpl connFactory =
- (DatastoreConnectionFactoryImpl) connectionMgr.lookupConnectionFactory(primaryConnectionFactoryName);
- return connFactory.isAutoCreateTransaction();
- }
- /**
- * Returns a fresh instance so that callers can make changes without
- * impacting global state.
- */
- public DatastoreServiceConfig getDefaultDatastoreServiceConfigForReads() {
- return copyDatastoreServiceConfig(defaultDatastoreServiceConfigPrototypeForReads);
- }
- /**
- * Returns a fresh instance so that callers can make changes without
- * impacting global state.
- */
- public DatastoreServiceConfig getDefaultDatastoreServiceConfigForWrites() {
- return copyDatastoreServiceConfig(defaultDatastoreServiceConfigPrototypeForWrites);
- }
- private DatastoreServiceConfig createDatastoreServiceConfigPrototypeForReads(
- PersistenceConfiguration persistenceConfig) {
- return createDatastoreServiceConfigPrototype(persistenceConfig, PropertyNames.PROPERTY_DATASTORE_READ_TIMEOUT);
- }
- private DatastoreServiceConfig createDatastoreServiceConfigPrototypeForWrites(
- PersistenceConfiguration persistenceConfig) {
- return createDatastoreServiceConfigPrototype(persistenceConfig, PropertyNames.PROPERTY_DATASTORE_WRITE_TIMEOUT);
- }
- private DatastoreServiceConfig createDatastoreServiceConfigPrototype(
- PersistenceConfiguration persistenceConfiguration, String... timeoutProps) {
- DatastoreServiceConfig datastoreServiceConfig = DatastoreServiceConfig.Builder.withDefaults();
- for (String timeoutProp : timeoutProps) {
- int defaultDeadline = persistenceConfiguration.getIntProperty(timeoutProp);
- if (defaultDeadline > 0) {
- datastoreServiceConfig.deadline(defaultDeadline / 1000d);
- }
- }
- String defaultReadConsistencyStr = persistenceConfiguration.getStringProperty(
- DATASTORE_READ_CONSISTENCY_PROPERTY);
- if (defaultReadConsistencyStr != null) {
- try {
- datastoreServiceConfig.readPolicy(new ReadPolicy(Consistency.valueOf(defaultReadConsistencyStr)));
- } catch (IllegalArgumentException iae) {
- throw new NucleusFatalUserException(
- "Illegal value for " + DATASTORE_READ_CONSISTENCY_PROPERTY +
- ". Valid values are " + Arrays.toString(Consistency.values()));
- }
- }
- return datastoreServiceConfig;
- }
- @SuppressWarnings("deprecation")
- private TransactionOptions createDatastoreTransactionOptionsPrototype(
- PersistenceConfiguration persistenceConfig) {
- // return TransactionOptions.Builder.withXG(
- return TransactionOptions.Builder.allowMultipleEntityGroups(
- persistenceConfig.getBooleanProperty(DATASTORE_ENABLE_XG_TXNS_PROPERTY));
- }
- public TypeConversionUtils getTypeConversionUtils() {
- return typeConversionUtils;
- }
- // visible for testing
- static DatastoreServiceConfig copyDatastoreServiceConfig(DatastoreServiceConfig config) {
- DatastoreServiceConfig newConfig = DatastoreServiceConfig.Builder.
- withImplicitTransactionManagementPolicy(config.getImplicitTransactionManagementPolicy()).
- readPolicy(config.getReadPolicy());
- if (config.getDeadline() != null) {
- newConfig.deadline(config.getDeadline());
- }
- return newConfig;
- }
- // visible for testing
- static TransactionOptions copyTransactionOptions(TransactionOptions txnOpts) {
- // Maintenance nightmare, use clone() once it's available in the sdk
- TransactionOptions options = TransactionOptions.Builder.withDefaults();
- if (txnOpts.isXG()) {
- options.setXG(txnOpts.isXG());
- } else {
- options.clearXG();
- }
- return txnOpts;
- }
- }