1 // Copyright 2010 Google Inc. All rights reserved.
2 package com
.google
.appengine
.api
.taskqueue
;
4 import java
.io
.IOException
;
5 import java
.io
.ObjectInputStream
;
6 import java
.io
.Serializable
;
7 import java
.io
.UnsupportedEncodingException
;
8 import java
.net
.URLDecoder
;
9 import java
.util
.ArrayList
;
10 import java
.util
.List
;
14 * Created from {@link Queue#add(TaskOptions)}. Contains the
15 * task name (generated if otherwise unspecified), task ETA (computed if
16 * not specified) and queue name. The queue name and task name
17 * uniquely identify the task for an application.
20 public final class TaskHandle
implements Serializable
{
21 private static final long serialVersionUID
= -2578988193753847512L;
22 private String taskName
;
23 private String queueName
;
25 private long etaMillis
;
26 private Integer retryCount
;
27 private TaskOptions options
;
29 TaskHandle(TaskOptions options
, String queueName
,Integer retryCount
) {
30 validateTaskName(options
.getTaskName());
31 QueueApiHelper
.validateQueueName(queueName
);
32 this.queueName
= queueName
;
33 this.retryCount
= retryCount
;
36 this.options
= new TaskOptions(options
);
37 setEtaUsecFromOptions(this.options
);
40 public TaskHandle(TaskOptions options
, String queueName
) {
41 this(options
, queueName
, 0);
45 * @deprecated Use {@link TaskHandle#TaskHandle(TaskOptions, String)}
48 public TaskHandle(String name
, String queueName
, long etaMillis
) {
49 this(TaskOptions
.Builder
.withTaskName(name
).etaMillis(etaMillis
),
53 private void readObject(ObjectInputStream in
) throws IOException
, ClassNotFoundException
{
54 in
.defaultReadObject();
55 if (options
== null) {
56 options
= TaskOptions
.Builder
.withTaskName(taskName
).etaMillis(etaMillis
);
60 if (etaUsec
== 0 && options
.getEtaMillis() != null) {
61 setEtaUsecFromOptions(options
);
66 public int hashCode() {
68 int result
= options
.hashCode();
69 result
= result
* prime
+ queueName
.hashCode();
70 result
= result
* prime
+ (retryCount
== null ?
0 : retryCount
.intValue());
71 result
= result
* prime
+ (int) (etaUsec ^
(etaUsec
>>> 32));
76 public boolean equals(Object obj
) {
77 if (this == obj
) return true;
78 if (obj
== null) return false;
79 if (getClass() != obj
.getClass()) return false;
80 TaskHandle other
= (TaskHandle
) obj
;
81 if (options
== null) {
82 if (other
.options
!= null) return false;
83 } else if (!options
.equals(other
.options
)) return false;
84 if (queueName
== null) {
85 if (other
.queueName
!= null) return false;
86 } else if (!queueName
.equals(other
.queueName
)) return false;
87 if (retryCount
== null) {
88 if (other
.retryCount
!= null) return false;
89 } else if (!retryCount
.equals(other
.retryCount
)) return false;
90 if (etaUsec
!= other
.etaUsec
) {
97 public String
toString() {
98 return "TaskHandle[options=" + options
.toString() + "queueName=" + queueName
99 + ", retryCount=" + retryCount
+ ", etaMillis = " + etaMillis
100 + ", etaUsec = " + etaUsec
+ "]";
104 * Checks the name of this task matches QueueConstants.TASK_NAME_PATTERN
106 * @throws IllegalArgumentException
108 static void validateTaskName(String taskName
) {
109 if (taskName
== null || taskName
.length() == 0 ||
110 !QueueConstants
.TASK_NAME_PATTERN
.matcher(taskName
).matches()) {
111 throw new IllegalArgumentException(
112 "Task name does not match expression " + QueueConstants
.TASK_NAME_REGEX
+
113 "; given taskname: '" + taskName
+ "'");
118 * Returns the name of this task. This may have been generated
119 * by a call to {@link Queue#add()} if the name was not otherwise specified.
121 public String
getName() {
122 return options
.getTaskName();
126 * Returns the name of the queue that this task was submitted into.
128 public String
getQueueName() {
133 * Returns a time comparable to {@link System#currentTimeMillis()} when
134 * this task is scheduled for execution.
136 public long getEtaMillis() {
137 return options
.getEtaMillis();
141 * Set the time comparable to {@link System#currentTimeMillis()} when
142 * this task is scheduled for execution. For pull tasks this value specifies
143 * the lease expiration time.
145 void etaMillis(long etaMillis
) {
146 options
.etaMillis(etaMillis
);
147 etaUsec
= etaMillis
* 1000;
151 * Returns the time when the task is scheduled for execution to microsecond
152 * precision. For pull tasks this value specifies the lease expiration time.
153 * Microsecond precision is required for the lease verification check when
154 * modifying the task lease.
161 * Set the time for when this task is scheduled for execution to microsecond
162 * precision. For pull tasks this value specifies the lease expiraion time.
164 TaskHandle
etaUsec(long etaUsec
) {
165 this.etaUsec
= etaUsec
;
166 options
.etaMillis(etaUsec
/ 1000);
171 * Returns number of leases that had been performed on this task.
172 * Can return {@code null}.
174 public Integer
getRetryCount() {
179 * Returns binary payload data of this task.
180 * Can return {@code null}.
182 public byte[] getPayload() {
183 return options
.getPayload();
187 * Returns tag of this task. Can return {@code null}.
188 * @throws UnsupportedEncodingException
190 public String
getTag() throws UnsupportedEncodingException
{
191 return options
.getTag();
195 * Returns tag of this task. Can return {@code null}.
197 public byte[] getTagAsBytes() {
198 return options
.getTagAsBytes();
201 private void setEtaUsecFromOptions(TaskOptions options
) {
202 if (options
!= null && options
.getEtaMillis() != null) {
203 etaUsec
= options
.getEtaMillis() * 1000;
209 static final class KeyValuePair
implements Map
.Entry
<String
, String
> {
210 private final String key
;
211 private String value
;
213 public KeyValuePair(String key
, String value
) {
218 public String
getKey() {
222 public String
getValue() {
226 public String
setValue(String v
) {
232 public int hashCode() {
235 result
= prime
* result
+ key
.hashCode();
236 result
= prime
* result
+ value
.hashCode();
240 public boolean equals(Object o
) {
241 if (this == o
) return true;
242 if (o
== null || !(o
instanceof KeyValuePair
)) return false;
243 KeyValuePair that
= (KeyValuePair
) o
;
244 return key
.equals(that
.key
) && value
.equals(that
.value
);
249 * Attempts to decode the {@code payload} byte array in our {@code options}
250 * into a list of Map.Entry<String, String>.
252 * @throws UnsupportedEncodingException if the payload cannot be decoded as a
253 * {@code application/x-www-form-urlencoded} string.
254 * @throws UnsupportedOperationException if the {@code options} has no payload
255 * or the payload bytes could not be interpreted as application/x-www-form-urlencoded
258 public List
<Map
.Entry
<String
, String
>> extractParams()
259 throws UnsupportedEncodingException
, UnsupportedOperationException
{
260 String payload
= new String(getPayload());
261 String
[] paramStrings
= payload
.split("&");
263 List
<Map
.Entry
<String
, String
>> result
= new ArrayList
<Map
.Entry
<String
, String
>>();
264 for (String param
: paramStrings
) {
265 String
[] kv
= param
.split("=", 2);
266 if (kv
.length
!= 2) {
267 throw new UnsupportedOperationException(
268 "Payload " + payload
+ " failed to decode as application/x-www-form-urlencoded pairs. "
269 + param
+ " Length" + kv
.length
);
271 result
.add(new KeyValuePair(URLDecoder
.decode(kv
[0], "UTF-8"),
272 URLDecoder
.decode(kv
[1], "UTF-8")));