1 // Copyright 2009 Google Inc. All Rights Reserved.
3 package com
.google
.appengine
.api
;
5 import com
.google
.apphosting
.api
.ApiProxy
;
6 import com
.google
.apphosting
.api
.ApiProxy
.Environment
;
9 import java
.util
.regex
.Pattern
;
12 * Provides functions for manipulating the current namespace used for
15 * <p>The "current namespace" is the string that is returned by
16 * {@link #get()} and used by a number of APIs including Datatore,
17 * Memcache and Task Queue.
19 * <p>When a namespace aware class (e.g.,
20 * {@link com.google.appengine.api.datastore.Key},
21 * {@link com.google.appengine.api.datastore.Query} and
22 * {@link com.google.appengine.api.memcache.MemcacheService}) is constructed, it
23 * determines which namespace will be used by calling
24 * {@link NamespaceManager#get()} if it is otherwise unspecified. If
25 * {@link NamespaceManager#get()} returns null, the current namespace is unset
26 * and these APIs will use the empty ("") namespace in its place.
28 * <p>Example: <code><pre>
29 * {@link NamespaceManager}.{@link #set}("a-namespace");
30 * MemcacheService memcache = MemcacheServiceFactory.getMemcacheService();
31 * // Store record in namespace "a-namespace"
32 * memcache.put("key1", "value1");
34 * {@link NamespaceManager}.{@link #set}("other-namespace");
35 * // Store record in namespace "other-namespace"
36 * memcache.put("key2", "value2");
38 * MemcacheService boundMemcache =
39 * MemcacheServiceFactory.getMemcacheService("specific-namespace");
40 * {@link NamespaceManager}.{@link #set}("whatever-namespace");
41 * // The record is still stored in namespace "specific-namespace".
42 * boundMemcache.put("key3", "value3");
45 * <p>MemcacheService {@code memcache} (in the above example) uses the current
46 * namespace and {@code key1} will be stored in namespace {@code "a-namespace"},
47 * while {@code key2} is stored in namespace {@code "other-namespace"}. It is
48 * possible to override the current namespace and store data in specific
49 * namespace. In the above example {@code key3} is stored in namespace
50 * {@code "specific-namespace"}.
52 * <p>The Task Queue {@link com.google.appengine.api.taskqueue.Queue#add}
53 * methods will forward the {@link NamespaceManager} settings into the task
54 * being added causing the added task to be executed with the same current
55 * namespace as the task creator. The exception is that an unset current
56 * namespace (i.e. {@link NamespaceManager#get()} returns null) will be
57 * forwarded as an empty ("") namespace to the created task's requests.
60 * href="http://code.google.com/appengine/docs/java/multitenancy/">
61 * Multitenancy and the Namespaces Java API. In <em>Google App Engine
62 * Developer's Guide</em></a>.
64 public final class NamespaceManager
{
65 private final static int NAMESPACE_MAX_LENGTH
= 100;
66 private final static String NAMESPACE_REGEX
= "[0-9A-Za-z._-]{0," + NAMESPACE_MAX_LENGTH
+"}";
67 private static final Pattern NAMESPACE_PATTERN
= Pattern
.compile(NAMESPACE_REGEX
);
70 * We store the current namespace as an environment attribute identified
73 private static final String CURRENT_NAMESPACE_KEY
=
74 NamespaceManager
.class.getName() + ".currentNamespace";
75 private static final String APPS_NAMESPACE_KEY
=
76 NamespaceManager
.class.getName() + ".appsNamespace";
79 * Set the value used to initialize the namespace of namespace-aware services.
81 * @param newNamespace the new namespace.
82 * @throws IllegalArgumentException if namespace string is invalid.
84 public static void set(String newNamespace
) {
85 if (newNamespace
== null) {
86 ApiProxy
.getCurrentEnvironment().getAttributes().remove(CURRENT_NAMESPACE_KEY
);
88 validateNamespace(newNamespace
);
89 Environment environment
= ApiProxy
.getCurrentEnvironment();
90 environment
.getAttributes().put(CURRENT_NAMESPACE_KEY
, newNamespace
);
95 * Returns the current namespace setting or {@code null} if not set.
97 * <p>If the current namespace is unset, callers should assume
98 * the use of the "" (empty) namespace in all namespace-aware services.
100 public static String
get() {
101 Map
<String
, Object
> attributes
= ApiProxy
.getCurrentEnvironment().getAttributes();
102 return (String
) attributes
.get(CURRENT_NAMESPACE_KEY
);
106 * Returns the Google Apps domain referring this request or
107 * otherwise the empty string ("").
109 public static String
getGoogleAppsNamespace() {
110 Map
<String
, Object
> attributes
= ApiProxy
.getCurrentEnvironment().getAttributes();
111 String appsNamespace
= (String
) attributes
.get(APPS_NAMESPACE_KEY
);
112 return appsNamespace
== null ?
"" : appsNamespace
;
116 * Validate the format of a namespace string.
117 * @throws IllegalArgumentException If the format of the namespace string
120 public static void validateNamespace(String namespace
) {
121 if (!NAMESPACE_PATTERN
.matcher(namespace
).matches()) {
122 throw new IllegalArgumentException(
123 "Namespace '" + namespace
+ "' does not match pattern '" + NAMESPACE_PATTERN
+ "'.");
127 private NamespaceManager() {