// Copyright 2016 The Nomulus Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package google.registry.model.ofy; import static com.google.appengine.api.memcache.ErrorHandlers.getConsistentLogAndContinue; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Predicates.not; import static com.googlecode.objectify.ObjectifyService.factory; import static google.registry.util.TypeUtils.hasAnnotation; import com.google.appengine.api.datastore.AsyncDatastoreService; import com.google.appengine.api.datastore.DatastoreServiceConfig; import com.google.appengine.api.datastore.DatastoreServiceFactory; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Iterables; import com.googlecode.objectify.Key; import com.googlecode.objectify.Objectify; import com.googlecode.objectify.ObjectifyFactory; import com.googlecode.objectify.annotation.Entity; import com.googlecode.objectify.annotation.EntitySubclass; import com.googlecode.objectify.impl.translate.TranslatorFactory; import com.googlecode.objectify.impl.translate.opt.joda.MoneyStringTranslatorFactory; import google.registry.config.RegistryEnvironment; import google.registry.model.EntityClasses; import google.registry.model.ImmutableObject; import google.registry.model.translators.CidrAddressBlockTranslatorFactory; import google.registry.model.translators.CommitLogRevisionsTranslatorFactory; import google.registry.model.translators.CreateAutoTimestampTranslatorFactory; import google.registry.model.translators.CurrencyUnitTranslatorFactory; import google.registry.model.translators.DurationTranslatorFactory; import google.registry.model.translators.InetAddressTranslatorFactory; import google.registry.model.translators.ReadableInstantUtcTranslatorFactory; import google.registry.model.translators.UpdateAutoTimestampTranslatorFactory; import java.util.Arrays; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; /** * An instance of Ofy, obtained via {@code #ofy()}, should be used to access all persistable * objects. The class contains a static initializer to call factory().register(...) on all * persistable objects in this package. */ public class ObjectifyService { /** * A placeholder String passed into DatastoreService.allocateIds that ensures that all ids are * initialized from the same id pool. */ public static final String APP_WIDE_ALLOCATION_KIND = "common"; /** A singleton instance of our Ofy wrapper. */ private static final Ofy OFY = new Ofy(null); /** * Returns a singleton {@link Ofy} instance. * *
Deprecated: This will go away once everything injects {@code Ofy}. */ public static Ofy ofy() { return OFY; } static { initOfyOnce(); } /** Ensures that Objectify has been fully initialized. */ public static void initOfy() { // This method doesn't actually do anything; it's here so that callers have something to call // to ensure that the static initialization of ObjectifyService has been performed (which Java // guarantees will happen exactly once, before any static methods are invoked). // // See JLS section 12.4: http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4 } /** * Performs static initialization for Objectify to register types and do other setup. * *
This method is non-idempotent, so it should only be called exactly once, which is achieved
* by calling it from this class's static initializer block.
*/
private static void initOfyOnce() {
// Set an ObjectifyFactory that uses our extended ObjectifyImpl.
// The "false" argument means that we are not using the v5-style Objectify embedded entities.
com.googlecode.objectify.ObjectifyService.setFactory(new ObjectifyFactory(false) {
@Override
public Objectify begin() {
return new SessionKeyExposingObjectify(this);
}
@Override
protected AsyncDatastoreService createRawAsyncDatastoreService(DatastoreServiceConfig cfg) {
// In the unit test environment, wrap the datastore service in a proxy that can be used to
// examine the number of requests sent to datastore.
AsyncDatastoreService service = super.createRawAsyncDatastoreService(cfg);
return RegistryEnvironment.get().equals(RegistryEnvironment.UNITTEST)
? new RequestCapturingAsyncDatastoreService(service)
: service;
}});
// Translators must be registered before any entities can be registered.
registerTranslators();
registerEntityClasses(EntityClasses.ALL_CLASSES);
// Set the memcache error handler so that we don't see internally logged errors.
factory().setMemcacheErrorHandler(getConsistentLogAndContinue(Level.INFO));
}
/** Register translators that allow less common types to be stored directly in Datastore. */
private static void registerTranslators() {
for (TranslatorFactory> translatorFactory : Arrays.asList(
new CidrAddressBlockTranslatorFactory(),
new CommitLogRevisionsTranslatorFactory(),
new CreateAutoTimestampTranslatorFactory(),
new CurrencyUnitTranslatorFactory(),
new DurationTranslatorFactory(),
new InetAddressTranslatorFactory(),
new MoneyStringTranslatorFactory(),
new ReadableInstantUtcTranslatorFactory(),
new UpdateAutoTimestampTranslatorFactory())) {
factory().getTranslators().add(translatorFactory);
}
}
/** Register classes that can be persisted via Objectify as datastore entities. */
private static void registerEntityClasses(
Iterable