Skip to content

Commit 754e40a

Browse files
committed
Simplify how the stack is maintained.
Some significant API changes to how Objectify is used outside of the ObjectifyFilter.
1 parent a96cdb2 commit 754e40a

25 files changed

Lines changed: 176 additions & 167 deletions

src/main/java/com/googlecode/objectify/ObjectifyFilter.java

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33

44
package com.googlecode.objectify;
55

6-
import com.googlecode.objectify.cache.AsyncCacheFilter;
7-
6+
import com.googlecode.objectify.util.AbstractFilter;
7+
import com.googlecode.objectify.util.Closeable;
88
import javax.servlet.FilterChain;
99
import javax.servlet.ServletException;
1010
import javax.servlet.ServletRequest;
@@ -34,28 +34,18 @@
3434
*</pre>
3535
*
3636
* <p>If you use the Objectify outside of the context of a request (say, using the remote
37-
* API or from a unit test), then you should call {@code ObjectifyFilter.complete()} after every operation
38-
* that you consider a "request". For example, after each test.</p>
37+
* API or from a unit test), then you should use the ObjectifyService.run() method.</p>
3938
*
4039
* @author Jeff Schnitzer
4140
*/
42-
public class ObjectifyFilter extends AsyncCacheFilter
41+
public class ObjectifyFilter extends AbstractFilter
4342
{
4443
/** */
4544
@Override
4645
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
47-
try {
48-
super.doFilter(request, response, chain);
49-
} finally {
50-
ObjectifyService.reset();
51-
}
52-
}
5346

54-
/**
55-
* Perform the actions that are performed upon normal completion of a request.
56-
*/
57-
public static void complete() {
58-
AsyncCacheFilter.complete();
59-
ObjectifyService.reset();
47+
try (Closeable closeable = ObjectifyService.begin()) {
48+
chain.doFilter(request, response);
49+
}
6050
}
6151
}

src/main/java/com/googlecode/objectify/ObjectifyService.java

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
package com.googlecode.objectify;
55

6+
import com.googlecode.objectify.cache.PendingFutures;
7+
import com.googlecode.objectify.util.Closeable;
68
import java.util.ArrayDeque;
79
import java.util.Deque;
810

@@ -37,8 +39,11 @@ protected Deque<Objectify> initialValue() {
3739
*/
3840
public static Objectify ofy() {
3941
Deque<Objectify> stack = STACK.get();
42+
4043
if (stack.isEmpty())
41-
stack.add(factory.begin());
44+
throw new IllegalStateException("You have not started an Objectify context. You are probably missing the " +
45+
"ObjectifyFilter. If you are not running in the context of an http request, see the " +
46+
"ObjectifyService.run() method.");
4247

4348
return stack.getLast();
4449
}
@@ -58,7 +63,53 @@ public static ObjectifyFactory factory() {
5863
public static void register(Class<?> clazz) {
5964
factory().register(clazz);
6065
}
61-
66+
67+
/**
68+
* <p>Runs one unit of work, making the root Objectify context available. This does not start a transaction,
69+
* but it makes the static ofy() method return an appropriate object.</p>
70+
*
71+
* <p>Normally you do not need to use this method. When servicing a normal request, the ObjectifyFilter
72+
* will run this for you. This method is useful for using Objectify outside of a normal request -
73+
* using the remote api, for example.</p>
74+
*
75+
* <p>Alternatively, you can use the begin() method and close the session manually.</p>
76+
*
77+
* @return the result of the work.
78+
*/
79+
public static <R> R run(Work<R> work) {
80+
try (Closeable closeable = begin()) {
81+
return work.run();
82+
}
83+
}
84+
85+
/**
86+
* <p>An alternative to run() which is somewhat easier to use with testing (ie, @Before and @After) frameworks.
87+
* You must close the return value at the end of the request in a finally block. It's better/safer to use run().</p>
88+
*
89+
* <p>This method is not typically necessary - in a normal request, the ObjectifyFilter takes care of this housekeeping
90+
* for you. However, in unit tests or remote API calls it can be useful.</p>
91+
*/
92+
public static Closeable begin() {
93+
final Deque<Objectify> stack = STACK.get();
94+
95+
if (!stack.isEmpty())
96+
throw new IllegalStateException("You already have an initial Objectify context. Perhaps you want to use the ofy() method?");
97+
98+
stack.add(factory.begin());
99+
100+
return new Closeable() {
101+
@Override
102+
public void close() {
103+
if (stack.isEmpty())
104+
throw new IllegalStateException("You have already destroyed the Objectify context.");
105+
106+
PendingFutures.completeAllPendingFutures();
107+
108+
stack.removeLast();
109+
}
110+
};
111+
}
112+
62113
/** Pushes new context onto stack when a transaction starts */
63114
public static void push(Objectify ofy) {
64115
STACK.get().add(ofy);
@@ -68,9 +119,4 @@ public static void push(Objectify ofy) {
68119
public static void pop() {
69120
STACK.get().removeLast();
70121
}
71-
72-
/** Clear the stack of any leftover Objectify instances */
73-
public static void reset() {
74-
STACK.get().clear();
75-
}
76122
}

src/main/java/com/googlecode/objectify/cache/AsyncCacheFilter.java

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
package com.googlecode.objectify.cache;
22

3-
import javax.servlet.Filter;
3+
import com.googlecode.objectify.util.AbstractFilter;
44
import javax.servlet.FilterChain;
5-
import javax.servlet.FilterConfig;
65
import javax.servlet.ServletException;
76
import javax.servlet.ServletRequest;
87
import javax.servlet.ServletResponse;
98
import java.io.IOException;
109

1110
/**
1211
* <p>This Filter is a companion to the CachingAsyncDatastoreService, and must be
13-
* installed any time the CachingAsyncDatastoreService is used.</p>
12+
* installed any time the CachingAsyncDatastoreService is used without Objectify.</p>
1413
*
1514
* <p>This Filter is a temporary measure until Google provides a hook that lets
1615
* us intercept the raw Future<?> calls at the end of a request. At that point
@@ -42,16 +41,8 @@
4241
*
4342
* @author Jeff Schnitzer <jeff@infohazard.org>
4443
*/
45-
public class AsyncCacheFilter implements Filter
44+
public class AsyncCacheFilter extends AbstractFilter
4645
{
47-
@Override
48-
public void init(FilterConfig config) throws ServletException {
49-
}
50-
51-
@Override
52-
public void destroy() {
53-
}
54-
5546
@Override
5647
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
5748
try {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.googlecode.objectify.util;
2+
3+
import javax.servlet.Filter;
4+
import javax.servlet.FilterConfig;
5+
import javax.servlet.ServletException;
6+
7+
/**
8+
* Get rid of some boilerplate.
9+
*/
10+
abstract public class AbstractFilter implements Filter {
11+
@Override
12+
public void init(FilterConfig filterConfig) throws ServletException {
13+
}
14+
15+
@Override
16+
public void destroy() {
17+
}
18+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.googlecode.objectify.util;
2+
3+
/**
4+
* Get rid of the stupid checked exception
5+
*/
6+
public interface Closeable extends java.io.Closeable {
7+
@Override void close();
8+
}

src/test/java/com/googlecode/objectify/test/AlsoLoadTests.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,7 @@ public HasAlsoLoads(String stuff, String otherStuff) {
124124
* Add an entry to the database that should never come back from null queries.
125125
*/
126126
@BeforeMethod
127-
public void setUp() {
128-
super.setUp();
129-
127+
public void setUpExtra() {
130128
fact().register(HasAlsoLoads.class);
131129
fact().register(HasEmbedded.class);
132130
fact().register(HasEmbeddedArray.class);

src/test/java/com/googlecode/objectify/test/AlsoLoadTests2.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,8 @@ public void set(@AlsoLoad("foo") String overrides)
5454
* Add an entry to the database that should never come back from null queries.
5555
*/
5656
@BeforeMethod
57-
public void setUp()
57+
public void setUpExtra()
5858
{
59-
super.setUp();
60-
6159
fact().register(MethodOverridesField.class);
6260
}
6361

src/test/java/com/googlecode/objectify/test/CachingDatastoreTests.java

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,6 @@
33

44
package com.googlecode.objectify.test;
55

6-
import java.util.Collections;
7-
import java.util.List;
8-
import java.util.Map;
9-
import java.util.Set;
10-
import java.util.concurrent.Future;
11-
import java.util.logging.Logger;
12-
13-
import org.testng.annotations.BeforeMethod;
14-
import org.testng.annotations.Test;
15-
166
import com.google.appengine.api.datastore.DatastoreServiceFactory;
177
import com.google.appengine.api.datastore.Entity;
188
import com.google.appengine.api.datastore.Key;
@@ -21,6 +11,14 @@
2111
import com.googlecode.objectify.cache.EntityMemcache;
2212
import com.googlecode.objectify.test.util.MockAsyncDatastoreService;
2313
import com.googlecode.objectify.test.util.TestBase;
14+
import org.testng.annotations.BeforeMethod;
15+
import org.testng.annotations.Test;
16+
import java.util.Collections;
17+
import java.util.List;
18+
import java.util.Map;
19+
import java.util.Set;
20+
import java.util.concurrent.Future;
21+
import java.util.logging.Logger;
2422

2523
/**
2624
* Tests of the caching datastore directly, outside of the rest of Objectify. Tries to create as few
@@ -49,10 +47,8 @@ public class CachingDatastoreTests extends TestBase
4947
/**
5048
*/
5149
@BeforeMethod
52-
public void setUp()
50+
public void setUpExtra()
5351
{
54-
super.setUp();
55-
5652
EntityMemcache mc = new EntityMemcache(null);
5753
cads = new CachingAsyncDatastoreService(DatastoreServiceFactory.getAsyncDatastoreService(), mc);
5854
nods = new CachingAsyncDatastoreService(new MockAsyncDatastoreService(), mc);

src/test/java/com/googlecode/objectify/test/CachingTests.java

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,6 @@
33

44
package com.googlecode.objectify.test;
55

6-
import static com.googlecode.objectify.test.util.TestObjectifyService.fact;
7-
import static com.googlecode.objectify.test.util.TestObjectifyService.ofy;
8-
9-
import java.util.ArrayList;
10-
import java.util.List;
11-
import java.util.Map;
12-
import java.util.logging.Logger;
13-
14-
import org.testng.annotations.BeforeMethod;
15-
import org.testng.annotations.Test;
16-
176
import com.google.appengine.api.memcache.MemcacheService;
187
import com.google.appengine.api.memcache.MemcacheServiceFactory;
198
import com.googlecode.objectify.Key;
@@ -22,6 +11,14 @@
2211
import com.googlecode.objectify.annotation.Entity;
2312
import com.googlecode.objectify.annotation.Id;
2413
import com.googlecode.objectify.test.util.TestBase;
14+
import org.testng.annotations.BeforeMethod;
15+
import org.testng.annotations.Test;
16+
import java.util.ArrayList;
17+
import java.util.List;
18+
import java.util.Map;
19+
import java.util.logging.Logger;
20+
import static com.googlecode.objectify.test.util.TestObjectifyService.fact;
21+
import static com.googlecode.objectify.test.util.TestObjectifyService.ofy;
2522

2623
/**
2724
* @author Jeff Schnitzer <jeff@infohazard.org>
@@ -50,9 +47,7 @@ static class Cached {
5047
/**
5148
*/
5249
@BeforeMethod
53-
public void setUp() {
54-
super.setUp();
55-
50+
public void setUpExtra() {
5651
fact().register(Uncached.class);
5752
fact().register(Cached.class);
5853
}

src/test/java/com/googlecode/objectify/test/EmbeddedNullTests.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,8 @@ public class EmbeddedNullTests extends TestBase
2424
* Add an entry to the database that should never come back from null queries.
2525
*/
2626
@BeforeMethod
27-
public void setUp()
27+
public void setUpExtra()
2828
{
29-
super.setUp();
30-
3129
fact().register(Criminal.class);
3230

3331
Criminal avoid = new Criminal();

0 commit comments

Comments
 (0)