1.9.30 sync.
[gae.git] / java / src / main / com / google / appengine / api / datastore / DatastoreApiHelper.java
blob162b978d785f35b2c561e89bc1e3bc7de51a5256
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.InvalidProtocolBufferException;
16 import com.google.protobuf.MessageLite;
17 import com.google.protobuf.Parser;
18 import com.google.rpc.Code;
20 import java.util.ConcurrentModificationException;
21 import java.util.concurrent.Future;
23 /**
24 * Helper methods and constants shared by classes that implement the Java api
25 * on top of the datastore.
27 * <p>Note: users should not access this class directly.
30 public final class DatastoreApiHelper {
32 static final String DATASTORE_V3_PACKAGE = "datastore_v3";
33 static final String CLOUD_DATASTORE_V1_PACKAGE = "cloud_datastore_v1beta3";
35 /**
36 * Key to put in {@link ApiProxy.Environment#getAttributes()} to override the app id used by the
37 * datastore api. If absent, {@link ApiProxy.Environment#getAppId()} will be used.
39 @SuppressWarnings("javadoc")
40 static final String APP_ID_OVERRIDE_KEY = "com.google.appengine.datastore.AppIdOverride";
42 private static final AppIdentityService appIdentityService =
43 AppIdentityServiceFactory.getAppIdentityService();
45 private DatastoreApiHelper() {}
47 public static RuntimeException translateError(ApiProxy.ApplicationException exception) {
48 Error.ErrorCode errorCode = Error.ErrorCode.valueOf(exception.getApplicationError());
49 if (errorCode == null) {
50 return new DatastoreFailureException(exception.getErrorDetail());
52 switch (errorCode) {
53 case BAD_REQUEST:
54 return new IllegalArgumentException(exception.getErrorDetail());
56 case CONCURRENT_TRANSACTION:
57 return new ConcurrentModificationException(exception.getErrorDetail());
59 case NEED_INDEX:
60 return new DatastoreNeedIndexException(exception.getErrorDetail());
62 case TIMEOUT:
63 case BIGTABLE_ERROR:
64 return new DatastoreTimeoutException(exception.getErrorDetail());
66 case COMMITTED_BUT_STILL_APPLYING:
67 return new CommittedButStillApplyingException(exception.getErrorDetail());
69 case INTERNAL_ERROR:
70 default:
71 return new DatastoreFailureException(exception.getErrorDetail());
75 static RuntimeException createV1Exception(Code code, String message, Datastore.Method method) {
76 if (code == null) {
77 return new DatastoreFailureException(message);
79 switch (code) {
80 case INVALID_ARGUMENT:
81 return new IllegalArgumentException(message);
82 case ABORTED:
83 return new ConcurrentModificationException(message);
84 case FAILED_PRECONDITION:
85 return new DatastoreNeedIndexException(message);
86 case DEADLINE_EXCEEDED:
87 return new DatastoreTimeoutException(message);
88 case PERMISSION_DENIED:
89 return new IllegalArgumentException(message);
90 case UNAVAILABLE:
91 return new ApiProxy.RPCFailedException(CLOUD_DATASTORE_V1_PACKAGE, method.name());
92 case RESOURCE_EXHAUSTED:
93 return new ApiProxy.OverQuotaException(CLOUD_DATASTORE_V1_PACKAGE, method.name());
94 case INTERNAL:
95 default:
96 return new DatastoreFailureException(message);
100 private static RuntimeException translateV1CanonicalCodeError(
101 ApiProxy.ApplicationException exception, Datastore.Method method) {
102 return createV1Exception(Code.valueOf(exception.getApplicationError()),
103 exception.getErrorDetail(), method);
106 static <T extends ProtocolMessage<T>> Future<T> makeAsyncCall(ApiConfig apiConfig,
107 final DatastoreService_3.Method method, ProtocolMessage<?> request, final T responseProto) {
108 Future<byte[]> response = ApiProxy.makeAsyncCall(DATASTORE_V3_PACKAGE, method.name(),
109 request.toByteArray(), apiConfig);
110 return new FutureWrapper<byte[], T>(response) {
111 @Override
112 protected T wrap(byte[] responseBytes) throws InvalidProtocolBufferException {
113 if (responseBytes != null) {
114 if (!responseProto.parseFrom(responseBytes)) {
115 throw new InvalidProtocolBufferException(String.format("Invalid %s.%s response",
116 DATASTORE_V3_PACKAGE, method.name()));
118 String initializationError = responseProto.findInitializationError();
119 if (initializationError != null) {
120 throw new InvalidProtocolBufferException(initializationError);
123 return responseProto;
126 @Override
127 protected Throwable convertException(Throwable cause) {
128 if (cause instanceof ApiProxy.ApplicationException) {
129 return translateError((ApiProxy.ApplicationException) cause);
131 return cause;
136 static <T extends MessageLite> Future<T> makeV1AsyncCall(ApiProxy.ApiConfig apiConfig,
137 Datastore.Method method, MessageLite request, Parser<T> responseParser) {
138 return makeV1AsyncCall(apiConfig, method, request.toByteArray(), responseParser);
141 static <T extends MessageLite> Future<T> makeV1AsyncCall(ApiProxy.ApiConfig apiConfig,
142 final Datastore.Method method, byte[] request, final Parser<T> responseParser) {
143 Future<byte[]> response = ApiProxy.makeAsyncCall(CLOUD_DATASTORE_V1_PACKAGE, method.name(),
144 request, apiConfig);
145 return new FutureWrapper<byte[], T>(response) {
146 @Override
147 protected T wrap(byte[] responseBytes) throws Exception {
148 if (responseBytes != null) {
149 return responseParser.parseFrom(responseBytes);
151 return responseParser.parsePartialFrom(new byte[0]);
154 @Override
155 protected Throwable convertException(Throwable cause) {
156 if (cause instanceof ApiProxy.ApplicationException) {
157 return translateV1CanonicalCodeError((ApiProxy.ApplicationException) cause, method);
159 return cause;
164 static String getCurrentProjectId() {
165 return toProjectId(getCurrentAppId());
168 static String toProjectId(String appId) {
169 ParsedAppId parsedAppId = appIdentityService.parseFullAppId(appId);
170 if (parsedAppId.getDomain().isEmpty()) {
171 return parsedAppId.getId();
172 } else {
173 return String.format("%s:%s", parsedAppId.getDomain(), parsedAppId.getId());
177 static String getCurrentAppId() {
178 ApiProxy.Environment environment = ApiProxy.getCurrentEnvironment();
179 if (environment == null) {
180 throw new NullPointerException("No API environment is registered for this thread.");
183 Object appIdOverride = environment.getAttributes().get(APP_ID_OVERRIDE_KEY);
184 if (appIdOverride != null) {
185 return (String) appIdOverride;
188 return environment.getAppId();
192 * Returns a new {@link AppIdNamespace} with the current appId and the namespace
193 * registered with the {@link NamespaceManager}
195 static AppIdNamespace getCurrentAppIdNamespace() {
196 return getCurrentAppIdNamespace(getCurrentAppId());
200 * Returns a new {@link AppIdNamespace} with the namespace currently
201 * registered with the {@link NamespaceManager} for a given appid.
203 static AppIdNamespace getCurrentAppIdNamespace(String appId) {
204 String namespace = NamespaceManager.get();
205 namespace = namespace == null ? "" : namespace;
206 return new AppIdNamespace(appId, namespace);