1 // Copyright 2010 Google Inc. All Rights Reserved.
3 package com
.google
.appengine
.api
.datastore
;
5 import static com
.google
.common
.base
.Preconditions
.checkArgument
;
7 import com
.google
.apphosting
.api
.ApiProxy
;
8 import com
.google
.apphosting
.api
.DatastorePb
;
10 import java
.util
.concurrent
.Future
;
11 import java
.util
.logging
.Logger
;
14 * State and behavior that is common to both synchronous and asynchronous
15 * Datastore API implementations.
18 abstract class BaseDatastoreServiceImpl
{
20 * It doesn't actually matter what this value is, the back end will set its
21 * own deadline. All that matters is that we set a value.
23 static final long ARBITRARY_FAILOVER_READ_MS
= -1;
26 * User-provided config options.
28 private final DatastoreServiceConfig datastoreServiceConfig
;
31 * Config that we'll pass to all api calls.
33 final ApiProxy
.ApiConfig apiConfig
;
36 * Knows which transaction to use when the user does not explicitly provide
39 final TransactionStack defaultTxnProvider
;
41 final Logger logger
= Logger
.getLogger(getClass().getName());
43 BaseDatastoreServiceImpl(DatastoreServiceConfig datastoreServiceConfig
,
44 TransactionStack defaultTxnProvider
) {
45 this.datastoreServiceConfig
= datastoreServiceConfig
;
46 this.apiConfig
= createApiConfig(datastoreServiceConfig
);
47 this.defaultTxnProvider
= defaultTxnProvider
;
50 protected DatastoreServiceConfig
getDatastoreServiceConfig() {
51 return datastoreServiceConfig
;
54 private ApiProxy
.ApiConfig
createApiConfig(DatastoreServiceConfig config
) {
55 ApiProxy
.ApiConfig apiConfig
= new ApiProxy
.ApiConfig();
56 apiConfig
.setDeadlineInSeconds(config
.getDeadline());
61 * Helper class used to encapsulate the result of a call to
62 * {@link #getOrCreateTransaction()}.
64 static final class GetOrCreateTransactionResult
{
66 private final boolean isNew
;
67 private final Transaction txn
;
69 GetOrCreateTransactionResult(boolean isNew
,Transaction txn
) {
75 * @return {@code true} if the Transaction was created and should therefore
76 * be closed before the end of the operation, {@code false} otherwise.
78 public boolean isNew() {
83 * @return The Transaction to use. Can be {@code null}.
85 public Transaction
getTransaction() {
90 static void validateQuery(Query query
) {
91 checkArgument(query
.getFilterPredicates().isEmpty() || query
.getFilter() == null,
92 "A query cannot have both a filter and filter predicates set.");
93 checkArgument(query
.getProjections().isEmpty() || !query
.isKeysOnly(),
94 "A query cannot have both projections and keys-only set.");
98 * Return the current transaction if one already exists, otherwise create
99 * a new transaction or throw an exception according to the
100 * {@link ImplicitTransactionManagementPolicy}.
102 GetOrCreateTransactionResult
getOrCreateTransaction() {
103 Transaction currentTxn
= getCurrentTransaction(null);
104 if (currentTxn
!= null) {
105 return new GetOrCreateTransactionResult(false, currentTxn
);
108 switch(datastoreServiceConfig
.getImplicitTransactionManagementPolicy()) {
110 return new GetOrCreateTransactionResult(false, null);
112 return new GetOrCreateTransactionResult(true, beginTransactionInternal(
113 TransactionOptions
.Builder
.withDefaults()));
115 final String msg
= "Unexpected Transaction Creation Policy: " +
116 datastoreServiceConfig
.getImplicitTransactionManagementPolicy();
118 throw new IllegalArgumentException(msg
);
122 static DatastorePb
.Transaction
localTxnToRemoteTxn(Transaction local
) {
123 DatastorePb
.Transaction remote
= new DatastorePb
.Transaction();
124 remote
.setApp(local
.getApp());
125 remote
.setHandle(Long
.parseLong(local
.getId()));
129 Transaction
beginTransactionInternal(TransactionOptions options
) {
130 DatastorePb
.Transaction remoteTxn
= new DatastorePb
.Transaction();
131 DatastorePb
.BeginTransactionRequest request
= new DatastorePb
.BeginTransactionRequest();
132 request
.setApp(DatastoreApiHelper
.getCurrentAppId());
133 request
.setAllowMultipleEg(options
.isXG());
135 Future
<DatastorePb
.Transaction
> future
=
136 DatastoreApiHelper
.makeAsyncCall(apiConfig
, "BeginTransaction", request
, remoteTxn
);
138 Transaction localTxn
= new TransactionImpl(apiConfig
, request
.getApp(), future
,
139 defaultTxnProvider
, datastoreServiceConfig
.getDatastoreCallbacks());
141 defaultTxnProvider
.push(localTxn
);
145 public Transaction
getCurrentTransaction() {
146 return defaultTxnProvider
.peek();
149 public Transaction
getCurrentTransaction(Transaction returnedIfNoTxn
) {
150 return defaultTxnProvider
.peek(returnedIfNoTxn
);