Skip to content

Commit b85f58e

Browse files
committed
---
yaml --- r: 47 b: refs/heads/master c: 546cd04 h: refs/heads/master i: 45: 522675b 43: 9b5e07c 39: 8f0624d 31: c5787e1 v: v3
1 parent 105e79c commit b85f58e

6 files changed

Lines changed: 194 additions & 39 deletions

File tree

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
---
2-
refs/heads/master: baf41a7b3a2b69d6762e8c76ccd788cb82f3466d
2+
refs/heads/master: 546cd049cbde735c760988310089dc074a73cc8e

trunk/.classpath

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,17 @@
2121
<attributes>
2222
<attribute name="maven.pomderived" value="true"/>
2323
</attributes>
24-
<accessrules>
25-
<accessrule kind="nonaccessible" pattern="**/repackaged/**"/>
26-
</accessrules>
2724
</classpathentry>
2825
<classpathentry kind="var" path="ECLIPSE_HOME"/>
26+
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
27+
<attributes>
28+
<attribute name="maven.pomderived" value="true"/>
29+
</attributes>
30+
</classpathentry>
31+
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
32+
<attributes>
33+
<attribute name="maven.pomderived" value="true"/>
34+
</attributes>
35+
</classpathentry>
2936
<classpathentry kind="output" path="target/classes"/>
3037
</classpath>

trunk/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@
4242
<artifactId>joda-time</artifactId>
4343
<version>RELEASE</version>
4444
</dependency>
45+
<dependency>
46+
<groupId>org.json</groupId>
47+
<artifactId>json</artifactId>
48+
<version>20090211</version>
49+
</dependency>
4550
</dependencies>
4651
<build>
4752
<plugins>
Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
package com.google.gcloud.datastore;
22

3-
import static com.google.common.base.MoreObjects.firstNonNull;
4-
53
import com.google.api.services.datastore.client.DatastoreException;
4+
import com.google.common.base.MoreObjects;
5+
import com.google.common.collect.ImmutableMap;
66

7-
import java.util.HashMap;
8-
import java.util.Map;
7+
import org.json.JSONException;
8+
import org.json.JSONObject;
9+
import org.json.JSONTokener;
910

1011
public class DatastoreServiceException extends RuntimeException {
1112

1213
private static final long serialVersionUID = 8170357898917041899L;
13-
private static final Map<Integer, Code> HTTP_TO_CODE = new HashMap<>();
14+
private static final ImmutableMap<String, Code> REASON_TO_CODE;
1415

1516
private final Code code;
1617

@@ -21,23 +22,22 @@ public class DatastoreServiceException extends RuntimeException {
2122
*/
2223
public enum Code {
2324

24-
ABORTED(409, true, "Request aborted"),
25-
DEADLINE_EXCEEDED(403, true, "Deadline exceeded"),
26-
UNAVAILABLE(503, true, "Could not reach service"),
27-
FAILED_PRECONDITION(412, false, "Invalid request"),
28-
INVALID_ARGUMENT(400, false, "Request parameter has an invalid value"),
29-
PERMISSION_DENIED(403, false, "Unauthorized request"),
30-
RESOURCE_EXHAUSTED(402, false, "Quota exceeded"),
31-
INTERNAL(500, false, "Server returned an error"),
32-
UNKNOWN(0, false, "Unknown failure");
25+
ABORTED(true, "Request aborted"),
26+
DEADLINE_EXCEEDED(true, "Deadline exceeded"),
27+
UNAVAILABLE(true, "Could not reach service"),
28+
FAILED_PRECONDITION(false, "Invalid request"),
29+
INVALID_ARGUMENT(false, "Request parameter has an invalid value"),
30+
PERMISSION_DENIED(false, "Unauthorized request"),
31+
RESOURCE_EXHAUSTED(false, "Quota exceeded"),
32+
INTERNAL(false, "Server returned an error"),
33+
UNKNOWN(false, "Unknown failure");
3334

3435
private final boolean isTransient;
35-
private final String msg;
36+
private final String defaultMessage;
3637

37-
Code(int httpStatus, boolean isTransient, String msg) {
38+
Code(boolean isTransient, String msg) {
3839
this.isTransient = isTransient;
39-
this.msg = msg;
40-
HTTP_TO_CODE.put(httpStatus, this);
40+
this.defaultMessage = msg;
4141
}
4242

4343
/**
@@ -48,16 +48,28 @@ public boolean isTransient() {
4848
return isTransient;
4949
}
5050

51-
DatastoreServiceException translate(DatastoreException exception) {
52-
return new DatastoreServiceException(this, exception);
51+
DatastoreServiceException translate(DatastoreException exception, String msg) {
52+
return new DatastoreServiceException(this, msg, exception);
5353
}
5454
}
5555

56-
public DatastoreServiceException(Code code, Exception cause) {
57-
super(code.msg, cause);
56+
static {
57+
ImmutableMap.Builder<String, Code> builder = ImmutableMap.builder();
58+
for (Code code : Code.values()) {
59+
builder.put(code.name(), code);
60+
}
61+
REASON_TO_CODE = builder.build();
62+
}
63+
64+
public DatastoreServiceException(Code code, String msg, Exception cause) {
65+
super(MoreObjects.firstNonNull(msg, code.defaultMessage), cause);
5866
this.code = code;
5967
}
6068

69+
public DatastoreServiceException(Code code, String msg) {
70+
this(code, msg, null);
71+
}
72+
6173
/**
6274
* Returns the code associated with this exception.
6375
*/
@@ -72,19 +84,30 @@ public Code code() {
7284
* @throws DatastoreServiceException every time
7385
*/
7486
static DatastoreServiceException translateAndThrow(DatastoreException exception) {
75-
throw firstNonNull(HTTP_TO_CODE.get(exception.getCode()), Code.UNKNOWN).translate(exception);
87+
String message = exception.getMessage();
88+
String reason = "";
89+
if (message != null) {
90+
try {
91+
JSONObject json = new JSONObject(new JSONTokener(exception.getMessage()));
92+
JSONObject error = json.getJSONObject("error").getJSONArray("errors").getJSONObject(0);
93+
reason = error.getString("reason");
94+
message = error.getString("message");
95+
} catch (JSONException ex) {
96+
// ignore - will be converted to unknown
97+
}
98+
}
99+
Code code = MoreObjects.firstNonNull(REASON_TO_CODE.get(reason), Code.UNKNOWN);
100+
throw code.translate(exception, message);
76101
}
77102

103+
78104
/**
79105
* Throw a DatastoreServiceException with {@code FAILED_PRECONDITION} code and the {@code msg}
80106
* in a nested exception.
81107
*
82108
* @throws DatastoreServiceException every time
83109
*/
84110
static DatastoreServiceException throwInvalidRequest(String msg, Object... params) {
85-
if (params.length > 0) {
86-
msg = String.format(msg, params);
87-
}
88-
throw new DatastoreServiceException(Code.FAILED_PRECONDITION, new RuntimeException(msg));
111+
throw new DatastoreServiceException(Code.FAILED_PRECONDITION, String.format(msg, params));
89112
}
90113
}

trunk/src/main/java/com/google/gcloud/datastore/TransactionImpl.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ public void commit() {
5555
@Override
5656
public void rollback() {
5757
super.checkValid();
58-
datastore.rollbackTransaction(transaction);
58+
if (!wasRolledback) {
59+
datastore.rollbackTransaction(transaction);
60+
}
5961
wasRolledback = true;
6062
}
6163

trunk/src/test/java/com/google/gcloud/datastore/DatastoreServiceTest.java

Lines changed: 125 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ public class DatastoreServiceTest {
2929
private static final Key KEY1 = PARTIAL_KEY1.newKey("name");
3030
private static final Key KEY2 = Key.builder(KEY1, KIND2, 1).build();
3131
private static final Key KEY3 = Key.builder(KEY2).name("bla").build();
32+
private static final Key KEY4 = KEY2.newKey("newName1");
33+
private static final Key KEY5 = KEY2.newKey("newName2");
3234
private static final KeyValue KEY_VALUE = new KeyValue(KEY1);
3335
private static final ListValue LIST_VALUE1 = ListValue.builder()
3436
.addValue(NULL_VALUE)
@@ -80,7 +82,7 @@ public void setUp() {
8082
.build();
8183
datastore = DatastoreServiceFactory.getDefault(options);
8284
// Prepare data for testing
83-
datastore.delete(KEY1, KEY2, KEY3);
85+
datastore.delete(KEY1, KEY2, KEY3, KEY4, KEY5);
8486
datastore.add(ENTITY1, ENTITY2);
8587
}
8688

@@ -91,13 +93,119 @@ public void testGetOptions() {
9193

9294
@Test
9395
public void testNewTransactionCommit() {
94-
// TODO (also try list value with different types)
95-
fail("Not yet implemented");
96+
Transaction transaction = datastore.newTransaction();
97+
transaction.add(ENTITY3);
98+
Entity entity2 = Entity.builder(ENTITY2)
99+
.clearProperties()
100+
.setNullProperty("bla")
101+
.build();
102+
transaction.update(entity2);
103+
transaction.delete(KEY1);
104+
transaction.commit();
105+
106+
Iterator<Entity> iter = datastore.get(KEY1, KEY2, KEY3);
107+
assertNull(iter.next());
108+
assertEquals(entity2, iter.next());
109+
assertEquals(ENTITY3, iter.next());
110+
assertFalse(iter.hasNext());
111+
112+
try {
113+
transaction.commit();
114+
fail("Expecting a failure");
115+
} catch (DatastoreServiceException ex) {
116+
// expected to fail
117+
}
118+
119+
try {
120+
transaction.rollback();
121+
fail("Expecting a failure");
122+
} catch (DatastoreServiceException ex) {
123+
// expected to fail
124+
}
125+
126+
verifyNotUsable(transaction);
127+
}
128+
129+
@Test
130+
public void testTransactionWithRead() {
131+
Transaction transaction = datastore.newTransaction();
132+
assertNull(transaction.get(KEY3));
133+
transaction.add(ENTITY3);
134+
transaction.commit();
135+
assertEquals(ENTITY3, datastore.get(KEY3));
136+
137+
transaction = datastore.newTransaction();
138+
assertEquals(ENTITY3, transaction.get(KEY3));
139+
// update entity3 during the transaction
140+
datastore.put(Entity.builder(ENTITY3).clearProperties().build());
141+
transaction.update(ENTITY2);
142+
try {
143+
transaction.commit();
144+
fail("Expecting a failure");
145+
} catch (DatastoreServiceException expected) {
146+
expected.printStackTrace();
147+
assertEquals(DatastoreServiceException.Code.ABORTED, expected.code());
148+
}
96149
}
97150

98151
@Test
99152
public void testNewTransactionRollback() {
100-
fail("Not yet implemented");
153+
Transaction transaction = datastore.newTransaction();
154+
transaction.add(ENTITY3);
155+
Entity entity2 = Entity.builder(ENTITY2)
156+
.clearProperties()
157+
.setNullProperty("bla")
158+
.setListProperty("list3", new StringValue("bla"), StringValue.builder("bla").build())
159+
.build();
160+
transaction.update(entity2);
161+
transaction.delete(KEY1);
162+
transaction.rollback();
163+
transaction.rollback(); // should be safe to repeat rollback calls
164+
165+
try {
166+
transaction.commit();
167+
fail("Expecting a failure");
168+
} catch (DatastoreServiceException ex) {
169+
// expected to fail
170+
}
171+
172+
verifyNotUsable(transaction);
173+
174+
Iterator<Entity> iter = datastore.get(KEY1, KEY2, KEY3);
175+
assertEquals(ENTITY1, iter.next());
176+
assertEquals(ENTITY2, iter.next());
177+
assertNull(iter.next());
178+
assertFalse(iter.hasNext());
179+
}
180+
181+
private void verifyNotUsable(DatastoreWriter writer) {
182+
try {
183+
writer.add(ENTITY3);
184+
fail("Expecting a failure");
185+
} catch (DatastoreServiceException ex) {
186+
// expected to fail
187+
}
188+
189+
try {
190+
writer.put(ENTITY3);
191+
fail("Expecting a failure");
192+
} catch (DatastoreServiceException ex) {
193+
// expected to fail
194+
}
195+
196+
try {
197+
writer.update(ENTITY3);
198+
fail("Expecting a failure");
199+
} catch (DatastoreServiceException ex) {
200+
// expected to fail
201+
}
202+
203+
try {
204+
writer.delete(ENTITY3.key());
205+
fail("Expecting a failure");
206+
} catch (DatastoreServiceException ex) {
207+
// expected to fail
208+
}
101209
}
102210

103211
@Test
@@ -108,10 +216,10 @@ public void testNewBatchWriter() {
108216
.clearProperties()
109217
.setNullProperty("bla")
110218
.build();
111-
Entity entity4 = Entity.builder(KEY2.newKey("newName1"))
219+
Entity entity4 = Entity.builder(KEY4)
112220
.setProperty("value", new StringValue("value"))
113221
.build();
114-
Entity entity5 = Entity.builder(KEY2.newKey("newName2"))
222+
Entity entity5 = Entity.builder(KEY5)
115223
.setStringProperty("value", "value")
116224
.build();
117225
batchWriter.add(entity4, entity5);
@@ -127,9 +235,12 @@ public void testNewBatchWriter() {
127235

128236
try {
129237
batchWriter.submit();
238+
fail("Expecting a failure");
130239
} catch (DatastoreServiceException ex) {
131240
// expected to fail
132241
}
242+
verifyNotUsable(batchWriter);
243+
133244
batchWriter = datastore.newBatchWriter();
134245
batchWriter.delete(entity4.key(), entity5.key());
135246
batchWriter.update(ENTITY1, ENTITY2, ENTITY3);
@@ -142,7 +253,11 @@ public void testNewBatchWriter() {
142253
assertNull(entities.next());
143254
assertFalse(entities.hasNext());
144255

145-
// TODO need to cover more edge cases (ds failures, re-use of same entities,..)
256+
// TODO need to cover the cases of:
257+
// delete after put/add/update
258+
// put after delete/add/update
259+
// update after delete/add/put
260+
// add after delete/update/put
146261
}
147262

148263
@Test
@@ -235,6 +350,7 @@ public void testGetArray() {
235350
assertFalse(entity3.hasProperty("bla"));
236351
try {
237352
entity3.stringProperty("str");
353+
fail("Expecting a failure");
238354
} catch (DatastoreServiceException expected) {
239355
// expected - no such property
240356
}
@@ -250,6 +366,7 @@ public void testAdd() {
250366

251367
try {
252368
datastore.add(ENTITY1);
369+
fail("Expecting a failure");
253370
} catch (DatastoreServiceException expected) {
254371
// expected;
255372
}
@@ -266,6 +383,7 @@ public void testUpdate() {
266383

267384
try {
268385
datastore.update(ENTITY3);
386+
fail("Expecting a failure");
269387
} catch (DatastoreServiceException expected) {
270388
// expected;
271389
}

0 commit comments

Comments
 (0)