1 package com
.google
.appengine
.api
.labs
.datastore
.overlay
;
3 import static com
.google
.common
.base
.Preconditions
.checkNotNull
;
5 import com
.google
.appengine
.api
.NamespaceManager
;
6 import com
.google
.appengine
.api
.datastore
.AsyncDatastoreService
;
7 import com
.google
.appengine
.api
.datastore
.BaseDatastoreService
;
8 import com
.google
.appengine
.api
.datastore
.DatastoreAttributes
;
9 import com
.google
.appengine
.api
.datastore
.DatastoreFailureException
;
10 import com
.google
.appengine
.api
.datastore
.Entity
;
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
.apphosting
.api
.ApiProxy
;
17 import com
.google
.apphosting
.datastore
.DatastoreV4
;
18 import com
.google
.common
.collect
.ImmutableList
;
19 import com
.google
.protobuf
.InvalidProtocolBufferException
;
21 import java
.util
.List
;
23 import java
.util
.concurrent
.Future
;
26 * A thin wrapper around {@link AsyncDatastoreService}, where all operations are redirected to use
27 * an alternate namespace.
29 * <p>For operations which use the ambient namespace from the {@link NamespaceManager} (such as
30 * {@code allocateIds}), {@code namespacePrefix} is applied to the current namespace
31 * before completing the operation.
33 * <p>For operations whose arguments contain namespaces in some form (such as keys),
34 * {@code namespacePrefix} is applied to the relevant namespaces before completing the
37 * <p>In all cases, there are no lasting effects on the ambient namespace; it is restored to the
38 * original value after the operation is done.
40 final class NamespacePinnedAsyncDatastoreServiceImpl
extends NamespacePinnedBaseDatastoreServiceImpl
41 implements AsyncDatastoreService
{
42 private final AsyncDatastoreService datastore
;
45 * Constructs a new {@link NamespacePinnedAsyncDatastoreServiceImpl}.
47 * @param datastore the underlying {@link AsyncDatastoreService}
48 * @param namespacePrefix the prefix to apply to all namespaces
49 * @param shouldReserveIds a flag that indicates whether calling {@code put()} should explicitly
50 * reserve (not allocate) the numeric IDs used in any complete keys before storing them
52 NamespacePinnedAsyncDatastoreServiceImpl(AsyncDatastoreService datastore
, String namespacePrefix
,
53 boolean shouldReserveIds
) {
54 super(checkNotNull(namespacePrefix
), shouldReserveIds
);
55 this.datastore
= checkNotNull(datastore
);
59 public Future
<Entity
> get(Key key
) {
61 return getImpl(datastore
, key
);
65 public Future
<Entity
> get( Transaction txn
, Key key
) {
67 return getImpl(getTxnAsyncDatastore(txn
), key
);
70 private Future
<Entity
> getImpl(AsyncDatastoreService datastore
, Key key
) {
71 checkNotNull(datastore
);
73 return new RethrowingFutureWrapper
<Entity
, Entity
>(
74 datastore
.get(getAlternateNamespaceKey(key
))) {
76 protected Entity
wrap(Entity entity
) throws Exception
{
77 return getOriginalNamespaceEntity(entity
);
83 public Future
<Map
<Key
, Entity
>> get(Iterable
<Key
> keys
) {
85 return getImpl(datastore
, keys
);
89 public Future
<Map
<Key
, Entity
>> get( Transaction txn
, Iterable
<Key
> keys
) {
91 return getImpl(getTxnAsyncDatastore(txn
), keys
);
94 private Future
<Map
<Key
, Entity
>> getImpl(AsyncDatastoreService datastore
, Iterable
<Key
> keys
) {
95 checkNotNull(datastore
);
97 return new RethrowingFutureWrapper
<Map
<Key
, Entity
>, Map
<Key
, Entity
>>(
98 datastore
.get(getAlternateNamespaceKeys(keys
))) {
100 protected Map
<Key
, Entity
> wrap(Map
<Key
, Entity
> entityMap
) throws Exception
{
101 return getOriginalNamespaceEntityMap(entityMap
);
107 public Future
<Key
> put(Entity entity
) {
108 checkNotNull(entity
);
109 return putImpl(datastore
, entity
);
113 public Future
<Key
> put( Transaction txn
, Entity entity
) {
114 checkNotNull(entity
);
115 return putImpl(getTxnAsyncDatastore(txn
), entity
);
118 private Future
<Key
> putImpl(final AsyncDatastoreService datastore
, Entity entity
) {
119 checkNotNull(datastore
);
120 checkNotNull(entity
);
121 final Entity alternateEntity
= getAlternateNamespaceEntity(entity
);
122 if (shouldReserveIds
) {
123 return new RethrowingFutureWrapper
<Void
, Key
>(reserveId(alternateEntity
)) {
125 protected Key
wrap(Void v
) throws Exception
{
126 Key key
= datastore
.put(alternateEntity
).get();
127 return getOriginalNamespaceKey(key
);
131 return new RethrowingFutureWrapper
<Key
, Key
>(datastore
.put(alternateEntity
)) {
133 protected Key
wrap(Key key
) throws Exception
{
134 return getOriginalNamespaceKey(key
);
141 public Future
<List
<Key
>> put(Iterable
<Entity
> entities
) {
142 checkNotNull(entities
);
143 return putImpl(datastore
, entities
);
147 public Future
<List
<Key
>> put( Transaction txn
, Iterable
<Entity
> entities
) {
148 checkNotNull(entities
);
149 return putImpl(getTxnAsyncDatastore(txn
), entities
);
152 private Future
<List
<Key
>> putImpl(final AsyncDatastoreService datastore
,
153 Iterable
<Entity
> entities
) {
154 checkNotNull(datastore
);
155 checkNotNull(entities
);
156 final List
<Entity
> alternateEntities
= getAlternateNamespaceEntities(entities
);
157 if (shouldReserveIds
) {
158 return new RethrowingFutureWrapper
<Void
, List
<Key
>>(reserveIds(alternateEntities
)) {
160 protected List
<Key
> wrap(Void v
) throws Exception
{
161 List
<Key
> keys
= datastore
.put(alternateEntities
).get();
162 return getOriginalNamespaceKeys(keys
);
166 return new RethrowingFutureWrapper
<List
<Key
>, List
<Key
>>(datastore
.put(alternateEntities
)) {
168 protected List
<Key
> wrap(List
<Key
> keys
) throws Exception
{
169 return getOriginalNamespaceKeys(keys
);
176 public Future
<Void
> delete(Key
... keys
) {
178 return delete(ImmutableList
.copyOf(keys
));
182 public Future
<Void
> delete( Transaction txn
, Key
... keys
) {
184 return delete(txn
, ImmutableList
.copyOf(keys
));
188 public Future
<Void
> delete(Iterable
<Key
> keys
) {
190 return datastore
.delete(getAlternateNamespaceKeys(keys
));
194 public Future
<Void
> delete( Transaction txn
, Iterable
<Key
> keys
) {
196 return datastore
.delete(txn
, getAlternateNamespaceKeys(keys
));
200 public Future
<Transaction
> beginTransaction() {
201 return datastore
.beginTransaction();
205 public Future
<Transaction
> beginTransaction(TransactionOptions options
) {
206 checkNotNull(options
);
207 return datastore
.beginTransaction(options
);
211 public Future
<KeyRange
> allocateIds(String kind
, long num
) {
213 String currentNamespace
= NamespaceManager
.get();
215 NamespaceManager
.set(getAlternateNamespace(currentNamespace
));
216 return new RethrowingFutureWrapper
<KeyRange
, KeyRange
>(datastore
.allocateIds(kind
, num
)) {
218 protected KeyRange
wrap(KeyRange range
) throws Exception
{
219 return getOriginalNamespaceKeyRange(range
);
223 NamespaceManager
.set(currentNamespace
);
228 public Future
<KeyRange
> allocateIds(Key parent
, String kind
, long num
) {
229 checkNotNull(parent
);
231 String currentNamespace
= NamespaceManager
.get();
233 NamespaceManager
.set(getAlternateNamespace(parent
.getNamespace()));
234 return new RethrowingFutureWrapper
<KeyRange
, KeyRange
>(
235 datastore
.allocateIds(getAlternateNamespaceKey(parent
), kind
, num
)) {
237 protected KeyRange
wrap(KeyRange range
) throws Exception
{
238 return getOriginalNamespaceKeyRange(range
);
242 NamespaceManager
.set(currentNamespace
);
247 public Future
<DatastoreAttributes
> getDatastoreAttributes() {
248 return datastore
.getDatastoreAttributes();
252 public Future
<Map
<Index
, Index
.IndexState
>> getIndexes() {
253 return datastore
.getIndexes();
257 protected BaseDatastoreService
getUnderlyingBaseDatastoreService() {
262 * Issues an RPC to reserve the key of {@code entity}. This is used when encountering
263 * complete keys that have possibly been assigned IDs by the parent Datastore, but that the
264 * overlay may not have seen yet.
266 private static Future
<Void
> reserveId(Entity entity
) {
267 checkNotNull(entity
);
268 return reserveIds(ImmutableList
.of(entity
));
272 * Issues an RPC to reserve the keys of {@code entities}. This is used when encountering
273 * complete keys that have possibly been assigned IDs by the parent Datastore, but that the
274 * overlay may not have seen yet.
276 private static Future
<Void
> reserveIds(Iterable
<Entity
> entities
) {
277 checkNotNull(entities
);
278 DatastoreV4
.AllocateIdsRequest req
= makeReserveIdsRequest(entities
);
279 return new RethrowingFutureWrapper
<byte[], Void
>(
280 ApiProxy
.makeAsyncCall("datastore_v4", "AllocateIds", req
.toByteArray())) {
282 protected Void
wrap(byte[] respBytes
) throws Exception
{
284 DatastoreV4
.AllocateIdsResponse
.parseFrom(respBytes
);
285 } catch (InvalidProtocolBufferException e
) {
286 throw new DatastoreFailureException("error reserving IDs", e
);
294 protected BaseDatastoreService
getTxnBaseDatastore( Transaction txn
) {
295 return getTxnAsyncDatastore(txn
);
299 * Gets a version of the Datastore that is linked to {@code txn}.
301 private AsyncDatastoreService
getTxnAsyncDatastore( Transaction txn
) {
302 return new TransactionLinkedAsyncDatastoreServiceImpl(datastore
, txn
);