1 package com
.google
.appengine
.api
.labs
.datastore
.overlay
;
3 import static com
.google
.common
.base
.Preconditions
.checkNotNull
;
5 import com
.google
.appengine
.api
.datastore
.BaseDatastoreService
;
6 import com
.google
.appengine
.api
.datastore
.DatastoreAttributes
;
7 import com
.google
.appengine
.api
.datastore
.DatastoreService
;
8 import com
.google
.appengine
.api
.datastore
.DatastoreService
.KeyRangeState
;
9 import com
.google
.appengine
.api
.datastore
.Entity
;
10 import com
.google
.appengine
.api
.datastore
.EntityNotFoundException
;
11 import com
.google
.appengine
.api
.datastore
.Index
;
12 import com
.google
.appengine
.api
.datastore
.Key
;
13 import com
.google
.appengine
.api
.datastore
.KeyRange
;
14 import com
.google
.appengine
.api
.datastore
.Transaction
;
15 import com
.google
.appengine
.api
.datastore
.TransactionOptions
;
16 import com
.google
.common
.collect
.ImmutableList
;
17 import com
.google
.common
.collect
.Maps
;
18 import com
.google
.common
.collect
.Sets
;
20 import java
.util
.Iterator
;
21 import java
.util
.List
;
26 * An implementation of {@link DatastoreService} using an overlay model. Conceptually, an overlay
27 * Datastore is based on some other Datastore (the "parent"). The overlay allows developers to
28 * effectively update or delete entities on the parent, but without actually modifying the data that
29 * the parent stores. (There is one exception: ID allocation is forwarded to the parent Datastore.)
31 final class OverlayDatastoreServiceImpl
extends OverlayBaseDatastoreServiceImpl
32 implements DatastoreService
{
33 private final DatastoreService datastore
;
34 private final DatastoreService parent
;
36 OverlayDatastoreServiceImpl(DatastoreService datastore
, DatastoreService parent
) {
37 this.datastore
= checkNotNull(datastore
);
38 this.parent
= checkNotNull(parent
);
42 public Entity
get(Key key
) throws EntityNotFoundException
{
44 return getImpl(datastore
, key
);
48 public Entity
get( Transaction txn
, Key key
) throws EntityNotFoundException
{
50 return getImpl(getTxnDatastore(txn
), key
);
53 private Entity
getImpl(DatastoreService datastore
, Key key
) throws EntityNotFoundException
{
54 checkNotNull(datastore
);
56 Entity result
= getImpl(datastore
, ImmutableList
.of(key
)).get(key
);
58 throw new EntityNotFoundException(key
);
64 public Map
<Key
, Entity
> get(Iterable
<Key
> keys
) {
66 return getImpl(datastore
, keys
);
70 public Map
<Key
, Entity
> get( Transaction txn
, Iterable
<Key
> keys
) {
72 return getImpl(getTxnDatastore(txn
), keys
);
75 private Map
<Key
, Entity
> getImpl(DatastoreService datastore
, Iterable
<Key
> keys
) {
76 checkNotNull(datastore
);
79 Map
<Key
, Entity
> results
= datastore
.get(OverlayUtils
.getKeysAndTombstoneKeys(keys
));
81 Set
<Key
> remainingKeys
= Sets
.newHashSet(keys
);
82 Iterator
<Map
.Entry
<Key
, Entity
>> entryIterator
= results
.entrySet().iterator();
83 while (entryIterator
.hasNext()) {
84 Map
.Entry
<Key
, Entity
> entry
= entryIterator
.next();
85 if (OverlayUtils
.isTombstone(entry
.getValue())) {
86 entryIterator
.remove();
87 remainingKeys
.remove(OverlayUtils
.getKeyFromTombstoneKey(entry
.getKey()));
89 remainingKeys
.remove(entry
.getKey());
93 results
.putAll(parent
.get(null, remainingKeys
));
99 public Key
put(Entity entity
) {
100 checkNotNull(entity
);
101 return put(ImmutableList
.of(entity
)).get(0);
105 public Key
put( Transaction txn
, Entity entity
) {
106 checkNotNull(entity
);
107 return put(txn
, ImmutableList
.of(entity
)).get(0);
111 public List
<Key
> put(Iterable
<Entity
> entities
) {
112 checkNotNull(entities
);
113 return putImpl(datastore
, entities
);
117 public List
<Key
> put( Transaction txn
, Iterable
<Entity
> entities
) {
118 checkNotNull(entities
);
119 return putImpl(getTxnDatastore(txn
), entities
);
122 private List
<Key
> putImpl(DatastoreService datastore
, Iterable
<Entity
> entities
) {
123 checkNotNull(datastore
);
124 checkNotNull(entities
);
126 Map
<IncompleteKey
, Integer
> idsNeededMap
= OverlayUtils
.getIdsNeededMap(entities
);
128 Map
<IncompleteKey
, Iterator
<Key
>> idsAllocatedMap
= Maps
.newHashMap();
129 for (Map
.Entry
<IncompleteKey
, Integer
> entry
: idsNeededMap
.entrySet()) {
130 IncompleteKey incompleteKey
= entry
.getKey();
131 idsAllocatedMap
.put(incompleteKey
, incompleteKey
.parent
== null
132 ?
allocateIds(incompleteKey
.kind
, entry
.getValue()).iterator()
133 : allocateIds(incompleteKey
.parent
, incompleteKey
.kind
, entry
.getValue()).iterator());
136 List
<Entity
> completedEntities
= OverlayUtils
.completeEntityKeys(entities
, idsAllocatedMap
);
138 List
<Key
> keys
= datastore
.put(completedEntities
);
140 datastore
.delete(OverlayUtils
.getTombstoneKeys(keys
));
146 public void delete(Key
... keys
) {
148 delete(ImmutableList
.copyOf(keys
));
152 public void delete( Transaction txn
, Key
... keys
) {
154 delete(txn
, ImmutableList
.copyOf(keys
));
158 public void delete(Iterable
<Key
> keys
) {
160 deleteImpl(datastore
, keys
);
164 public void delete( Transaction txn
, Iterable
<Key
> keys
) {
166 deleteImpl(getTxnDatastore(txn
), keys
);
169 private void deleteImpl(DatastoreService datastore
, Iterable
<Key
> keys
) {
170 checkNotNull(datastore
);
173 datastore
.put(OverlayUtils
.getTombstonesForKeys(keys
));
175 datastore
.delete(keys
);
179 public Transaction
beginTransaction() {
180 return datastore
.beginTransaction();
184 public Transaction
beginTransaction(TransactionOptions options
) {
185 checkNotNull(options
);
186 return datastore
.beginTransaction(options
);
190 public KeyRange
allocateIds(String kind
, long num
) {
192 return parent
.allocateIds(kind
, num
);
196 public KeyRange
allocateIds(Key parent
, String kind
, long num
) {
197 checkNotNull(parent
);
199 return this.parent
.allocateIds(parent
, kind
, num
);
203 public KeyRangeState
allocateIdRange(KeyRange range
) {
205 return this.parent
.allocateIdRange(range
);
209 public DatastoreAttributes
getDatastoreAttributes() {
210 return datastore
.getDatastoreAttributes();
214 public Map
<Index
, Index
.IndexState
> getIndexes() {
215 return datastore
.getIndexes();
219 public Map
<Key
, Entity
> getFromOverlayOnly( Transaction txn
, Iterable
<Key
> keys
) {
221 return datastore
.get(txn
, keys
);
225 protected BaseDatastoreService
getParentBaseDatastoreService() {
230 protected BaseDatastoreService
getUnderlyingBaseDatastoreService() {
235 * Gets a version of the Datastore that is linked to {@code txn}.
237 private DatastoreService
getTxnDatastore( Transaction txn
) {
238 return new TransactionLinkedDatastoreServiceImpl(datastore
, txn
);