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
;
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() {
31 public DatastoreService
getDatastoreService(DatastoreServiceConfig config
) {
32 return DatastoreServiceFactory
.getDatastoreService(config
);
36 public AsyncDatastoreService
getAsyncDatastoreService(DatastoreServiceConfig config
) {
37 return DatastoreServiceFactory
.getAsyncDatastoreService(config
);
42 OverlayDatastoreServiceFactory(IDatastoreServiceFactory baseFactory
) {
43 this(baseFactory
, new NameFactory());
46 OverlayDatastoreServiceFactory(IDatastoreServiceFactory baseFactory
,
47 NameFactory nameFactory
) {
48 this.baseFactory
= baseFactory
;
49 this.nameFactory
= nameFactory
;
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
) {
59 return createOverlayDatastoreService(baseFactory
.getAsyncDatastoreService(config
));
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()));
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
75 public OverlayDatastoreService
createOverlayDatastoreService(String name
) {
76 return createOverlayDatastoreService(baseFactory
.getAsyncDatastoreService(withDefaults()),
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
85 public OverlayDatastoreService
createOverlayDatastoreService(AsyncDatastoreService datastore
) {
86 checkNotNull(datastore
);
87 return new SyncOverlayDatastoreServiceAdapter(createOverlayAsyncDatastoreService(datastore
));
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}
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
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
,
116 checkNotNull(datastore
);
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
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}
137 public OverlayDatastoreService
createOverlayDatastoreService(DatastoreService datastore
,
139 checkNotNull(datastore
);
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
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
156 public OverlayDatastoreService
createOverlayDatastoreService(AsyncDatastoreService datastore
,
157 String name
, boolean create
) throws IllegalStateException
{
158 checkNotNull(datastore
);
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
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}
179 * @raises IllegalStateException if {@code create} is false and the overlay does not already
182 public OverlayDatastoreService
createOverlayDatastoreService(DatastoreService datastore
,
183 String name
, boolean create
) throws IllegalStateException
{
184 checkNotNull(datastore
);
186 return createOverlayDatastoreService(new LazyAsyncDatastoreServiceAdapter(datastore
), name
,
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
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
);
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
262 public OverlayAsyncDatastoreService
createOverlayAsyncDatastoreService(
263 AsyncDatastoreService datastore
, String name
, boolean create
) throws IllegalStateException
{
264 checkNotNull(datastore
);
266 DatastoreService syncWrappedDatastore
= new SyncDatastoreServiceAdapter(datastore
);
267 if (!overlayExists(syncWrappedDatastore
, name
, null)) {
269 registerOverlay(syncWrappedDatastore
, name
, null);
271 throw new IllegalStateException("Overlay \"" + name
+ "\" does not exist.");
274 return new OverlayAsyncDatastoreServiceImpl(
276 new NamespacePinnedAsyncDatastoreServiceImpl(datastore
, getOverlayNamespace(name
), true),
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
) {
301 Transaction txn
= datastore
.beginTransaction();
303 String name
= nameFactory
.getRandomName();
304 if (!overlayExists(datastore
, name
, txn
)) {
305 registerOverlay(datastore
, name
, txn
);
310 if (txn
.isActive()) {
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();
328 NamespaceManager
.set(OVERLAY_METADATA_NAMESPACE
);
329 Entity entity
= new Entity(OVERLAY_KIND
, name
);
330 datastore
.put(txn
, entity
);
332 NamespaceManager
.set(currentNamespace
);
336 private static boolean overlayExists(DatastoreService datastore
, String overlayName
, Transaction txn
) {
337 String currentNamespace
= NamespaceManager
.get();
339 NamespaceManager
.set(OVERLAY_METADATA_NAMESPACE
);
340 Key key
= KeyFactory
.createKey(OVERLAY_KIND
, overlayName
);
341 datastore
.get(txn
, key
);
343 } catch (EntityNotFoundException e
) {
346 NamespaceManager
.set(currentNamespace
);