PageRenderTime 114ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/com/google/appengine/datanucleus/DatastorePersistenceHandler.java

http://datanucleus-appengine.googlecode.com/
Java | 736 lines | 464 code | 78 blank | 194 comment | 146 complexity | 32abb3ce49195783cdd66e7050508fe2 MD5 | raw file
Possible License(s): Apache-2.0
  1. /**********************************************************************
  2. Copyright (c) 2009 Google Inc.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. **********************************************************************/
  13. package com.google.appengine.datanucleus;
  14. import com.google.appengine.api.datastore.Entity;
  15. import com.google.appengine.api.datastore.EntityNotFoundException;
  16. import com.google.appengine.api.datastore.Key;
  17. import com.google.appengine.api.datastore.KeyFactory;
  18. import com.google.appengine.datanucleus.mapping.DatastoreTable;
  19. import com.google.appengine.datanucleus.mapping.DependentDeleteRequest;
  20. import com.google.appengine.datanucleus.mapping.FetchMappingConsumer;
  21. import org.datanucleus.ClassLoaderResolver;
  22. import org.datanucleus.PropertyNames;
  23. import org.datanucleus.exceptions.NucleusObjectNotFoundException;
  24. import org.datanucleus.exceptions.NucleusOptimisticException;
  25. import org.datanucleus.metadata.AbstractClassMetaData;
  26. import org.datanucleus.metadata.AbstractMemberMetaData;
  27. import org.datanucleus.metadata.ColumnMetaData;
  28. import org.datanucleus.metadata.DiscriminatorMetaData;
  29. import org.datanucleus.metadata.IdentityType;
  30. import org.datanucleus.metadata.VersionMetaData;
  31. import org.datanucleus.metadata.VersionStrategy;
  32. import org.datanucleus.store.AbstractPersistenceHandler;
  33. import org.datanucleus.ExecutionContext;
  34. import org.datanucleus.state.ObjectProvider;
  35. import org.datanucleus.store.PersistenceBatchType;
  36. import org.datanucleus.store.StoreManager;
  37. import org.datanucleus.store.VersionHelper;
  38. import org.datanucleus.store.mapped.DatastoreClass;
  39. import org.datanucleus.store.mapped.DatastoreField;
  40. import org.datanucleus.store.mapped.mapping.ArrayMapping;
  41. import org.datanucleus.store.mapped.mapping.CollectionMapping;
  42. import org.datanucleus.store.mapped.mapping.IndexMapping;
  43. import org.datanucleus.store.mapped.mapping.JavaTypeMapping;
  44. import org.datanucleus.store.mapped.mapping.MapMapping;
  45. import org.datanucleus.store.mapped.mapping.MappingCallbacks;
  46. import org.datanucleus.store.schema.naming.ColumnType;
  47. import org.datanucleus.store.types.SCO;
  48. import org.datanucleus.util.Localiser;
  49. import org.datanucleus.util.NucleusLogger;
  50. import org.datanucleus.util.StringUtils;
  51. import java.sql.Timestamp;
  52. import java.util.Collection;
  53. import java.util.Collections;
  54. import java.util.Iterator;
  55. import java.util.List;
  56. import java.util.Map;
  57. import java.util.Set;
  58. import java.util.concurrent.ConcurrentHashMap;
  59. /**
  60. * Handler for persistence requests for GAE/J datastore. Lifecycle management processes persists, updates, deletes
  61. * and field access and hands them off here to interface with the datastore.
  62. * No method in here should be called from anywhere other than DataNucleus core.
  63. * <h3>Persistence Process</h3>
  64. * Receive calls to the following from DataNucleus core for persistence events. All persistence events arrive in
  65. * the store plugin in the order they are performed by the user. Optimistic operations are queued until flush().
  66. * <p>
  67. * <b>PersistenceHandler.insertObject</b><br/>
  68. * <ol>
  69. * <li>CREATE Entity belonging to the appropriate entity group to represent the object.</li>
  70. *
  71. * <li>If the entity is “owned” the entity group must be established before the Entity is initially put(),
  72. * there is no way to adjust it after. So when persisting an Entity that is a child of some other Entity, you need to
  73. * figure out who its parent is before you can do this first put.
  74. * <ol>
  75. * <li>Key 'id' can be assigned by datastore (long, Long)</li>
  76. * <li>Key 'name' can be assigned by the application (or value-generator) (String)</li>
  77. * </ol></li>
  78. *
  79. * <li>Create StoreFieldManager, and set properties in Entity for all fields which have values ready.
  80. * <ol>
  81. * <li>If “identity” not set on this related object, make note of and skip relation fields.</li>
  82. * <li>If related object is detached, note its field number</li>
  83. * <li>If “identity” set on this related object, and related object not persistent, flush the related object(s) to
  84. * get their Key(s).</li>
  85. * </ol></li>
  86. *
  87. * <li>PUT the Entity in datastore</li>
  88. * <li>Set any generated id back on the Entity</li>
  89. * <li>If fields noted in step 3.1
  90. * <ol>
  91. * <li>Reuse StoreFieldManager from above, and process fields noted earlier.
  92. * <ol>
  93. * <li>Attach any detached related objects</li>
  94. * <li>Persist and flush new related object(s), and add property(s) for relation fields to the Entity</li>
  95. * </ol></li>
  96. * <li>PUT the updated Entity in datastore</li>
  97. * </ol></li>
  98. *
  99. * </ol>
  100. * </p>
  101. *
  102. * <p>
  103. * <b>PersistenceHandler.updateObject</b><br/>
  104. * <ol>
  105. * <li>GET Entity that represents the object</li>
  106. * <li>Populate all updated fields that have values ready, forcing the flush of any relation fields that don't have
  107. * their id present, and attach any detached related objects</li>
  108. * <li>PUT the updated Entity in datastore</li>
  109. * </ol>
  110. * </p>
  111. * <p>
  112. * <b>PersistenceHandler.deleteObject</b><br/>
  113. * <ol>
  114. * <li>GET Entity that represents the object</li>
  115. * <li>Handle any cascade deletion</li>
  116. * <li>DELETE the Entity from datastore</li>
  117. * </ol>
  118. * </p>
  119. *
  120. * @author Max Ross <maxr@google.com>
  121. * @author Andy Jefferson
  122. */
  123. public class DatastorePersistenceHandler extends AbstractPersistenceHandler {
  124. protected static final Localiser GAE_LOCALISER = Localiser.getInstance(
  125. "com.google.appengine.datanucleus.Localisation", DatastoreManager.class.getClassLoader());
  126. private final Map<ExecutionContext, BatchPutManager> batchPutManagerByExecutionContext = new ConcurrentHashMap();
  127. private final Map<ExecutionContext, BatchDeleteManager> batchDeleteManagerByExecutionContext = new ConcurrentHashMap();
  128. private final DatastoreManager datastoreMgr;
  129. /**
  130. * Constructor.
  131. * @param storeMgr The StoreManager to use.
  132. */
  133. public DatastorePersistenceHandler(StoreManager storeMgr) {
  134. super(storeMgr);
  135. this.datastoreMgr = (DatastoreManager) storeMgr;
  136. }
  137. public void close() {}
  138. protected BatchPutManager getBatchPutManager(ExecutionContext ec) {
  139. BatchPutManager putMgr = batchPutManagerByExecutionContext.get(ec);
  140. if (putMgr == null) {
  141. putMgr = new BatchPutManager();
  142. batchPutManagerByExecutionContext.put(ec, putMgr);
  143. }
  144. return putMgr;
  145. }
  146. protected BatchDeleteManager getBatchDeleteManager(ExecutionContext ec) {
  147. BatchDeleteManager deleteMgr = batchDeleteManagerByExecutionContext.get(ec);
  148. if (deleteMgr == null) {
  149. deleteMgr = new BatchDeleteManager(ec);
  150. batchDeleteManagerByExecutionContext.put(ec, deleteMgr);
  151. }
  152. return deleteMgr;
  153. }
  154. /* (non-Javadoc)
  155. * @see org.datanucleus.store.AbstractPersistenceHandler#batchStart(org.datanucleus.store.ExecutionContext, org.datanucleus.store.PersistenceBatchType)
  156. */
  157. @Override
  158. public void batchStart(ExecutionContext ec, PersistenceBatchType batchType) {
  159. if (batchType == PersistenceBatchType.PERSIST) {
  160. getBatchPutManager(ec).start();
  161. }
  162. else if (batchType == PersistenceBatchType.DELETE) {
  163. getBatchDeleteManager(ec).start();
  164. }
  165. }
  166. /* (non-Javadoc)
  167. * @see org.datanucleus.store.AbstractPersistenceHandler#batchEnd(org.datanucleus.store.ExecutionContext, org.datanucleus.store.PersistenceBatchType)
  168. */
  169. @Override
  170. public void batchEnd(ExecutionContext ec, PersistenceBatchType batchType) {
  171. if (batchType == PersistenceBatchType.PERSIST) {
  172. getBatchPutManager(ec).finish(this);
  173. batchPutManagerByExecutionContext.remove(ec);
  174. }
  175. else if (batchType == PersistenceBatchType.DELETE) {
  176. getBatchDeleteManager(ec).finish(this);
  177. batchDeleteManagerByExecutionContext.remove(ec);
  178. }
  179. }
  180. /**
  181. * Method to insert the specified managed object into the datastore.
  182. * @param op ObjectProvider for the managed object
  183. */
  184. public void insertObject(ObjectProvider op) {
  185. // Make sure writes are permitted
  186. assertReadOnlyForUpdateOfObject(op);
  187. datastoreMgr.validateMetaDataForClass(op.getClassMetaData());
  188. // If we're in the middle of a batch operation just register the ObjectProvider that needs the insertion
  189. BatchPutManager batchPutMgr = getBatchPutManager(op.getExecutionContext());
  190. if (batchPutMgr.batchOperationInProgress()) {
  191. batchPutMgr.add(op);
  192. return;
  193. }
  194. insertObjectsInternal(Collections.singletonList(op));
  195. }
  196. /**
  197. * Method to perform the work of inserting the specified objects. If multiple are to be inserted then
  198. * performs it initially as a batch PUT. If some of these need subsequent work (e.g forcing the persist of
  199. * children followed by a repersist to link to the children) then this is done one-by-one.
  200. */
  201. void insertObjectsInternal(List<ObjectProvider> opsToInsert) {
  202. if (opsToInsert == null || opsToInsert.isEmpty()) {
  203. return;
  204. }
  205. // All must be in same ExecutionContext
  206. ExecutionContext ec = opsToInsert.get(0).getExecutionContext();
  207. List<PutState> putStateList = Utils.newArrayList();
  208. for (ObjectProvider op : opsToInsert) {
  209. AbstractClassMetaData cmd = op.getClassMetaData();
  210. // Create the Entity, and populate all fields that can be populated (this will omit any owned child objects
  211. // if we don't have the key of this object yet).
  212. StoreFieldManager fieldMgr =
  213. new StoreFieldManager(op, EntityUtils.determineKind(cmd, ec));
  214. op.provideFields(op.getClassMetaData().getAllMemberPositions(), fieldMgr);
  215. // Make sure the Entity parent is set (if any)
  216. Object assignedParentPk = fieldMgr.establishEntityGroup();
  217. Entity entity = fieldMgr.getEntity();
  218. if (!datastoreMgr.storageVersionAtLeast(StorageVersion.READ_OWNED_CHILD_KEYS_FROM_PARENTS)) {
  219. // Older storage versions : store list positions in the element
  220. DatastoreTable table = datastoreMgr.getDatastoreClass(op.getClassMetaData().getFullClassName(),
  221. ec.getClassLoaderResolver());
  222. Collection<JavaTypeMapping> orderMappings = table.getExternalOrderMappings().values();
  223. for (JavaTypeMapping orderMapping : orderMappings) {
  224. if (orderMapping instanceof IndexMapping) {
  225. Object orderValue = op.getAssociatedValue(orderMapping);
  226. if (orderValue != null) {
  227. // Set order index on the entity
  228. DatastoreField indexProp = orderMapping.getDatastoreMapping(0).getDatastoreField();
  229. entity.setProperty(indexProp.getIdentifier().toString(), orderValue); // Is this indexed in the datastore?
  230. } else {
  231. // Element has been persisted and has the owner set, but not positioned, so leave til user does it
  232. }
  233. }
  234. }
  235. }
  236. // Set version
  237. handleVersioningBeforeWrite(op, entity, true, "inserting");
  238. // Set discriminator
  239. if (op.getClassMetaData().hasDiscriminatorStrategy()) {
  240. DiscriminatorMetaData dismd = op.getClassMetaData().getDiscriminatorMetaDataRoot();
  241. EntityUtils.setEntityProperty(entity, dismd,
  242. EntityUtils.getDiscriminatorPropertyName(datastoreMgr.getIdentifierFactory(), dismd),
  243. op.getClassMetaData().getDiscriminatorValue());
  244. }
  245. // Add Multi-tenancy discriminator if applicable
  246. if (storeMgr.getStringProperty(PropertyNames.PROPERTY_TENANT_ID) != null) {
  247. if ("true".equalsIgnoreCase(cmd.getValueForExtension("multitenancy-disable"))) {
  248. // Don't bother with multitenancy for this class
  249. }
  250. else {
  251. String name = storeMgr.getNamingFactory().getColumnName(cmd, ColumnType.MULTITENANCY_COLUMN);
  252. EntityUtils.setEntityProperty(entity, cmd, name, storeMgr.getStringProperty(PropertyNames.PROPERTY_TENANT_ID));
  253. }
  254. }
  255. // Update parent PK field on pojo
  256. AbstractMemberMetaData parentPkMmd = datastoreMgr.getMetaDataForParentPK(cmd);
  257. if (assignedParentPk != null) {
  258. // we automatically assigned a parent to the entity so make sure that makes it back on to the pojo
  259. op.replaceField(parentPkMmd.getAbsoluteFieldNumber(), assignedParentPk);
  260. }
  261. // Add the "state" for this put to the list.
  262. putStateList.add(new PutState(op, fieldMgr, entity));
  263. }
  264. // PUT all entities in single call
  265. if (!putStateList.isEmpty()) {
  266. DatastoreTransaction txn = null;
  267. AbstractClassMetaData acmd = null;
  268. List<Entity> entityList = Utils.newArrayList();
  269. for (PutState putState : putStateList) {
  270. if (txn == null) {
  271. txn = datastoreMgr.getDatastoreTransaction(ec);
  272. }
  273. if (acmd == null) {
  274. acmd = putState.op.getClassMetaData();
  275. }
  276. entityList.add(putState.entity);
  277. }
  278. EntityUtils.putEntitiesIntoDatastore(ec, entityList);
  279. for (PutState putState : putStateList) {
  280. putState.op.setAssociatedValue(txn, putState.entity);
  281. }
  282. }
  283. // Post-processing for all puts
  284. for (PutState putState : putStateList) {
  285. AbstractClassMetaData cmd = putState.op.getClassMetaData();
  286. // Set the generated key back on the pojo. If the pk field is a Key just set it on the field directly.
  287. // If the pk field is a String, convert the Key to a String, similarly for long.
  288. // Assumes we only have a single pk member position
  289. Object newId = null;
  290. Class pkType = null;
  291. boolean identityStrategyUsed = false;
  292. if (cmd.pkIsDatastoreAttributed(storeMgr)) {
  293. if (cmd.getIdentityType() == IdentityType.APPLICATION) {
  294. // Assume only 1 PK field
  295. identityStrategyUsed = true;
  296. pkType = cmd.getMetaDataForManagedMemberAtAbsolutePosition(cmd.getPKMemberPositions()[0]).getType();
  297. } else if (cmd.getIdentityType() == IdentityType.DATASTORE) {
  298. identityStrategyUsed = true;
  299. pkType = Key.class;
  300. ColumnMetaData colmd = cmd.getIdentityMetaData().getColumnMetaData();
  301. if (colmd != null) {
  302. if ("varchar".equalsIgnoreCase(colmd.getJdbcType()) || "char".equalsIgnoreCase(colmd.getJdbcType())) {
  303. pkType = String.class;
  304. } else if ("integer".equalsIgnoreCase(colmd.getJdbcType()) || "numeric".equalsIgnoreCase(colmd.getJdbcType())) {
  305. pkType = Long.class;
  306. }
  307. }
  308. }
  309. }
  310. if (identityStrategyUsed) {
  311. // Update the identity of the object with the datastore-assigned id
  312. if (pkType.equals(Key.class)) {
  313. newId = putState.entity.getKey();
  314. } else if (pkType.equals(String.class)) {
  315. if (MetaDataUtils.hasEncodedPKField(cmd)) {
  316. newId = KeyFactory.keyToString(putState.entity.getKey());
  317. } else {
  318. newId = putState.entity.getKey().getName();
  319. }
  320. } else if (pkType.equals(Long.class) || pkType.equals(long.class)) {
  321. newId = putState.entity.getKey().getId();
  322. }
  323. putState.op.setPostStoreNewObjectId(newId);
  324. }
  325. // Update relation fields (including cascade-persist etc)
  326. if (putState.fieldMgr.storeRelations(KeyRegistry.getKeyRegistry(ec))) {
  327. // PUT Entity into datastore with these changes
  328. EntityUtils.putEntityIntoDatastore(ec, putState.entity);
  329. }
  330. putState.op.replaceAllLoadedSCOFieldsWithWrappers();
  331. if (ec.getStatistics() != null) {
  332. ec.getStatistics().incrementInsertCount();
  333. }
  334. }
  335. }
  336. /**
  337. * Method to update the specified fields of the managed object in the datastore.
  338. * @param op ObjectProvider of the managed object
  339. * @param fieldNumbers Fields to be updated in the datastore
  340. */
  341. public void updateObject(ObjectProvider op, int fieldNumbers[]) {
  342. if (op.getLifecycleState().isDeleted()) {
  343. // don't perform updates on objects that are already deleted - this will cause them to be recreated
  344. // This happens with JPAOneToOneTest/JDOOneToOneTest when deleting, called from DependentDeleteRequest
  345. return;
  346. }
  347. // Make sure writes are permitted
  348. assertReadOnlyForUpdateOfObject(op);
  349. datastoreMgr.validateMetaDataForClass(op.getClassMetaData());
  350. AbstractClassMetaData cmd = op.getClassMetaData();
  351. long startTime = System.currentTimeMillis();
  352. if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled()) {
  353. StringBuffer fieldStr = new StringBuffer();
  354. for (int i=0;i<fieldNumbers.length;i++) {
  355. if (i > 0) {
  356. fieldStr.append(",");
  357. }
  358. fieldStr.append(cmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumbers[i]).getName());
  359. }
  360. NucleusLogger.DATASTORE_PERSIST.debug(GAE_LOCALISER.msg("AppEngine.Update.Start",
  361. StringUtils.toJVMIDString(op.getObject()), op.getInternalObjectId(), fieldStr.toString()));
  362. }
  363. ExecutionContext ec = op.getExecutionContext();
  364. Entity entity = (Entity) op.getAssociatedValue(datastoreMgr.getDatastoreTransaction(ec));
  365. if (entity == null) {
  366. // Corresponding entity hasn't been fetched yet, so get it.
  367. Key key = EntityUtils.getPkAsKey(op);
  368. entity = EntityUtils.getEntityFromDatastore(datastoreMgr.getDatastoreServiceForReads(ec), op, key);
  369. }
  370. // Update the Entity with the specified fields
  371. StoreFieldManager fieldMgr = new StoreFieldManager(op, entity, fieldNumbers);
  372. op.provideFields(fieldNumbers, fieldMgr);
  373. // Check and update the version
  374. handleVersioningBeforeWrite(op, entity, true, "updating");
  375. // Update relation fields (including cascade-persist etc)
  376. fieldMgr.storeRelations(KeyRegistry.getKeyRegistry(op.getExecutionContext()));
  377. // PUT Entity into datastore
  378. DatastoreTransaction txn = EntityUtils.putEntityIntoDatastore(ec, entity);
  379. op.setAssociatedValue(txn, entity);
  380. op.replaceAllLoadedSCOFieldsWithWrappers();
  381. if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled()) {
  382. NucleusLogger.DATASTORE_PERSIST.debug(GAE_LOCALISER.msg("AppEngine.ExecutionTime",
  383. (System.currentTimeMillis() - startTime)));
  384. }
  385. if (ec.getStatistics() != null) {
  386. ec.getStatistics().incrementUpdateCount();
  387. }
  388. }
  389. /**
  390. * Method to delete the specified managed object from the datastore.
  391. * @param op ObjectProvider of the managed object
  392. */
  393. public void deleteObject(ObjectProvider op) {
  394. // Make sure writes are permitted
  395. assertReadOnlyForUpdateOfObject(op);
  396. datastoreMgr.validateMetaDataForClass(op.getClassMetaData());
  397. long startTime = System.currentTimeMillis();
  398. if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled()) {
  399. NucleusLogger.DATASTORE_PERSIST.debug(GAE_LOCALISER.msg("AppEngine.Delete.Start",
  400. StringUtils.toJVMIDString(op.getObject()), op.getInternalObjectId()));
  401. }
  402. ExecutionContext ec = op.getExecutionContext();
  403. Entity entity = (Entity) op.getAssociatedValue(datastoreMgr.getDatastoreTransaction(ec));
  404. if (entity == null) {
  405. // Corresponding entity hasn't been fetched yet, so get it.
  406. Key key = EntityUtils.getPkAsKey(op);
  407. entity = EntityUtils.getEntityFromDatastore(datastoreMgr.getDatastoreServiceForReads(ec), op, key);
  408. }
  409. DatastoreTransaction txn = datastoreMgr.getDatastoreTransaction(ec);
  410. if (txn != null) {
  411. txn.addDeletedKey(entity.getKey());
  412. }
  413. // Check the version is valid to delete; any updates since read?
  414. handleVersioningBeforeWrite(op, entity, false, "deleting");
  415. // first handle any dependent deletes that need deleting before we delete this object
  416. ClassLoaderResolver clr = ec.getClassLoaderResolver();
  417. DatastoreClass dc = datastoreMgr.getDatastoreClass(op.getObject().getClass().getName(), clr);
  418. DependentDeleteRequest req = new DependentDeleteRequest(dc, op.getClassMetaData(), clr);
  419. Set relatedObjectsToDelete = req.execute(op, entity);
  420. Key keyToDelete = EntityUtils.getPkAsKey(op);
  421. // If we're in the middle of a batch operation just register the key that needs the delete
  422. BatchDeleteManager bdm = getBatchDeleteManager(ec);
  423. if (bdm.batchOperationInProgress()) {
  424. bdm.add(new BatchDeleteManager.BatchDeleteState(txn, keyToDelete));
  425. if (relatedObjectsToDelete != null && !relatedObjectsToDelete.isEmpty()) {
  426. // Delete any related objects that need deleting after the delete of this object
  427. Iterator iter = relatedObjectsToDelete.iterator();
  428. while (iter.hasNext()) {
  429. Object relatedObject = iter.next();
  430. ec.deleteObjectInternal(relatedObject);
  431. }
  432. }
  433. if (ec.getStatistics() != null) {
  434. ec.getStatistics().incrementDeleteCount();
  435. }
  436. return;
  437. }
  438. // Delete this object
  439. EntityUtils.deleteEntitiesFromDatastore(ec, Collections.singletonList(keyToDelete));
  440. if (relatedObjectsToDelete != null && !relatedObjectsToDelete.isEmpty()) {
  441. // Delete any related objects that need deleting after the delete of this object
  442. Iterator iter = relatedObjectsToDelete.iterator();
  443. while (iter.hasNext()) {
  444. Object relatedObject = iter.next();
  445. ec.deleteObjectInternal(relatedObject);
  446. }
  447. }
  448. if (ec.getStatistics() != null) {
  449. ec.getStatistics().incrementDeleteCount();
  450. }
  451. if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled()) {
  452. NucleusLogger.DATASTORE_PERSIST.debug(GAE_LOCALISER.msg("AppEngine.ExecutionTime",
  453. (System.currentTimeMillis() - startTime)));
  454. }
  455. }
  456. /**
  457. * Method to fetch the specified fields of the managed object from the datastore.
  458. * @param op ObjectProvider of the object whose fields need fetching
  459. * @param fieldNumbers Fields to fetch
  460. */
  461. public void fetchObject(ObjectProvider op, int fieldNumbers[]) {
  462. if (fieldNumbers == null || fieldNumbers.length == 0) {
  463. return;
  464. }
  465. AbstractClassMetaData cmd = op.getClassMetaData();
  466. datastoreMgr.validateMetaDataForClass(cmd);
  467. // We always fetch the entire object, so if the state manager
  468. // already has an associated Entity we know that associated
  469. // Entity has all the fields.
  470. ExecutionContext ec = op.getExecutionContext();
  471. Entity entity = (Entity) op.getAssociatedValue(datastoreMgr.getDatastoreTransaction(ec));
  472. if (entity == null) {
  473. Key pk = EntityUtils.getPkAsKey(op);
  474. entity = EntityUtils.getEntityFromDatastore(datastoreMgr.getDatastoreServiceForReads(ec), op, pk); // Throws NucleusObjectNotFoundException if necessary
  475. }
  476. if (NucleusLogger.DATASTORE_RETRIEVE.isDebugEnabled()) {
  477. // Debug information about what we are retrieving
  478. StringBuffer str = new StringBuffer("Fetching object \"");
  479. str.append(StringUtils.toJVMIDString(op.getObject())).append("\" (id=");
  480. str.append(op.getInternalObjectId()).append(")").append(" fields [");
  481. for (int i=0;i<fieldNumbers.length;i++) {
  482. if (i > 0) {
  483. str.append(",");
  484. }
  485. str.append(cmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumbers[i]).getName());
  486. }
  487. str.append("]");
  488. NucleusLogger.DATASTORE_RETRIEVE.debug(str);
  489. }
  490. long startTime = System.currentTimeMillis();
  491. if (NucleusLogger.DATASTORE_RETRIEVE.isDebugEnabled()) {
  492. NucleusLogger.DATASTORE_RETRIEVE.debug(GAE_LOCALISER.msg("AppEngine.Fetch.Start",
  493. StringUtils.toJVMIDString(op.getObject()), op.getInternalObjectId()));
  494. }
  495. op.replaceFields(fieldNumbers, new FetchFieldManager(op, entity, fieldNumbers));
  496. // Refresh version in case not yet set (e.g created HOLLOW object, and this is first fetch)
  497. VersionMetaData vmd = cmd.getVersionMetaDataForClass();
  498. if (cmd.isVersioned()) {
  499. Object versionValue = entity.getProperty(EntityUtils.getVersionPropertyName(datastoreMgr.getIdentifierFactory(), vmd));
  500. if (vmd.getVersionStrategy() == VersionStrategy.DATE_TIME) {
  501. versionValue = new Timestamp((Long)versionValue);
  502. }
  503. op.setVersion(versionValue);
  504. }
  505. // Run post-fetch mapping callbacks. What is this actually achieving?
  506. AbstractMemberMetaData[] fmds = new AbstractMemberMetaData[fieldNumbers.length];
  507. for (int i = 0; i < fmds.length; i++) {
  508. fmds[i] = op.getClassMetaData().getMetaDataForManagedMemberAtAbsolutePosition(fieldNumbers[i]);
  509. }
  510. ClassLoaderResolver clr = ec.getClassLoaderResolver();
  511. DatastoreClass dc = datastoreMgr.getDatastoreClass(op.getObject().getClass().getName(), clr);
  512. FetchMappingConsumer consumer = new FetchMappingConsumer(op.getClassMetaData());
  513. dc.provideMappingsForMembers(consumer, fmds, true);
  514. dc.provideDatastoreIdMappings(consumer);
  515. dc.providePrimaryKeyMappings(consumer);
  516. for (MappingCallbacks callback : consumer.getMappingCallbacks()) {
  517. // Arrays and Maps don't use backing stores
  518. if (callback instanceof ArrayMapping || callback instanceof MapMapping) {
  519. // Do nothing since arrays and maps are stored in the parent property and loaded above using FetchFieldManager
  520. } else if (callback instanceof CollectionMapping) {
  521. CollectionMapping m = (CollectionMapping)callback;
  522. Object val = op.provideField(m.getMemberMetaData().getAbsoluteFieldNumber());
  523. if (val == null || !(val instanceof SCO)) {
  524. // Not yet wrapped, so make sure we wrap it
  525. callback.postFetch(op);
  526. }
  527. } else {
  528. callback.postFetch(op);
  529. }
  530. }
  531. if (NucleusLogger.DATASTORE_RETRIEVE.isDebugEnabled()) {
  532. NucleusLogger.DATASTORE_RETRIEVE.debug(GAE_LOCALISER.msg("AppEngine.ExecutionTime",
  533. (System.currentTimeMillis() - startTime)));
  534. }
  535. if (ec.getStatistics() != null) {
  536. ec.getStatistics().incrementFetchCount();
  537. }
  538. }
  539. /**
  540. * Method to locate the specified managed objects in the datastore.
  541. * @param ops ObjectProviders for the managed objects
  542. * @throws NucleusObjectNotFoundException if any of the objects aren't found in the datastore
  543. */
  544. public void locateObjects(ObjectProvider[] ops) {
  545. if (ops == null) {
  546. return;
  547. }
  548. List<Key> keysToLocate = Utils.newArrayList();
  549. for (int i=0;i<ops.length;i++) {
  550. Key key = EntityUtils.getPkAsKey(ops[i]);
  551. keysToLocate.add(key);
  552. }
  553. EntityUtils.getEntitiesFromDatastore(datastoreMgr.getDatastoreServiceForReads(ops[0].getExecutionContext()),
  554. keysToLocate, ops[0].getExecutionContext());
  555. }
  556. /**
  557. * Method to locate the specified managed object in the datastore.
  558. * @param op ObjectProvider for the managed object
  559. * @throws NucleusObjectNotFoundException if the object isn't found in the datastore
  560. */
  561. public void locateObject(ObjectProvider op) {
  562. datastoreMgr.validateMetaDataForClass(op.getClassMetaData());
  563. EntityUtils.getEntityFromDatastore(datastoreMgr.getDatastoreServiceForReads(op.getExecutionContext()), op,
  564. EntityUtils.getPkAsKey(op));
  565. }
  566. /**
  567. * Implementation of this operation is optional and is intended for
  568. * datastores that instantiate the model objects themselves (as opposed
  569. * to letting datanucleus do it). The App Engine datastore lets
  570. * datanucleus instantiate the model objects so we just return null.
  571. */
  572. public Object findObject(ExecutionContext ec, Object id) {
  573. return null;
  574. }
  575. /**
  576. * Method to check optimistic versioning, and to set the version on the entity when required.
  577. * @param op ObjectProvider for the object
  578. * @param entity The entity being updated
  579. * @param versionBehavior Behaviour required for versioning here
  580. * @param operation Convenience string for messages
  581. */
  582. private void handleVersioningBeforeWrite(ObjectProvider op, Entity entity, boolean increment, String operation) {
  583. AbstractClassMetaData cmd = op.getClassMetaData();
  584. VersionMetaData vmd = cmd.getVersionMetaDataForClass();
  585. if (cmd.isVersioned()) {
  586. ExecutionContext ec = op.getExecutionContext();
  587. String versionPropertyName = EntityUtils.getVersionPropertyName(datastoreMgr.getIdentifierFactory(), vmd);
  588. Object curVersion = op.getVersion();
  589. if (curVersion != null) {
  590. // Fetch the latest and greatest version of the entity from the datastore
  591. // to see if anyone has made a change underneath us. We need to execute
  592. // the fetch outside a txn to guarantee that we see the latest version.
  593. if (NucleusLogger.DATASTORE_NATIVE.isDebugEnabled()) {
  594. NucleusLogger.DATASTORE_NATIVE.debug("Getting entity with key " + entity.getKey());
  595. }
  596. Entity refreshedEntity;
  597. try {
  598. if (ec.getStatistics() != null) {
  599. ec.getStatistics().incrementNumReads();
  600. }
  601. refreshedEntity = datastoreMgr.getDatastoreServiceForReads(op.getExecutionContext()).get(entity.getKey());
  602. } catch (EntityNotFoundException e) {
  603. // someone deleted out from under us
  604. throw new NucleusOptimisticException(GAE_LOCALISER.msg("AppEngine.OptimisticError.EntityHasBeenDeleted", operation,
  605. cmd.getFullClassName(), entity.getKey()));
  606. }
  607. Object datastoreVersion = refreshedEntity.getProperty(versionPropertyName);
  608. if (vmd.getVersionStrategy() == VersionStrategy.DATE_TIME) {
  609. datastoreVersion = new Timestamp((Long) datastoreVersion);
  610. }
  611. if (!datastoreVersion.equals(curVersion)) {
  612. throw new NucleusOptimisticException(GAE_LOCALISER.msg("AppEngine.OptimisticError.EntityHasBeenUpdated", operation,
  613. cmd.getFullClassName(), entity.getKey()));
  614. }
  615. }
  616. Object nextVersion = VersionHelper.getNextVersion(vmd.getVersionStrategy(), curVersion);
  617. op.setTransactionalVersion(nextVersion);
  618. if (vmd.getVersionStrategy() == VersionStrategy.DATE_TIME) {
  619. EntityUtils.setEntityProperty(entity, vmd, versionPropertyName, ((Timestamp)nextVersion).getTime());
  620. } else {
  621. EntityUtils.setEntityProperty(entity, vmd, versionPropertyName, nextVersion);
  622. }
  623. // Version field - update the version on the object
  624. if (increment && vmd.getFieldName() != null) {
  625. AbstractMemberMetaData verfmd =
  626. ((AbstractClassMetaData)vmd.getParent()).getMetaDataForMember(vmd.getFieldName());
  627. if (nextVersion instanceof Number) {
  628. // Version can be long, Long, int, Integer, short, Short (or Timestamp).
  629. Number nextNumber = (Number) nextVersion;
  630. if (verfmd.getType().equals(Long.class) || verfmd.getType().equals(Long.TYPE)) {
  631. nextVersion = nextNumber.longValue();
  632. } else if (verfmd.getType().equals(Integer.class) || verfmd.getType().equals(Integer.TYPE)) {
  633. nextVersion = nextNumber.intValue();
  634. } else if (verfmd.getType().equals(Short.class) || verfmd.getType().equals(Short.TYPE)) {
  635. nextVersion = nextNumber.shortValue();
  636. }
  637. }
  638. op.replaceField(verfmd.getAbsoluteFieldNumber(), nextVersion);
  639. }
  640. }
  641. }
  642. /**
  643. * All the information needed to perform a put on an Entity.
  644. */
  645. private static final class PutState {
  646. private final ObjectProvider op;
  647. private final StoreFieldManager fieldMgr;
  648. private final Entity entity;
  649. private PutState(ObjectProvider op, StoreFieldManager fieldMgr, Entity entity) {
  650. this.op = op;
  651. this.fieldMgr = fieldMgr;
  652. this.entity = entity;
  653. }
  654. }
  655. }