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

arez.persist.runtime.Registry Maven / Gradle / Ivy

There is a newer version: 0.48
Show newest version
package arez.persist.runtime;

import arez.Disposable;
import arez.SafeProcedure;
import grim.annotations.OmitSymbol;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import static org.realityforge.braincheck.Guards.*;

/**
 * A utility class that contains references to stores and the root scope.
 * This is extracted to a separate class to eliminate the  from {@link ArezPersist} and thus
 * make it much easier for GWT to optimize out code based on build time compilation parameters.
 */
final class Registry
{
  /**
   * The map of registered stores.
   */
  @Nonnull
  private static final Map c_stores = new HashMap<>();
  /**
   * The map of converters for application types.
   */
  @Nonnull
  private static final Map, Converter> c_converters = new HashMap<>();
  /**
   * The root scope.
   */
  @Nullable
  private static Scope c_rootScope = Scope.create( null, Scope.ROOT_SCOPE_NAME );

  static
  {
    // register "app" store if enabled.
    ArezPersist.registerApplicationStoreIfEnabled();
  }

  private Registry()
  {
  }

  /**
   * Return the root scope under which all other scopes are nested.
   *
   * @return the root scope under which all other scopes are nested.
   */
  @Nonnull
  static Scope getRootScope()
  {
    if ( null == c_rootScope )
    {
      c_rootScope = Scope.create( null, Scope.ROOT_SCOPE_NAME );
    }
    return c_rootScope;
  }

  /**
   * Dispose the specified scope.
   * A dispose operation first performs a {@link #releaseScope(Scope)} on the scope, then attempts to
   * dispose all nested scopes and finally disposes the specified scope. A disposed scope should no longer be
   * used to store state. It is an error to attempt to dispose the root scope.
   *
   * @param scope the scope to dispose.
   */
  static void disposeScope( @Nonnull final Scope scope )
  {
    if ( ArezPersist.shouldCheckApiInvariants() )
    {
      apiInvariant( () -> Disposable.isNotDisposed( scope ),
                    () -> "disposeScope() passed a disposed scope named '" + scope.getName() + "'" );
    }
    releaseScope( scope );
    _disposeScope( scope );
  }

  /**
   * Release the specified scope.
   * A release operation removes any state associated with the scope and any nested scope.
   *
   * @param scope the scope to release.
   */
  static void releaseScope( @Nonnull final Scope scope )
  {
    if ( ArezPersist.shouldCheckApiInvariants() )
    {
      apiInvariant( () -> Disposable.isNotDisposed( scope ),
                    () -> "releaseScope() passed a disposed scope named '" + scope.getName() + "'" );
    }
    c_stores.values().forEach( store -> store.releaseScope( scope ) );
  }

  private static void _disposeScope( @Nonnull final Scope scope )
  {
    new ArrayList<>( scope.getNestedScopes() ).forEach( Registry::_disposeScope );
    Disposable.dispose( scope );
  }

  /**
   * Register a store with specified name and storage service.
   * It is an error to register multiple stores with the same name.
   *
   * 

As part of the register operation, the store will attempt to restore state from the storage service. * If an error occurs during the restore, then the error will be logged and registration will complete.

* * @param name the name of the store. * @param service the associated StorageService. * @return the action to invoke to deregister service. */ static SafeProcedure registerStore( @Nonnull final String name, @Nonnull final StorageService service ) { if ( ArezPersist.shouldCheckApiInvariants() ) { apiInvariant( () -> !c_stores.containsKey( name ), () -> "registerStore() invoked with name '" + name + "' but a store is already registered with that name" ); } final Store store = new Store( service ); c_stores.put( name, store ); try { store.restore(); } catch ( final Throwable t ) { LogUtil.getLogger().log( "Failed to restore state for store named '" + name + "'", t ); } return () -> deregisterStore( name ); } /** * Deregister a store with the specified name. * * @param name the name of the store. */ private static void deregisterStore( @Nonnull final String name ) { final Store store = c_stores.remove( name ); if ( null != store ) { assert !store.isDisposed(); store.dispose(); } } /** * Return the store that is registered with the specified name. * It is an error to invoke this method without registering a store under this name. * * @param name the name of the store. * @return the store. */ @Nonnull static Store getStore( @Nonnull final String name ) { if ( ArezPersist.shouldCheckApiInvariants() ) { apiInvariant( () -> c_stores.containsKey( name ), () -> "getStore() invoked with name " + name + " but no such store exists" ); } return Objects.requireNonNull( c_stores.get( name ) ); } /** * Register a converter for a type. * It is an error to register multiple converters with the same name. * * @param type the application type. * @param converter the converter. * @return the action to invoke to deregister converter. */ static SafeProcedure registerConverter( @Nonnull final Class type, @Nonnull final Converter converter ) { if ( ArezPersist.shouldCheckApiInvariants() ) { apiInvariant( () -> !c_converters.containsKey( type ), () -> "registerConverter() invoked with type '" + type.getName() + "' but a converter is already registered for that type" ); } c_converters.put( type, converter ); return () -> c_converters.remove( type ); } /** * Return the converter registered for the specified application type or the identity converter if none are specified. * * @param type the application type. * @return the converter if any. */ @SuppressWarnings( "unchecked" ) @Nonnull static Converter getConverter( @Nonnull final Class type ) { final Converter converter = (Converter) c_converters.get( type ); return null == converter ? IdentityConverter.instance() : converter; } @Nonnull static Map getStores() { return c_stores; } @Nonnull static Map, Converter> getConverters() { return c_converters; } /** * cleanup service. * This is dangerous as it may leave dangling references and should only be done in tests. */ @OmitSymbol static void reset() { c_stores.values().forEach( Store::dispose ); c_stores.clear(); c_converters.clear(); ArezPersist.registerApplicationStoreIfEnabled(); if ( null != c_rootScope ) { disposeScope( c_rootScope ); c_rootScope = null; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy