Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / datastore / AdminDatastoreService.java
blobd94f251bf2d8872cb4fb14c153408802773c26a6
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;
21 import java.util.Map;
22 import java.util.Set;
23 import java.util.concurrent.Future;
25 /**
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.
41 @AppEngineInternal
42 public final class AdminDatastoreService implements AsyncDatastoreService {
44 /**
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;
51 private String kind;
53 private QueryBuilder(AppIdNamespace appIdNamespace) {
54 this.appIdNamespace = appIdNamespace;
57 public QueryBuilder setKind(String kind) {
58 this.kind = kind;
59 return this;
62 public String getKind() {
63 return kind;
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;
75 protected Long id;
76 protected Key parent;
78 protected AbstractKeyBuilder(AppIdNamespace appIdNamespace, String kind) {
79 this.appIdNamespace = appIdNamespace;
80 setKind(kind);
83 public String getKind() {
84 return kind;
87 @SuppressWarnings("unchecked")
88 public T setKind(String kind) {
89 checkNotNull(kind);
90 this.kind = kind;
91 return (T) this;
94 public String getName() {
95 return name;
98 @SuppressWarnings("unchecked")
99 public T setName(String name) {
100 this.name = name;
101 return (T) this;
104 public Long getId() {
105 return id;
108 protected long getIdAsPrimitiveLong() {
109 return id == null ? Key.NOT_ASSIGNED : id;
112 @SuppressWarnings("unchecked")
113 public T setId(Long id) {
114 this.id = id;
115 return (T) this;
118 public Key getParent() {
119 return parent;
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;
129 return (T) this;
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);
147 public Key build() {
148 return createKey();
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() {
176 @Override
177 public AsyncDatastoreServiceImpl getInstance(DatastoreServiceConfig config) {
178 return (AsyncDatastoreServiceImpl) DatastoreServiceFactory.getAsyncDatastoreService(config);
181 @Override
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);
264 if (index != null) {
265 resultSet.add(IndexTranslator.convertFromPb(index));
268 return resultSet;
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);
291 if (index != null) {
292 resultSet.add(IndexTranslator.convertFromPb(index));
295 return resultSet;
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;
320 @Override
321 public PreparedQuery prepare(Query query) {
322 validateQuery(query);
323 return delegate.prepare(query);
326 @Override
327 public PreparedQuery prepare(Transaction txn, Query query) {
328 validateQuery(query);
329 return delegate.prepare(txn, query);
332 @Override
333 public Transaction getCurrentTransaction() {
334 return delegate.getCurrentTransaction();
337 @Override
338 public Transaction getCurrentTransaction(Transaction returnedIfNoTxn) {
339 return delegate.getCurrentTransaction(returnedIfNoTxn);
342 @Override
343 public Collection<Transaction> getActiveTransactions() {
344 return delegate.getActiveTransactions();
347 @Override
348 public Future<Transaction> beginTransaction() {
349 return delegate.beginTransaction();
352 @Override
353 public Future<Transaction> beginTransaction(TransactionOptions options) {
354 return delegate.beginTransaction(options);
357 @Override
358 public Future<Entity> get(Key key) {
359 validateKey(key);
360 return delegate.get(key);
363 @Override
364 public Future<Entity> get(Transaction txn, Key key) {
365 validateKey(key);
366 return delegate.get(txn, key);
369 @Override
370 public Future<Map<Key, Entity>> get(Iterable<Key> keys) {
371 validateKeys(keys);
372 return delegate.get(keys);
375 @Override
376 public Future<Map<Key, Entity>> get(Transaction txn, Iterable<Key> keys) {
377 validateKeys(keys);
378 return delegate.get(txn, keys);
381 @Override
382 public Future<Key> put(Entity entity) {
383 validateEntity(entity);
384 return delegate.put(entity);
387 @Override
388 public Future<Key> put(Transaction txn, Entity entity) {
389 validateEntity(entity);
390 return delegate.put(txn, entity);
393 @Override
394 public Future<List<Key>> put(Iterable<Entity> entities) {
395 validateEntities(entities);
396 return delegate.put(entities);
399 @Override
400 public Future<List<Key>> put(Transaction txn, Iterable<Entity> entities) {
401 validateEntities(entities);
402 return delegate.put(txn, entities);
405 @Override
406 public Future<Void> delete(Key... keys) {
407 validateKeys(Arrays.asList(keys));
408 return delegate.delete(keys);
411 @Override
412 public Future<Void> delete(Transaction txn, Key... keys) {
413 validateKeys(Arrays.asList(keys));
414 return delegate.delete(txn, keys);
417 @Override
418 public Future<Void> delete(Iterable<Key> keys) {
419 validateKeys(keys);
420 return delegate.delete(keys);
423 @Override
424 public Future<Void> delete(Transaction txn, Iterable<Key> keys) {
425 validateKeys(keys);
426 return delegate.delete(txn, keys);
429 @Override
430 public Future<KeyRange> allocateIds(String kind, long num) {
431 return delegate.allocateIds(kind, num);
434 @Override
435 public Future<KeyRange> allocateIds(Key parent, String kind, long num) {
436 return delegate.allocateIds(parent, kind, num);
439 @Override
440 public Future<DatastoreAttributes> getDatastoreAttributes() {
441 return delegate.getDatastoreAttributes();
444 @Override
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) {
461 validateKey(key);
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);