All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.sitebricks.persist.AbstractPersistenceModule Maven / Gradle / Ivy

package com.google.sitebricks.persist;

import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.PrivateModule;
import com.google.inject.Provider;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;

import javax.inject.Inject;
import javax.persistence.Entity;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.logging.Logger;

import static com.google.inject.matcher.Matchers.annotatedWith;

/**
 * Any Sitebricks persistence support module MUST subclass this module. It
 * provides many defaults and sanity checks.
 *
 * @author [email protected] (Dhanji R. Prasanna)
 */
public abstract class AbstractPersistenceModule extends PrivateModule {
  protected boolean autoStart = true;
  final Class selector;
  private final Set> entityClasses = new LinkedHashSet>();

  public AbstractPersistenceModule(Class selector) {
    this.selector = selector;
  }

  @Override
  protected final void configure() {
    configurePersistence();
    internalConfigure();

    // Bind the entity classes that have been registered in this persistence module.
    bind(new TypeLiteral>>() {})
        .toInstance(Collections.unmodifiableSet(entityClasses));
    bind(EntityMetadata.class);

    // Do some validation of the subclass module.
    if (autoStart) {
      if (currentStage() == Stage.DEVELOPMENT)
        Logger.getLogger(AbstractPersistenceModule.class.getName())
            .warning("Auto-start is on, in DEVELOPMENT this is OK. But please make sure you disable" +
                " it in production. See http://sitebricks.org/autostart for more info.");
      else if (currentStage() == Stage.PRODUCTION)
        addError("Auto-start is on, this is not allowed in PRODUCTION. Please make sure you start" +
            " and stop the Persister manually. " +
            "See http://sitebricks.org/autostart for more info.");

      bind(AutoStart.class).asEagerSingleton();
    }

    // Make sure that certain bindings are available.
    requireBinding(Persister.class);
    final Key persisterKey = selectorKey(Persister.class);
    Key entityStoreKey = selectorKey(EntityStore.class);
    bind(entityStoreKey).toProvider(new Provider() {
      @Inject Injector injector;

      @Override
      public EntityStore get() {
        return injector.getInstance(persisterKey).currentEntityStore();
      }
    });

    // Locally.
    if (selector != null)
      bind(persisterKey).to(Persister.class);
    expose(persisterKey);
    expose(entityStoreKey);
  }

  private static class AutoStart {
    @Inject
    private AutoStart(final Persister persister) {
      persister.start();

      // Schedule shutdown.
      Runtime.getRuntime().addShutdownHook(new Thread() {
        @Override
        public void run() {
          persister.shutdown();
        }
      });
    }
  }

  /**
   * Disables automatic lifecycle management of the database. Generally, auto-start
   * is only advisable during development. In production you should always use
   * {@link Persister#start()} and {@link Persister#shutdown()} to manage the datastore's
   * lifecycle yourself.
   * 

* If left enabled, Sitebricks will add a shutdown hook that cleans up the data store * before the JVM exits. */ protected void disableAutoStart() { autoStart = false; } protected abstract void internalConfigure(); /** * Called by subclasses to expose any interface or API. You MUST expose * via this method, in order for the correct datastore "selector" to be applied. * For example, this allows RedisModule to expose the Jedis artifact * without knowing the given database selector. In usage, then, a user can specify * their own annotation as a database selector: * *

   *  {@literal @}Inject {@literal @}TemporaryStore Jedis jedis;
   *
   *  {@literal @}Work {@literal @}TemporaryStore
   *   public void saveParameter() { .. }
   * 
*/ protected void exposeToUser(Class clazz) { Key key = selectorKey(clazz); // Create a linked binding from the exposed annotation, then expose that link. if (selector != null) bind(key).to(clazz); expose(key); } Key selectorKey(Class clazz) { Key key; if (selector != null) key = Key.get(clazz, selector); else key = Key.get(clazz); return key; } @SuppressWarnings("unchecked") protected void exposeEntityStoreDelegate(Class clazz) { final Key key = selectorKey(EntityStore.class); bind(clazz).toProvider(new Provider() { @Inject Injector injector; @Override public T get() { return (T) injector.getInstance(key).delegate(); } }); exposeToUser(clazz); } protected void configurePersistence() {} /** * Adds an entity class for mapping into the current datastore. Each datastore has * its own semantics for mapping fields of a class into its particular storage format. *

* For example, JpaModule maps fields into SQL columns whereas RedisModule converts * an entire object into JSON for storage as a string. Customizing these semantics * is specific to each datastore. Consult the specific implementations docs for details. */ protected final void addPersistent(Class entity) { if (entityClasses.contains(entity)) { addError("Entity class was added more than once: " + entity.getName() + "; to the same persistence module: " + getClass().getSimpleName() + (selector == null ? "" : " => @" + selector.getSimpleName())); return; } entityClasses.add(entity); } /** * Scans all classes in the given package and its subpackages to discover * classes marked with {@literal @}Entity, which are then mapped * into this PersistenceModule for storage in the underlying datastore. *

* This is the equivalent of calling {@link #addPersistent(Class)} for each * mapped class in the given package tree. *

* Note that classes not explicitly marked with the {@literal @}Entity * will be ignored. */ protected final void scan(Package tree) { Set> classes = Classes.matching(annotatedWith(Entity.class)).in(tree); for (Class clazz : classes) { addPersistent(clazz); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy