1 // Copyright 2013 Google Inc. All Rights Reserved.
2 package com
.google
.appengine
.api
.datastore
;
4 import static com
.google
.common
.base
.Preconditions
.checkArgument
;
5 import static com
.google
.common
.base
.Preconditions
.checkNotNull
;
7 import com
.google
.appengine
.api
.datastore
.CompositeIndexManager
.IndexComponentsOnlyQuery
;
8 import com
.google
.appengine
.api
.datastore
.Index
.IndexState
;
9 import com
.google
.appengine
.api
.datastore
.Query
.FilterPredicate
;
10 import com
.google
.apphosting
.api
.AppEngineInternal
;
11 import com
.google
.apphosting
.datastore
.DatastoreV3Pb
;
12 import com
.google
.common
.collect
.Iterables
;
13 import com
.google
.common
.collect
.Lists
;
14 import com
.google
.storage
.onestore
.v3
.OnestoreEntity
;
16 import java
.util
.ArrayList
;
17 import java
.util
.Arrays
;
18 import java
.util
.Collection
;
19 import java
.util
.HashSet
;
20 import java
.util
.List
;
23 import java
.util
.concurrent
.Future
;
26 * An AsyncDatastoreService implementation that is pinned to a specific appId and namesapce.
27 * This implementation ignores the "current" appId provided by
28 * {@code ApiProxy.getCurrentEnvironment().getAppId()} and the "current" namespace provided
29 * by {@code NamespaceManager.get()}.
30 * Note, this is particularly important in the following methods:<ul>
31 * <li>{@link AsyncDatastoreService#getIndexes()}</li>
32 * <li>{@link AsyncDatastoreService#getDatastoreAttributes()}</li>
33 * <li>{@link AsyncDatastoreService#allocateIds(String, long)}</li></ul>
35 * In addition this class provides ways to create {@link Query}, {@link Entity} and {@link Key}
36 * that are pinned to the same appId/namespace.
38 * <p>Note: users should not access this class directly.
42 public final class AdminDatastoreService
implements AsyncDatastoreService
{
45 * A {@link Query} builder that pins it to the AdminUtils {@code appId}
46 * and {@code namespace}.
48 public static final class QueryBuilder
{
50 private final AppIdNamespace appIdNamespace
;
53 private QueryBuilder(AppIdNamespace appIdNamespace
) {
54 this.appIdNamespace
= appIdNamespace
;
57 public QueryBuilder
setKind(String kind
) {
62 public String
getKind() {
66 public Query
build() {
67 return new Query(kind
, null, null, false, appIdNamespace
, false, null);
71 private abstract static class AbstractKeyBuilder
<T
extends AbstractKeyBuilder
<T
>> {
72 protected final AppIdNamespace appIdNamespace
;
73 protected String kind
;
74 protected String name
;
78 protected AbstractKeyBuilder(AppIdNamespace appIdNamespace
, String kind
) {
79 this.appIdNamespace
= appIdNamespace
;
83 public String
getKind() {
87 @SuppressWarnings("unchecked")
88 public T
setKind(String kind
) {
94 public String
getName() {
98 @SuppressWarnings("unchecked")
99 public T
setName(String name
) {
104 public Long
getId() {
108 protected long getIdAsPrimitiveLong() {
109 return id
== null ? Key
.NOT_ASSIGNED
: id
;
112 @SuppressWarnings("unchecked")
113 public T
setId(Long id
) {
118 public Key
getParent() {
123 * When {@code parent} is not {@code null} its application and namespace will be used
124 * instead of the ones associated with AdminUtils.
126 @SuppressWarnings("unchecked")
127 public T
setParent(Key parent
) {
128 this.parent
= parent
;
132 protected Key
createKey() {
133 return new Key(kind
, parent
, getIdAsPrimitiveLong(), name
, appIdNamespace
);
138 * A {@link Key} builder that pins it to the AdminUtils {@code appId}
139 * and {@code namespace}.
141 public static final class KeyBuilder
extends AbstractKeyBuilder
<KeyBuilder
> {
143 private KeyBuilder(AppIdNamespace appIdNamespace
, String kind
) {
144 super(appIdNamespace
, kind
);
153 * An {@link Entity} builder that pins it to the AdminUtils {@code appId}
154 * and {@code namespace}.
156 public static final class EntityBuilder
extends AbstractKeyBuilder
<EntityBuilder
> {
158 private EntityBuilder(AppIdNamespace appIdNamespace
, String kind
) {
159 super(appIdNamespace
, kind
);
162 public Entity
build() {
163 return new Entity(createKey());
167 private final AsyncDatastoreServiceImpl delegate
;
168 private final AppIdNamespace appIdNamespace
;
170 interface AsyncDatastoreServiceImplFactory
{
171 AsyncDatastoreServiceImpl
getInstance(DatastoreServiceConfig config
);
172 CompositeIndexManager
getCompositeIndexManager();
175 static AsyncDatastoreServiceImplFactory factory
= new AsyncDatastoreServiceImplFactory() {
177 public AsyncDatastoreServiceImpl
getInstance(DatastoreServiceConfig config
) {
178 return (AsyncDatastoreServiceImpl
) DatastoreServiceFactory
.getAsyncDatastoreService(config
);
182 public CompositeIndexManager
getCompositeIndexManager() {
183 return new CompositeIndexManager();
187 private AdminDatastoreService(DatastoreServiceConfig config
, String appId
, String namespace
) {
188 appIdNamespace
= new AppIdNamespace(appId
, namespace
);
189 config
= new DatastoreServiceConfig(config
).appIdNamespace(appIdNamespace
);
190 delegate
= factory
.getInstance(config
);
194 * Returns an AdminUtils instance for the given {@code appId} and the "" (empty) namespace.
196 public static AdminDatastoreService
getInstance(String appId
) {
197 return getInstance(DatastoreServiceConfig
.Builder
.withDefaults(), appId
, "");
201 * Returns an AdminUtils instance for the given {@code appId} and {@code namespace}.
203 public static AdminDatastoreService
getInstance(String appId
, String namespace
) {
204 return getInstance(DatastoreServiceConfig
.Builder
.withDefaults(), appId
, namespace
);
208 * Returns an AdminUtils instance for the given {@code appId} and the "" (empty) namespace.
210 public static AdminDatastoreService
getInstance(DatastoreServiceConfig config
, String appId
) {
211 return getInstance(config
, appId
, "");
215 * Returns an AdminUtils instance for the given {@code appId} and {@code namespace}.
217 public static AdminDatastoreService
getInstance(
218 DatastoreServiceConfig config
, String appId
, String namespace
) {
219 return new AdminDatastoreService(config
, appId
, namespace
);
222 public String
getAppId() {
223 return appIdNamespace
.getAppId();
226 public String
getNamespace() {
227 return appIdNamespace
.getNamespace();
230 DatastoreServiceConfig
getDatastoreServiceConfig() {
231 return delegate
.getDatastoreServiceConfig();
234 public QueryBuilder
newQueryBuilder() {
235 return new QueryBuilder(appIdNamespace
);
238 public QueryBuilder
newQueryBuilder(String kind
) {
239 return new QueryBuilder(appIdNamespace
).setKind(kind
);
242 public KeyBuilder
newKeyBuilder(String kind
) {
243 return new KeyBuilder(appIdNamespace
, kind
);
246 public EntityBuilder
newEntityBuilder(String kind
) {
247 return new EntityBuilder(appIdNamespace
, kind
);
250 public Index
compositeIndexForQuery(Query query
) {
251 Set
<Index
> resultIndexes
= compositeIndexesForQuery(query
);
252 return Iterables
.getFirst(resultIndexes
, null);
255 public Set
<Index
> compositeIndexesForQuery(Query query
) {
256 List
<DatastoreV3Pb
.Query
> pbQueries
= convertQueryToPbs(query
,
257 FetchOptions
.Builder
.withDefaults());
258 Set
<Index
> resultSet
= new HashSet
<Index
>();
259 for (DatastoreV3Pb
.Query queryProto
: pbQueries
) {
260 IndexComponentsOnlyQuery indexQuery
= new IndexComponentsOnlyQuery(queryProto
);
262 OnestoreEntity
.Index index
=
263 factory
.getCompositeIndexManager().compositeIndexForQuery(indexQuery
);
265 resultSet
.add(IndexTranslator
.convertFromPb(index
));
271 public Index
minimumCompositeIndexForQuery(Query query
, Collection
<Index
> indexes
) {
272 Set
<Index
> resultIndexes
= minimumCompositeIndexesForQuery(query
, indexes
);
273 return Iterables
.getFirst(resultIndexes
, null);
276 public Set
<Index
> minimumCompositeIndexesForQuery(Query query
, Collection
<Index
> indexes
) {
277 List
<DatastoreV3Pb
.Query
> pbQueries
= convertQueryToPbs(query
,
278 FetchOptions
.Builder
.withDefaults());
280 List
<OnestoreEntity
.Index
> indexPbs
= Lists
.newArrayListWithCapacity(indexes
.size());
281 for (Index index
: indexes
) {
282 indexPbs
.add(IndexTranslator
.convertToPb(index
));
285 Set
<Index
> resultSet
= new HashSet
<Index
>();
286 for (DatastoreV3Pb
.Query queryProto
: pbQueries
) {
287 IndexComponentsOnlyQuery indexQuery
= new IndexComponentsOnlyQuery(queryProto
);
289 OnestoreEntity
.Index index
=
290 factory
.getCompositeIndexManager().minimumCompositeIndexForQuery(indexQuery
, indexPbs
);
292 resultSet
.add(IndexTranslator
.convertFromPb(index
));
299 * Convert a query to a list of ProtocolBuffer Queries.
301 private static List
<DatastoreV3Pb
.Query
> convertQueryToPbs(Query query
,
302 FetchOptions fetchOptions
) {
303 List
<MultiQueryBuilder
> queriesToRun
= QuerySplitHelper
.splitQuery(query
);
304 query
.setFilter(null);
305 query
.getFilterPredicates().clear();
306 List
<DatastoreV3Pb
.Query
> resultQueries
= new ArrayList
<DatastoreV3Pb
.Query
>();
307 for (MultiQueryBuilder multiQuery
: queriesToRun
) {
308 for (List
<List
<FilterPredicate
>> parallelQueries
: multiQuery
) {
309 for (List
<FilterPredicate
> singleQuery
: parallelQueries
) {
310 Query newQuery
= new Query(query
);
311 newQuery
.getFilterPredicates().addAll(singleQuery
);
312 DatastoreV3Pb
.Query queryProto
= QueryTranslator
.convertToPb(newQuery
, fetchOptions
);
313 resultQueries
.add(queryProto
);
317 return resultQueries
;
321 public PreparedQuery
prepare(Query query
) {
322 validateQuery(query
);
323 return delegate
.prepare(query
);
327 public PreparedQuery
prepare(Transaction txn
, Query query
) {
328 validateQuery(query
);
329 return delegate
.prepare(txn
, query
);
333 public Transaction
getCurrentTransaction() {
334 return delegate
.getCurrentTransaction();
338 public Transaction
getCurrentTransaction(Transaction returnedIfNoTxn
) {
339 return delegate
.getCurrentTransaction(returnedIfNoTxn
);
343 public Collection
<Transaction
> getActiveTransactions() {
344 return delegate
.getActiveTransactions();
348 public Future
<Transaction
> beginTransaction() {
349 return delegate
.beginTransaction();
353 public Future
<Transaction
> beginTransaction(TransactionOptions options
) {
354 return delegate
.beginTransaction(options
);
358 public Future
<Entity
> get(Key key
) {
360 return delegate
.get(key
);
364 public Future
<Entity
> get(Transaction txn
, Key key
) {
366 return delegate
.get(txn
, key
);
370 public Future
<Map
<Key
, Entity
>> get(Iterable
<Key
> keys
) {
372 return delegate
.get(keys
);
376 public Future
<Map
<Key
, Entity
>> get(Transaction txn
, Iterable
<Key
> keys
) {
378 return delegate
.get(txn
, keys
);
382 public Future
<Key
> put(Entity entity
) {
383 validateEntity(entity
);
384 return delegate
.put(entity
);
388 public Future
<Key
> put(Transaction txn
, Entity entity
) {
389 validateEntity(entity
);
390 return delegate
.put(txn
, entity
);
394 public Future
<List
<Key
>> put(Iterable
<Entity
> entities
) {
395 validateEntities(entities
);
396 return delegate
.put(entities
);
400 public Future
<List
<Key
>> put(Transaction txn
, Iterable
<Entity
> entities
) {
401 validateEntities(entities
);
402 return delegate
.put(txn
, entities
);
406 public Future
<Void
> delete(Key
... keys
) {
407 validateKeys(Arrays
.asList(keys
));
408 return delegate
.delete(keys
);
412 public Future
<Void
> delete(Transaction txn
, Key
... keys
) {
413 validateKeys(Arrays
.asList(keys
));
414 return delegate
.delete(txn
, keys
);
418 public Future
<Void
> delete(Iterable
<Key
> keys
) {
420 return delegate
.delete(keys
);
424 public Future
<Void
> delete(Transaction txn
, Iterable
<Key
> keys
) {
426 return delegate
.delete(txn
, keys
);
430 public Future
<KeyRange
> allocateIds(String kind
, long num
) {
431 return delegate
.allocateIds(kind
, num
);
435 public Future
<KeyRange
> allocateIds(Key parent
, String kind
, long num
) {
436 return delegate
.allocateIds(parent
, kind
, num
);
440 public Future
<DatastoreAttributes
> getDatastoreAttributes() {
441 return delegate
.getDatastoreAttributes();
445 public Future
<Map
<Index
, IndexState
>> getIndexes() {
446 return delegate
.getIndexes();
449 private void validateQuery(Query query
) {
450 checkArgument(query
.getAppIdNamespace().equals(appIdNamespace
),
451 "Query is associated with a different appId or namespace");
454 private void validateKey(Key key
) {
455 checkArgument(key
.getAppIdNamespace().equals(appIdNamespace
),
456 "key %s is associated with a different appId or namespace", key
);
459 private void validateKeys(Iterable
<Key
> keys
) {
460 for (Key key
: keys
) {
465 private void validateEntity(Entity entity
) {
466 checkArgument(entity
.getAppIdNamespace().equals(appIdNamespace
),
467 "Entity %s is associated with a different appId or namespace", entity
.getKey());
470 private void validateEntities(Iterable
<Entity
> entities
) {
471 for (Entity entity
: entities
) {
472 validateEntity(entity
);