Class AbstractConcurrentCustomScope<A extends Annotation>

java.lang.Object
io.micronaut.context.scope.AbstractConcurrentCustomScope<A>
Type Parameters:
A - The annotation type
All Implemented Interfaces:
LifeCycle<AbstractConcurrentCustomScope<A>>, CustomScope<A>, Closeable, AutoCloseable

public abstract class AbstractConcurrentCustomScope<A extends Annotation> extends Object implements CustomScope<A>, LifeCycle<AbstractConcurrentCustomScope<A>>, AutoCloseable
Abstract implementation of the custom scope interface that simplifies defining new scopes using the Map interface.

By default this implementation uses a single ReentrantReadWriteLock to lock the entire scope, and holds its write lock while a bean is created, hence it is designed for scopes that will hold a small amount of beans whose creation never waits on another thread. A scope that holds many beans, or whose beans may wait during creation for another thread that creates a bean of the same scope, should opt into a lock per BeanIdentifier through AbstractConcurrentCustomScope(Class, boolean).

Since:
3.0.0
Author:
graemerocher
  • Constructor Details

    • AbstractConcurrentCustomScope

      protected AbstractConcurrentCustomScope(Class<A> annotationType)
      A custom scope annotation.
      Parameters:
      annotationType - The annotation type
    • AbstractConcurrentCustomScope

      protected AbstractConcurrentCustomScope(Class<A> annotationType, boolean lockPerBean)
      A custom scope annotation, choosing how creation is locked.

      With lockPerBean the scope-wide lock is not used at all. A bean is created under a lock that belongs to its BeanIdentifier alone, so that beans of different identifiers are created in parallel, a creation may wait for another thread that creates a bean of the same scope, and the fast path of a bean already held is a plain lookup. remove(BeanIdentifier) takes the same lock, so it still waits for a creation of that identifier in flight and then destroys what was created, and so do remove(BeanDefinition) and destroyScope(Map), which wait for the creations in flight of the map they work on. Nothing else holds creation off: a creation that has not begun when a destruction of the scope map ends publishes its bean into that map afterwards, exactly as one that arrives after the scope-wide write lock is released does. findBeanRegistration(Object) works on the scope map without any lock, which is why the map returned by getScopeMap(boolean) must then be a ConcurrentMap: getOrCreate(BeanCreationContext) rejects any other map with an IllegalStateException. The lock objects live as long as the scope, one per identifier ever created or removed through it.

      Parameters:
      annotationType - The annotation type
      lockPerBean - Whether to lock creation per BeanIdentifier rather than for the whole scope
      Since:
      5.2.0
  • Method Details

    • getScopeMap

      protected abstract @Nullable Map<BeanIdentifier, CreatedBean<?>> getScopeMap(boolean forCreation)
      Parameters:
      forCreation - Whether it is for creation
      Returns:
      Obtains the scope map, never null
      Throws:
      IllegalStateException - if the scope map cannot be obtained in the current context
    • annotationType

      public final Class<A> annotationType()
      Specified by:
      annotationType in interface CustomScope<A extends Annotation>
      Returns:
      The scope annotation type
    • close

      public abstract void close()
      Implement the close logic for the scope.
      Specified by:
      close in interface AutoCloseable
      Specified by:
      close in interface Closeable
      Specified by:
      close in interface LifeCycle<A extends Annotation>
    • stop

      public final AbstractConcurrentCustomScope<A> stop()
      Description copied from interface: LifeCycle
      Stops the life cycle component.
      Specified by:
      stop in interface LifeCycle<A extends Annotation>
      Returns:
      This lifecycle component
    • destroyScope

      protected void destroyScope(@Nullable Map<BeanIdentifier, CreatedBean<?>> scopeMap)
      Destroys the scope.
      Parameters:
      scopeMap - The scope map
    • getOrCreate

      public final <T> T getOrCreate(BeanCreationContext<T> creationContext)
      Description copied from interface: CustomScope
      Resolve an object from the given scope.
      Specified by:
      getOrCreate in interface CustomScope<A extends Annotation>
      Type Parameters:
      T - The bean generic type
      Parameters:
      creationContext - The creation context
      Returns:
      The bean instance
    • doCreate

      protected <T> CreatedBean<T> doCreate(BeanCreationContext<T> creationContext)
      Perform creation.
      Type Parameters:
      T - The generic type
      Parameters:
      creationContext - The creation context
      Returns:
      Created bean
    • remove

      public final <T> Optional<T> remove(BeanIdentifier identifier)
      Description copied from interface: CustomScope
      Remove a bean definition from the scope.
      Specified by:
      remove in interface CustomScope<A extends Annotation>
      Type Parameters:
      T - The generic type
      Parameters:
      identifier - The BeanIdentifier
      Returns:
      An Optional of the instance that was destroyed if it exists
    • remove

      public <T> Optional<T> remove(BeanDefinition<T> beanDefinition)
      Remove and destroy the bean held for the given definition. The definition may be the ProxyBeanDefinition of a scoped proxy, in which case the target it stands for is removed.

      The bean is taken out of the scope under the write lock, or, where creation is locked per bean, under the lock of the identifier it is held under, having first waited for the creations into the scope map that are in flight so that a bean of the definition one of them is about to publish is not missed. It is closed after the lock is released, so a @PreDestroy hook may reach into the scope from another thread without deadlocking on it. Unlike remove(BeanIdentifier) a destruction failure is not routed through handleDestructionException(BeanDestructionException) but propagated to the caller.

      Type Parameters:
      T - The generic type
      Parameters:
      beanDefinition - The bean definition
      Returns:
      An Optional of the instance that was destroyed if it exists
      Throws:
      BeanDestructionException - If destroying the bean fails
      Since:
      5.2.0
    • handleDestructionException

      protected void handleDestructionException(BeanDestructionException e)
      Method that can be overridden to customize what happens on a shutdown error.
      Parameters:
      e - The exception
    • findBeanRegistration

      public final <T> Optional<BeanRegistration<T>> findBeanRegistration(T bean)
      Description copied from interface: CustomScope
      Get the BeanDefinition for the given bean.
      Specified by:
      findBeanRegistration in interface CustomScope<A extends Annotation>
      Type Parameters:
      T - The bean generic type
      Parameters:
      bean - The bean
      Returns:
      The bean definition if it can be resolved
    • findBeanRegistration

      public <T> Optional<BeanRegistration<T>> findBeanRegistration(BeanDefinition<T> beanDefinition)
      Finds the registration held for the given definition. The definition may be the ProxyBeanDefinition of a scoped proxy, in which case the registration of the target it stands for is returned.
      Specified by:
      findBeanRegistration in interface CustomScope<A extends Annotation>
      Type Parameters:
      T - The bean generic type
      Parameters:
      beanDefinition - The bean definition
      Returns:
      The registration if the scope holds an instance for the definition
      Since:
      5.2.0