1 // Copyright 2010 Google Inc. All rights reserved.
2 package com
.google
.appengine
.api
.taskqueue
;
4 import com
.google
.appengine
.api
.datastore
.DatastoreApiHelper
;
5 import com
.google
.appengine
.api
.taskqueue
.TaskQueuePb
.TaskQueueServiceError
.ErrorCode
;
6 import com
.google
.appengine
.api
.utils
.FutureWrapper
;
7 import com
.google
.apphosting
.api
.ApiProxy
;
8 import com
.google
.apphosting
.api
.ApiProxy
.ApiConfig
;
9 import com
.google
.io
.protocol
.ProtocolMessage
;
11 import java
.util
.concurrent
.ExecutionException
;
12 import java
.util
.concurrent
.Future
;
15 * Provides translation of calls between userland and appserver land.
18 class QueueApiHelper
{
19 static final String PACKAGE
= "taskqueue";
21 <T
extends ProtocolMessage
<T
>, V
extends ProtocolMessage
<V
>> void makeSyncCall(
23 ProtocolMessage
<T
> request
,
24 ProtocolMessage
<V
> response
) {
26 byte[] responseBytes
=
27 ApiProxy
.makeSyncCall(PACKAGE
, method
, request
.toByteArray());
28 if (responseBytes
!= null) {
29 response
.mergeFrom(responseBytes
);
31 } catch (ApiProxy
.ApplicationException exception
) {
32 throw translateError(exception
);
37 * Issue an async rpc against the taskqueue package with the given request and
38 * response pbs as input and apply standard exception handling. Do not
39 * use this helper function if you need non-standard exception handling.
41 <T
extends ProtocolMessage
<T
>> Future
<T
> makeAsyncCall(
43 ProtocolMessage
<?
> request
,
44 final T responseProto
,
45 ApiConfig apiConfig
) {
46 Future
<byte[]> response
=
47 ApiProxy
.makeAsyncCall(PACKAGE
, method
, request
.toByteArray(), apiConfig
);
48 return new FutureWrapper
<byte[], T
>(response
) {
50 protected Throwable
convertException(Throwable cause
) {
51 if (cause
instanceof ApiProxy
.ApplicationException
) {
52 return translateError((ApiProxy
.ApplicationException
) cause
);
58 protected T
wrap(byte[] responseBytes
) {
59 if (responseBytes
!= null) {
60 responseProto
.parseFrom(responseBytes
);
68 * Extract the future's result.
70 static <T
> T
getInternal(Future
<T
> future
) {
73 } catch (InterruptedException e
) {
74 Thread
.currentThread().interrupt();
75 throw new ApiProxy
.ApplicationException(ErrorCode
.TRANSIENT_ERROR
.getValue(),
76 "Interrupted while waiting for RPC response.");
77 } catch (ExecutionException e
) {
78 Throwable cause
= e
.getCause();
79 if (cause
instanceof RuntimeException
) {
80 throw (RuntimeException
) cause
;
81 } else if (cause
instanceof Error
) {
84 throw new RuntimeException(cause
);
89 static RuntimeException
translateError(int error
, String detail
) {
90 ErrorCode errorCode
= ErrorCode
.valueOf(error
);
92 int datastoreErrorCode
= ErrorCode
.DATASTORE_ERROR
.getValue();
93 if (error
>= datastoreErrorCode
) {
94 ApiProxy
.ApplicationException datastoreApplicationException
=
95 new ApiProxy
.ApplicationException(error
- datastoreErrorCode
, detail
);
96 TransactionalTaskException taskqueueException
= new TransactionalTaskException();
97 taskqueueException
.initCause(
98 DatastoreApiHelper
.translateError(datastoreApplicationException
));
99 return taskqueueException
;
104 return new IllegalStateException("The specified queue is unknown : " + detail
);
105 case TRANSIENT_ERROR
:
106 return new TransientFailureException(detail
);
108 return new InternalFailureException(detail
);
110 return new IllegalArgumentException("Task size is too large : " + detail
);
111 case INVALID_TASK_NAME
:
112 return new IllegalArgumentException("Invalid task name : " + detail
);
113 case INVALID_QUEUE_NAME
:
114 return new IllegalArgumentException("Invalid queue name : " + detail
);
116 return new IllegalArgumentException("Invalid URL : " + detail
);
117 case INVALID_QUEUE_RATE
:
118 return new IllegalArgumentException("Invalid queue rate : " + detail
);
119 case PERMISSION_DENIED
:
120 return new SecurityException("Permission for requested operation is denied : " + detail
);
121 case TASK_ALREADY_EXISTS
:
122 return new TaskAlreadyExistsException("Task name already exists : " + detail
);
123 case TOMBSTONED_TASK
:
124 return new TaskAlreadyExistsException("Task name is tombstoned : " + detail
);
126 return new IllegalArgumentException("ETA is invalid : " + detail
);
127 case INVALID_REQUEST
:
128 return new IllegalArgumentException("Invalid request : " + detail
);
130 return new TaskNotFoundException("Task does not exist : " + detail
);
131 case TOMBSTONED_QUEUE
:
132 return new IllegalStateException(
133 "The queue has been marked for deletion and is no longer usable : " + detail
);
134 case DUPLICATE_TASK_NAME
:
135 return new IllegalArgumentException("Identical task names in request : " + detail
);
137 return new IllegalArgumentException("Request contains too many tasks : " + detail
);
138 case INVALID_QUEUE_MODE
:
139 return new InvalidQueueModeException(
140 "Target queue mode does not support this operation : " + detail
);
141 case TASK_LEASE_EXPIRED
:
142 return new IllegalStateException(
143 "The task lease has expired : " + detail
);
145 return new IllegalStateException(
146 "The queue is paused and cannot process the request : " + detail
);
148 return new QueueFailureException("Unspecified error (" + errorCode
+ ") : " + detail
);
152 static RuntimeException
translateError(ApiProxy
.ApplicationException exception
) {
153 return translateError(exception
.getApplicationError(), exception
.getErrorDetail());
156 public static void validateQueueName(String queueName
) {
157 if (queueName
== null || queueName
.length() == 0 ||
158 !QueueConstants
.QUEUE_NAME_PATTERN
.matcher(queueName
).matches()) {
159 throw new IllegalArgumentException(
160 "Queue name does not match expression " + QueueConstants
.QUEUE_NAME_REGEX
+
161 "; found '" + queueName
+ "'");