Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / labs / datastore / overlay / OverlayDatastoreServiceFactory.java
blob0bb0a86090b300a17f38076777ca61eee68b084d
1 package com.google.appengine.api.labs.datastore.overlay;
3 import static com.google.appengine.api.datastore.DatastoreServiceConfig.Builder.withDefaults;
4 import static com.google.common.base.Preconditions.checkNotNull;
6 import com.google.appengine.api.NamespaceManager;
7 import com.google.appengine.api.datastore.AsyncDatastoreService;
8 import com.google.appengine.api.datastore.DatastoreService;
9 import com.google.appengine.api.datastore.DatastoreServiceConfig;
10 import com.google.appengine.api.datastore.DatastoreServiceFactory;
11 import com.google.appengine.api.datastore.Entity;
12 import com.google.appengine.api.datastore.EntityNotFoundException;
13 import com.google.appengine.api.datastore.IDatastoreServiceFactory;
14 import com.google.appengine.api.datastore.Key;
15 import com.google.appengine.api.datastore.KeyFactory;
16 import com.google.appengine.api.datastore.Transaction;
18 import java.util.concurrent.ThreadLocalRandom;
20 /**
21 * A factory for creating overlay-based {@link DatastoreService} implementations.
23 public final class OverlayDatastoreServiceFactory {
25 private final IDatastoreServiceFactory baseFactory;
26 private final NameFactory nameFactory;
28 public OverlayDatastoreServiceFactory() {
29 this(new IDatastoreServiceFactory() {
30 @Override
31 public DatastoreService getDatastoreService(DatastoreServiceConfig config) {
32 return DatastoreServiceFactory.getDatastoreService(config);
35 @Override
36 public AsyncDatastoreService getAsyncDatastoreService(DatastoreServiceConfig config) {
37 return DatastoreServiceFactory.getAsyncDatastoreService(config);
39 });
42 OverlayDatastoreServiceFactory(IDatastoreServiceFactory baseFactory) {
43 this(baseFactory, new NameFactory());
46 OverlayDatastoreServiceFactory(IDatastoreServiceFactory baseFactory,
47 NameFactory nameFactory) {
48 this.baseFactory = baseFactory;
49 this.nameFactory = nameFactory;
52 /**
53 * Creates a new overlay-based {@link DatastoreService} using {@code config}, where a single
54 * Datastore holds both original and overlay data. The data is segregated using namespaces.
56 public OverlayDatastoreService createOverlayDatastoreService(
57 DatastoreServiceConfig config) {
58 checkNotNull(config);
59 return createOverlayDatastoreService(baseFactory.getAsyncDatastoreService(config));
62 /**
63 * Creates a new overlay-based {@link DatastoreService} using the default config, where a single
64 * Datastore holds both original and overlay data. The data is segregated using namespaces.
66 public OverlayDatastoreService createOverlayDatastoreService() {
67 return createOverlayDatastoreService(baseFactory.getAsyncDatastoreService(withDefaults()));
70 /**
71 * Creates a new overlay-based {@link DatastoreService} named {@code name} using the default
72 * config, where a single Datastore holds both original and overlay data. The data is segregated
73 * using namespaces.
75 public OverlayDatastoreService createOverlayDatastoreService(String name) {
76 return createOverlayDatastoreService(baseFactory.getAsyncDatastoreService(withDefaults()),
77 name);
80 /**
81 * Creates a new overlay-based {@link DatastoreService} using {@code datastore},
82 * where a single Datastore holds both original and overlay data. The data is segregated using
83 * namespaces.
85 public OverlayDatastoreService createOverlayDatastoreService(AsyncDatastoreService datastore) {
86 checkNotNull(datastore);
87 return new SyncOverlayDatastoreServiceAdapter(createOverlayAsyncDatastoreService(datastore));
90 /**
91 * Creates a new overlay-based {@link DatastoreService} using {@code datastore}, where a single
92 * Datastore holds both original and overlay data. The data is segregated using namespaces.
94 * NOTE: An overlay created by this method will be significantly less performant than an overlay
95 * initialized with an {@code AsyncDatastoreService}. A single overlay operation may require
96 * several operations on the parent Datastore, which must be serialized if the parent is
97 * synchronous. It is recommended to initialize an overlay with an {@code AsyncDatastoreService}
98 * if possible.
100 public OverlayDatastoreService createOverlayDatastoreService(DatastoreService datastore) {
101 checkNotNull(datastore);
102 return createOverlayDatastoreService(new LazyAsyncDatastoreServiceAdapter(datastore));
106 * Creates an overlay-based {@link DatastoreService} named {@code name} using {@code datastore},
107 * where a single Datastore holds both original and overlay data. The data is segregated using
108 * namespaces.
110 * The name/datastore pair uniquely defines an overlay. If an overlay named {@code name} exists,
111 * the returned {@code DatastoreService} will be connected to the existing overlay; otherwise, a
112 * new overlay will be created.
114 public OverlayDatastoreService createOverlayDatastoreService(AsyncDatastoreService datastore,
115 String name) {
116 checkNotNull(datastore);
117 checkNotNull(name);
118 return new SyncOverlayDatastoreServiceAdapter(
119 createOverlayAsyncDatastoreService(datastore, name));
123 * Creates an overlay-based {@link DatastoreService} named {@code name} using {@code datastore},
124 * where a single Datastore holds both original and overlay data. The data is segregated using
125 * namespaces.
127 * The name/datastore pair uniquely defines an overlay. If an overlay named {@code name} exists,
128 * the returned {@code DatastoreService} will be connected to the existing overlay; otherwise, a
129 * new overlay will be created.
131 * NOTE: An overlay created by this method will be significantly less performant than an overlay
132 * initialized with an {@code AsyncDatastoreService}. A single overlay operation may require
133 * several operations on the parent Datastore, which must be serialized if the parent is
134 * synchronous. It is recommended to initialize an overlay with an {@code AsyncDatastoreService}
135 * if possible.
137 public OverlayDatastoreService createOverlayDatastoreService(DatastoreService datastore,
138 String name) {
139 checkNotNull(datastore);
140 checkNotNull(name);
141 return createOverlayDatastoreService(new LazyAsyncDatastoreServiceAdapter(datastore), name);
145 * Creates an overlay-based {@link DatastoreService} named {@code name} using {@code datastore},
146 * where a single Datastore holds both original and overlay data. The data is segregated using
147 * namespaces.
149 * The name/datastore pair uniquely defines an overlay. If an overlay named {@code name} exists,
150 * the returned {@code DatastoreService} will be connected to the existing overlay; otherwise, a
151 * new overlay will be created iff {@code create} is true.
153 * @raises IllegalStateException if {@code create} is false and the overlay does not already
154 * exist.
156 public OverlayDatastoreService createOverlayDatastoreService(AsyncDatastoreService datastore,
157 String name, boolean create) throws IllegalStateException {
158 checkNotNull(datastore);
159 checkNotNull(name);
160 return new SyncOverlayDatastoreServiceAdapter(
161 createOverlayAsyncDatastoreService(datastore, name, create));
165 * Creates an overlay-based {@link DatastoreService} named {@code name} using {@code datastore},
166 * where a single Datastore holds both original and overlay data. The data is segregated using
167 * namespaces.
169 * The name/datastore pair uniquely defines an overlay. If an overlay named {@code name} exists,
170 * the returned {@code DatastoreService} will be connected to the existing overlay; otherwise, a
171 * new overlay will be created iff {@code create} is true.
173 * NOTE: An overlay created by this method will be significantly less performant than an overlay
174 * initialized with an {@code AsyncDatastoreService}. A single overlay operation may require
175 * several operations on the parent Datastore, which must be serialized if the parent is
176 * synchronous. It is recommended to initialize an overlay with an {@code AsyncDatastoreService}
177 * if possible.
179 * @raises IllegalStateException if {@code create} is false and the overlay does not already
180 * exist.
182 public OverlayDatastoreService createOverlayDatastoreService(DatastoreService datastore,
183 String name, boolean create) throws IllegalStateException {
184 checkNotNull(datastore);
185 checkNotNull(name);
186 return createOverlayDatastoreService(new LazyAsyncDatastoreServiceAdapter(datastore), name,
187 create);
191 * Creates a new overlay-based {@link AsyncDatastoreService} using {@code config}, where a
192 * single Datastore holds both original and overlay data. The data is segregated using namespaces.
194 public OverlayAsyncDatastoreService createOverlayAsyncDatastoreService(
195 DatastoreServiceConfig config) {
196 checkNotNull(config);
197 return createOverlayAsyncDatastoreService(baseFactory.getAsyncDatastoreService(config));
201 * Creates a new overlay-based {@link AsyncDatastoreService} using the default config, where a
202 * single Datastore holds both original and overlay data. The data is segregated using namespaces.
204 public OverlayAsyncDatastoreService createOverlayAsyncDatastoreService() {
205 return createOverlayAsyncDatastoreService(baseFactory.getAsyncDatastoreService(withDefaults()));
209 * Creates a new overlay-based {@link AsyncDatastoreService} named {@code name} using the default
210 * config, where a single Datastore holds both original and overlay data. The data is segregated
211 * using namespaces.
213 public OverlayAsyncDatastoreService createOverlayAsyncDatastoreService(String name) {
214 return createOverlayAsyncDatastoreService(
215 baseFactory.getAsyncDatastoreService(withDefaults()), name);
219 * Creates a new overlay-based {@link AsyncDatastoreService} using {@code datastore}, where a
220 * single Datastore holds both original and overlay data. The data is segregated using namespaces.
222 public OverlayAsyncDatastoreService createOverlayAsyncDatastoreService(
223 AsyncDatastoreService datastore) {
224 checkNotNull(datastore);
225 String name = getUniqueOverlayName(new SyncDatastoreServiceAdapter(datastore));
226 return createOverlayAsyncDatastoreService(datastore, name);
230 * Creates an overlay-based {@link AsyncDatastoreService} named {@code name} using
231 * {@code datastore}, where a single Datastore holds both original and overlay data. The data is
232 * segregated using namespaces.
234 * The name/datastore pair uniquely defines an overlay. If an overlay named {@code name} exists,
235 * the returned {@code DatastoreService} will be connected to the existing overlay; otherwise, a
236 * new overlay will be created.
238 public OverlayAsyncDatastoreService createOverlayAsyncDatastoreService(
239 AsyncDatastoreService datastore, String name) {
240 checkNotNull(datastore);
241 checkNotNull(name);
242 try {
243 return createOverlayAsyncDatastoreService(datastore, name, true);
244 } catch (IllegalStateException e) {
245 throw new AssertionError(
246 "Unexpected IllegalStateException on overlay creation when create=true.", e);
251 * Creates an overlay-based {@link AsyncDatastoreService} named {@code name} using
252 * {@code datastore}, where a single Datastore holds both original and overlay data. The data is
253 * segregated using namespaces.
255 * The name/datastore pair uniquely defines an overlay. If an overlay named {@code name} exists,
256 * the returned {@code DatastoreService} will be connected to the existing overlay; otherwise, a
257 * new overlay will be created iff {@code create} is true.
259 * @raises IllegalStateException if {@code create} is false and the overlay does not already
260 * exist.
262 public OverlayAsyncDatastoreService createOverlayAsyncDatastoreService(
263 AsyncDatastoreService datastore, String name, boolean create) throws IllegalStateException {
264 checkNotNull(datastore);
265 checkNotNull(name);
266 DatastoreService syncWrappedDatastore = new SyncDatastoreServiceAdapter(datastore);
267 if (!overlayExists(syncWrappedDatastore, name, null)) {
268 if (create) {
269 registerOverlay(syncWrappedDatastore, name, null);
270 } else {
271 throw new IllegalStateException("Overlay \"" + name + "\" does not exist.");
274 return new OverlayAsyncDatastoreServiceImpl(
275 name,
276 new NamespacePinnedAsyncDatastoreServiceImpl(datastore, getOverlayNamespace(name), true),
277 datastore);
280 static class NameFactory {
281 private static final char[] ALPHA_NUMERIC_CHARS =
282 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
283 private static final int RANDOM_NAME_LENGTH = 8;
285 public String getRandomName() {
286 char[] buf = new char[RANDOM_NAME_LENGTH];
287 ThreadLocalRandom random = ThreadLocalRandom.current();
288 for (int i = 0; i < RANDOM_NAME_LENGTH; i++) {
289 buf[i] = ALPHA_NUMERIC_CHARS[random.nextInt(ALPHA_NUMERIC_CHARS.length)];
291 return new String(buf);
295 private static final String OVERLAY_METADATA_NAMESPACE = "_overlay_";
297 private static final String OVERLAY_KIND = "_overlay_";
299 private String getUniqueOverlayName(DatastoreService datastore) {
300 while (true) {
301 Transaction txn = datastore.beginTransaction();
302 try {
303 String name = nameFactory.getRandomName();
304 if (!overlayExists(datastore, name, txn)) {
305 registerOverlay(datastore, name, txn);
306 txn.commit();
307 return name;
309 } finally {
310 if (txn.isActive()) {
311 txn.rollback();
318 * Returns a name for an overlay with the given {@code suffix}. This method is deterministic:
319 * the name returned for a given suffix is stable.
321 private static String getOverlayNamespace(String suffix) {
322 return OVERLAY_METADATA_NAMESPACE + suffix;
325 private static void registerOverlay(DatastoreService datastore, String name, Transaction txn) {
326 String currentNamespace = NamespaceManager.get();
327 try {
328 NamespaceManager.set(OVERLAY_METADATA_NAMESPACE);
329 Entity entity = new Entity(OVERLAY_KIND, name);
330 datastore.put(txn, entity);
331 } finally {
332 NamespaceManager.set(currentNamespace);
336 private static boolean overlayExists(DatastoreService datastore, String overlayName, Transaction txn) {
337 String currentNamespace = NamespaceManager.get();
338 try {
339 NamespaceManager.set(OVERLAY_METADATA_NAMESPACE);
340 Key key = KeyFactory.createKey(OVERLAY_KIND, overlayName);
341 datastore.get(txn, key);
342 return true;
343 } catch (EntityNotFoundException e) {
344 return false;
345 } finally {
346 NamespaceManager.set(currentNamespace);