1 // Copyright 2008 Google Inc. All Rights Reserved.
2 package com
.google
.appengine
.api
.datastore
;
4 import com
.google
.appengine
.api
.NamespaceManager
;
5 import com
.google
.appengine
.api
.appidentity
.AppIdentityService
;
6 import com
.google
.appengine
.api
.appidentity
.AppIdentityService
.ParsedAppId
;
7 import com
.google
.appengine
.api
.appidentity
.AppIdentityServiceFactory
;
8 import com
.google
.appengine
.api
.utils
.FutureWrapper
;
9 import com
.google
.apphosting
.api
.ApiProxy
;
10 import com
.google
.apphosting
.api
.ApiProxy
.ApiConfig
;
11 import com
.google
.apphosting
.datastore
.DatastoreV3Pb
.DatastoreService_3
;
12 import com
.google
.apphosting
.datastore
.DatastoreV3Pb
.Error
;
13 import com
.google
.datastore
.v1beta3
.Datastore
;
14 import com
.google
.io
.protocol
.ProtocolMessage
;
15 import com
.google
.protobuf
.MessageLite
;
16 import com
.google
.protobuf
.Parser
;
17 import com
.google
.rpc
.Code
;
19 import java
.util
.ConcurrentModificationException
;
20 import java
.util
.concurrent
.Future
;
23 * Helper methods and constants shared by classes that implement the Java api
24 * on top of the datastore.
26 * <p>Note: users should not access this class directly.
29 public final class DatastoreApiHelper
{
31 static final String DATASTORE_V3_PACKAGE
= "datastore_v3";
32 static final String CLOUD_DATASTORE_V1_PACKAGE
= "cloud_datastore_v1";
35 * Key to put in {@link ApiProxy.Environment#getAttributes()} to override the app id used by the
36 * datastore api. If absent, {@link ApiProxy.Environment#getAppId()} will be used.
38 @SuppressWarnings("javadoc")
39 static final String APP_ID_OVERRIDE_KEY
= "com.google.appengine.datastore.AppIdOverride";
41 private static final AppIdentityService appIdentityService
=
42 AppIdentityServiceFactory
.getAppIdentityService();
44 private DatastoreApiHelper() {}
46 public static RuntimeException
translateError(ApiProxy
.ApplicationException exception
) {
47 Error
.ErrorCode errorCode
= Error
.ErrorCode
.valueOf(exception
.getApplicationError());
48 if (errorCode
== null) {
49 return new DatastoreFailureException(exception
.getErrorDetail());
53 return new IllegalArgumentException(exception
.getErrorDetail());
55 case CONCURRENT_TRANSACTION
:
56 return new ConcurrentModificationException(exception
.getErrorDetail());
59 return new DatastoreNeedIndexException(exception
.getErrorDetail());
63 return new DatastoreTimeoutException(exception
.getErrorDetail());
65 case COMMITTED_BUT_STILL_APPLYING
:
66 return new CommittedButStillApplyingException(exception
.getErrorDetail());
70 return new DatastoreFailureException(exception
.getErrorDetail());
74 static RuntimeException
createException(Code code
, String message
) {
76 return new DatastoreFailureException(message
);
79 case INVALID_ARGUMENT
:
80 return new IllegalArgumentException(message
);
82 return new ConcurrentModificationException(message
);
83 case FAILED_PRECONDITION
:
84 return new DatastoreNeedIndexException(message
);
85 case DEADLINE_EXCEEDED
:
86 return new DatastoreTimeoutException(message
);
87 case PERMISSION_DENIED
:
89 case RESOURCE_EXHAUSTED
:
90 return new IllegalStateException(message
);
93 return new DatastoreFailureException(message
);
97 private static RuntimeException
translateCanonicalCodeError(
98 ApiProxy
.ApplicationException exception
) {
99 return createException(Code
.valueOf(exception
.getApplicationError()),
100 exception
.getErrorDetail());
103 static <T
extends ProtocolMessage
<T
>> Future
<T
> makeAsyncCall(ApiConfig apiConfig
,
104 DatastoreService_3
.Method method
, ProtocolMessage
<?
> request
, final T responseProto
) {
105 Future
<byte[]> response
= ApiProxy
.makeAsyncCall(DATASTORE_V3_PACKAGE
, method
.name(),
106 request
.toByteArray(), apiConfig
);
107 return new FutureWrapper
<byte[], T
>(response
) {
109 protected T
wrap(byte[] responseBytes
) {
110 if (responseBytes
!= null) {
111 responseProto
.parseFrom(responseBytes
);
113 return responseProto
;
117 protected Throwable
convertException(Throwable cause
) {
118 if (cause
instanceof ApiProxy
.ApplicationException
) {
119 return translateError((ApiProxy
.ApplicationException
) cause
);
126 static <T
extends MessageLite
> Future
<T
> makeAsyncCall(ApiProxy
.ApiConfig apiConfig
,
127 Datastore
.Method method
, MessageLite request
, Parser
<T
> responseParser
) {
128 return makeAsyncCall(apiConfig
, method
, request
.toByteArray(), responseParser
);
131 static <T
extends MessageLite
> Future
<T
> makeAsyncCall(ApiProxy
.ApiConfig apiConfig
,
132 Datastore
.Method method
, byte[] request
, final Parser
<T
> responseParser
) {
133 Future
<byte[]> response
= ApiProxy
.makeAsyncCall(CLOUD_DATASTORE_V1_PACKAGE
, method
.name(),
135 return new FutureWrapper
<byte[], T
>(response
) {
137 protected T
wrap(byte[] responseBytes
) throws Exception
{
138 if (responseBytes
!= null) {
139 return responseParser
.parseFrom(responseBytes
);
141 return responseParser
.parsePartialFrom(new byte[0]);
145 protected Throwable
convertException(Throwable cause
) {
146 if (cause
instanceof ApiProxy
.ApplicationException
) {
147 return translateCanonicalCodeError((ApiProxy
.ApplicationException
) cause
);
154 static String
getCurrentProjectId() {
155 return toProjectId(getCurrentAppId());
158 static String
toProjectId(String appId
) {
159 ParsedAppId parsedAppId
= appIdentityService
.parseFullAppId(appId
);
160 if (parsedAppId
.getDomain().isEmpty()) {
161 return parsedAppId
.getId();
163 return String
.format("%s:%s", parsedAppId
.getDomain(), parsedAppId
.getId());
167 static String
getCurrentAppId() {
168 ApiProxy
.Environment environment
= ApiProxy
.getCurrentEnvironment();
169 if (environment
== null) {
170 throw new NullPointerException("No API environment is registered for this thread.");
173 Object appIdOverride
= environment
.getAttributes().get(APP_ID_OVERRIDE_KEY
);
174 if (appIdOverride
!= null) {
175 return (String
) appIdOverride
;
178 return environment
.getAppId();
182 * Returns a new {@link AppIdNamespace} with the current appId and the namespace
183 * registered with the {@link NamespaceManager}
185 static AppIdNamespace
getCurrentAppIdNamespace() {
186 return getCurrentAppIdNamespace(getCurrentAppId());
190 * Returns a new {@link AppIdNamespace} with the namespace currently
191 * registered with the {@link NamespaceManager} for a given appid.
193 static AppIdNamespace
getCurrentAppIdNamespace(String appId
) {
194 String namespace
= NamespaceManager
.get();
195 namespace
= namespace
== null ?
"" : namespace
;
196 return new AppIdNamespace(appId
, namespace
);