From 756c951112c1b120502a29b7851beeacb17c5ffd Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 7 Aug 2014 12:09:34 +0200 Subject: [PATCH] DATACMNS-557 - Polishing. Renamed CdiRepositoryConfigurationSource to CdiRepositoryConfiguration. Turned anonymous inner default implementation into dedicated enum to avoid repeated instantiation. Changed the CdiRepositoryBean to rather take a Bean instance of the custom implementation instead of the resolved object to make sure both instances (the custom implementation and the repository proxy) are instantiated at the same time. Fixed imports. Some early returns to avoid unnecessary nesting. Adapted architecture description to allow CDI module accessing the configuration module. Related pull request: #92. --- Spring Data Commons.sonargraph | 15 ++- .../repository/cdi/CdiRepositoryBean.java | 24 +++- ...e.java => CdiRepositoryConfiguration.java} | 2 +- .../cdi/CdiRepositoryExtensionSupport.java | 121 +++++++++++------- ...ustomRepositoryImplementationDetector.java | 105 +++++++-------- .../repository/cdi/DummyCdiExtension.java | 27 ++-- 6 files changed, 168 insertions(+), 126 deletions(-) rename src/main/java/org/springframework/data/repository/cdi/{CdiRepositoryConfigurationSource.java => CdiRepositoryConfiguration.java} (95%) diff --git a/Spring Data Commons.sonargraph b/Spring Data Commons.sonargraph index 093ca1591..171fe2174 100644 --- a/Spring Data Commons.sonargraph +++ b/Spring Data Commons.sonargraph @@ -1,5 +1,5 @@ - + @@ -66,6 +66,12 @@ + + + + + + @@ -73,6 +79,7 @@ + @@ -82,12 +89,6 @@ - - - - - - diff --git a/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryBean.java b/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryBean.java index 91ef6d841..ed0422ac5 100644 --- a/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryBean.java +++ b/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryBean.java @@ -51,7 +51,7 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl private final Set qualifiers; private final Class repositoryType; - private final Object customImplementation; + private final Bean customImplementationBean; private final BeanManager beanManager; private final String passivationId; @@ -74,9 +74,11 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl * @param qualifiers must not be {@literal null}. * @param repositoryType has to be an interface must not be {@literal null}. * @param beanManager the CDI {@link BeanManager}, must not be {@literal null}. - * @param customImplementation the custom implementation of the {@link org.springframework.data.repository.Repository}, can be {@literal null}. + * @param customImplementationBean the bean for the custom implementation of the + * {@link org.springframework.data.repository.Repository}, can be {@literal null}. */ - public CdiRepositoryBean(Set qualifiers, Class repositoryType, BeanManager beanManager, Object customImplementation) { + public CdiRepositoryBean(Set qualifiers, Class repositoryType, BeanManager beanManager, + Bean customImplementationBean) { Assert.notNull(qualifiers); Assert.notNull(beanManager); @@ -86,7 +88,7 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl this.qualifiers = qualifiers; this.repositoryType = repositoryType; this.beanManager = beanManager; - this.customImplementation = customImplementation; + this.customImplementationBean = customImplementationBean; this.passivationId = createPassivationId(qualifiers, repositoryType); } @@ -265,8 +267,15 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl * @param creationalContext will never be {@literal null}. * @param repositoryType will never be {@literal null}. * @return + * @deprecated overide {@link #create(CreationalContext, Class, Object)} instead. */ + @Deprecated protected T create(CreationalContext creationalContext, Class repositoryType) { + + Object customImplementation = customImplementationBean == null ? null : beanManager.getReference( + customImplementationBean, customImplementationBean.getBeanClass(), + beanManager.createCreationalContext(customImplementationBean)); + return create(creationalContext, repositoryType, customImplementation); } @@ -279,8 +288,8 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl * @return */ protected T create(CreationalContext creationalContext, Class repositoryType, Object customImplementation) { - throw new UnsupportedOperationException("You have to implement create(CreationalContext, Class, Object) " + - "in order to use custom repository implementations"); + throw new UnsupportedOperationException("You have to implement create(CreationalContext, Class, Object) " + + "in order to use custom repository implementations"); } /* @@ -289,6 +298,7 @@ public abstract class CdiRepositoryBean implements Bean, PassivationCapabl */ @Override public String toString() { - return String.format("JpaRepositoryBean: type='%s', qualifiers=%s", repositoryType.getName(), qualifiers.toString()); + return String + .format("JpaRepositoryBean: type='%s', qualifiers=%s", repositoryType.getName(), qualifiers.toString()); } } diff --git a/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryConfigurationSource.java b/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryConfiguration.java similarity index 95% rename from src/main/java/org/springframework/data/repository/cdi/CdiRepositoryConfigurationSource.java rename to src/main/java/org/springframework/data/repository/cdi/CdiRepositoryConfiguration.java index 6e6f5271c..97f9ad29b 100644 --- a/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryConfigurationSource.java +++ b/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryConfiguration.java @@ -21,7 +21,7 @@ package org.springframework.data.repository.cdi; * * @author Mark Paluch */ -public interface CdiRepositoryConfigurationSource { +public interface CdiRepositoryConfiguration { /** * Returns the configured postfix to be used for looking up custom implementation classes. diff --git a/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupport.java b/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupport.java index ad97517a4..2baba6fb8 100644 --- a/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupport.java +++ b/src/main/java/org/springframework/data/repository/cdi/CdiRepositoryExtensionSupport.java @@ -15,17 +15,27 @@ */ package org.springframework.data.repository.cdi; +import java.lang.annotation.Annotation; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.event.Observes; import javax.enterprise.inject.Any; import javax.enterprise.inject.Default; import javax.enterprise.inject.UnsatisfiedResolutionException; -import javax.enterprise.inject.spi.*; +import javax.enterprise.inject.spi.AfterDeploymentValidation; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.Extension; +import javax.enterprise.inject.spi.ProcessAnnotatedType; import javax.enterprise.util.AnnotationLiteral; import javax.inject.Qualifier; -import java.lang.annotation.Annotation; -import java.util.*; -import java.util.Map.Entry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,8 +52,8 @@ import org.springframework.data.repository.Repository; import org.springframework.data.repository.RepositoryDefinition; import org.springframework.data.repository.config.CustomRepositoryImplementationDetector; import org.springframework.data.repository.config.DefaultRepositoryConfiguration; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; /** * Base class for {@link Extension} implementations that create instances for Spring Data repositories. @@ -55,17 +65,20 @@ import org.springframework.util.StringUtils; public abstract class CdiRepositoryExtensionSupport implements Extension { private static final Logger LOGGER = LoggerFactory.getLogger(CdiRepositoryExtensionSupport.class); + private static final CdiRepositoryConfiguration DEFAULT_CONFIGURATION = DefaultCdiRepositoryConfiguration.INSTANCE; private final Map, Set> repositoryTypes = new HashMap, Set>(); private final Set> eagerRepositories = new HashSet>(); private final CustomRepositoryImplementationDetector customImplementationDetector; protected CdiRepositoryExtensionSupport() { + Environment environment = new StandardEnvironment(); ResourceLoader resourceLoader = new PathMatchingResourcePatternResolver(getClass().getClassLoader()); MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader); - customImplementationDetector = new CustomRepositoryImplementationDetector(metadataReaderFactory, - environment, resourceLoader); + + this.customImplementationDetector = new CustomRepositoryImplementationDetector(metadataReaderFactory, environment, + resourceLoader); } /** @@ -176,26 +189,24 @@ public abstract class CdiRepositoryExtensionSupport implements Extension { } /** - * Looks up an instance of a {@link CdiRepositoryConfigurationSource}. In case the instance cannot be found within - * the CDI scope, a default configuration is used. + * Looks up an instance of a {@link CdiRepositoryConfiguration}. In case the instance cannot be found within the CDI + * scope, a default configuration is used. * - * @return an available CdiRepositoryConfigurationSource instance or a default configuration. + * @return an available CdiRepositoryConfiguration instance or a default configuration. */ - protected CdiRepositoryConfigurationSource lookupConfiguration(BeanManager beanManager, Set qualifiers) { - Set> beans = beanManager.getBeans(CdiRepositoryConfigurationSource.class, getQualifiersArray(qualifiers)); + protected CdiRepositoryConfiguration lookupConfiguration(BeanManager beanManager, Set qualifiers) { + + Set> beans = beanManager.getBeans(CdiRepositoryConfiguration.class, getQualifiersArray(qualifiers)); + if (beans.isEmpty()) { - // no own defined type since these would be picked up by CDI by default. - return new CdiRepositoryConfigurationSource() { - @Override - public String getRepositoryImplementationPostfix() { - return DefaultRepositoryConfiguration.DEFAULT_REPOSITORY_IMPLEMENTATION_POSTFIX; - } - }; + return DEFAULT_CONFIGURATION; } Bean bean = beans.iterator().next(); CreationalContext creationalContext = beanManager.createCreationalContext(bean); - return (CdiRepositoryConfigurationSource) beanManager.getReference(bean, CdiRepositoryConfigurationSource.class, creationalContext); + + return (CdiRepositoryConfiguration) beanManager.getReference(bean, CdiRepositoryConfiguration.class, + creationalContext); } private Annotation[] getQualifiersArray(Set qualifiers) { @@ -210,52 +221,54 @@ public abstract class CdiRepositoryExtensionSupport implements Extension { * @param qualifiers * @return the custom implementation instance or null */ - protected Object findCustomImplementation(Class repositoryType, BeanManager beanManager, Set qualifiers) { + protected Bean getCustomImplementationBean(Class repositoryType, BeanManager beanManager, + Set qualifiers) { - CdiRepositoryConfigurationSource cdiRepositoryConfiguration = lookupConfiguration(beanManager, qualifiers); - Class customImplementationClass = findCustomImplementationClass(repositoryType, cdiRepositoryConfiguration); - if (customImplementationClass != null) { - Set> beans = beanManager.getBeans(customImplementationClass, getQualifiersArray(qualifiers)); - if (!beans.isEmpty()) { - Bean bean = beans.iterator().next(); - CreationalContext creationalContext = beanManager.createCreationalContext(bean); - return beanManager.getReference(bean, customImplementationClass, creationalContext); - } + CdiRepositoryConfiguration cdiRepositoryConfiguration = lookupConfiguration(beanManager, qualifiers); + Class customImplementationClass = getCustomImplementationClass(repositoryType, cdiRepositoryConfiguration); + + if (customImplementationClass == null) { + return null; } - return null; + Set> beans = beanManager.getBeans(customImplementationClass, getQualifiersArray(qualifiers)); + return beans.isEmpty() ? null : beans.iterator().next(); } /** - * Retrieves a custom repository interfaces from a repository type. This works for the whole class hierarchy and can find - * also a custom repo which is inherieted over many levels. + * Retrieves a custom repository interfaces from a repository type. This works for the whole class hierarchy and can + * find also a custom repo which is inherieted over many levels. * * @param repositoryType The class representing the repository. * @param cdiRepositoryConfiguration The configuration for CDI usage. - * @return the interface class or null. + * @return the interface class or {@literal null}. */ - protected Class findCustomImplementationClass(Class repositoryType, CdiRepositoryConfigurationSource cdiRepositoryConfiguration) { + private Class getCustomImplementationClass(Class repositoryType, + CdiRepositoryConfiguration cdiRepositoryConfiguration) { - String className = constructCustomImplementationClassName(repositoryType, cdiRepositoryConfiguration); + String className = getCustomImplementationClassName(repositoryType, cdiRepositoryConfiguration); AbstractBeanDefinition beanDefinition = customImplementationDetector.detectCustomImplementation(className, Collections.singleton(repositoryType.getPackage().getName())); - if (beanDefinition != null) { - try { - return Class.forName(beanDefinition.getBeanClassName()); - } catch (ClassNotFoundException e) { - throw new UnsatisfiedResolutionException(String.format("Unable to resolve class for '%s'", - beanDefinition.getBeanClassName()), e); - } + if (beanDefinition == null) { + return null; } - return null; + try { + return Class.forName(beanDefinition.getBeanClassName()); + } catch (ClassNotFoundException e) { + throw new UnsatisfiedResolutionException(String.format("Unable to resolve class for '%s'", + beanDefinition.getBeanClassName()), e); + } } - private String constructCustomImplementationClassName(Class repositoryType, CdiRepositoryConfigurationSource cdiRepositoryConfiguration) { + private String getCustomImplementationClassName(Class repositoryType, + CdiRepositoryConfiguration cdiRepositoryConfiguration) { + String configuredPostfix = cdiRepositoryConfiguration.getRepositoryImplementationPostfix(); - String implementationPostfix = StringUtils.hasText(configuredPostfix) ? configuredPostfix : DefaultRepositoryConfiguration.DEFAULT_REPOSITORY_IMPLEMENTATION_POSTFIX; - return ClassUtils.getShortName(repositoryType) + implementationPostfix; + Assert.hasText(configuredPostfix, "Configured repository postfix must not be null or empty!"); + + return ClassUtils.getShortName(repositoryType) + configuredPostfix; } @SuppressWarnings("all") @@ -271,4 +284,18 @@ public abstract class CdiRepositoryExtensionSupport implements Extension { private static final long serialVersionUID = 7261821376671361463L; private static final AnyAnnotationLiteral INSTANCE = new AnyAnnotationLiteral(); } + + static enum DefaultCdiRepositoryConfiguration implements CdiRepositoryConfiguration { + + INSTANCE; + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.cdi.CdiRepositoryConfiguration#getRepositoryImplementationPostfix() + */ + @Override + public String getRepositoryImplementationPostfix() { + return DefaultRepositoryConfiguration.DEFAULT_REPOSITORY_IMPLEMENTATION_POSTFIX; + } + } } diff --git a/src/main/java/org/springframework/data/repository/config/CustomRepositoryImplementationDetector.java b/src/main/java/org/springframework/data/repository/config/CustomRepositoryImplementationDetector.java index ebdff8037..e4b5262f3 100644 --- a/src/main/java/org/springframework/data/repository/config/CustomRepositoryImplementationDetector.java +++ b/src/main/java/org/springframework/data/repository/config/CustomRepositoryImplementationDetector.java @@ -35,81 +35,82 @@ import org.springframework.util.StringUtils; /** * Detects the custom implementation for a {@link org.springframework.data.repository.Repository} * + * @author Oliver Gierke * @author Mark Paluch */ public class CustomRepositoryImplementationDetector { - private static final String CUSTOM_IMPLEMENTATION_RESOURCE_PATTERN = "**/*%s.class"; + private static final String CUSTOM_IMPLEMENTATION_RESOURCE_PATTERN = "**/*%s.class"; - private MetadataReaderFactory metadataReaderFactory; - private Environment environment; - private ResourceLoader resourceLoader; + private final MetadataReaderFactory metadataReaderFactory; + private final Environment environment; + private final ResourceLoader resourceLoader; /** - * - * Creates a new {@link CustomRepositoryImplementationDetector} from the given {@link org.springframework.core.type.classreading.MetadataReaderFactory}, + * Creates a new {@link CustomRepositoryImplementationDetector} from the given + * {@link org.springframework.core.type.classreading.MetadataReaderFactory}, * {@link org.springframework.core.env.Environment} and {@link org.springframework.core.io.ResourceLoader}. * * @param metadataReaderFactory must not be {@literal null}. * @param environment must not be {@literal null}. * @param resourceLoader must not be {@literal null}. */ - public CustomRepositoryImplementationDetector(MetadataReaderFactory metadataReaderFactory, Environment environment, - ResourceLoader resourceLoader) { + public CustomRepositoryImplementationDetector(MetadataReaderFactory metadataReaderFactory, Environment environment, + ResourceLoader resourceLoader) { - Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null!"); - Assert.notNull(resourceLoader, "ResourceLoader must not be null!"); - Assert.notNull(environment, "Environment must not be null!"); + Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null!"); + Assert.notNull(resourceLoader, "ResourceLoader must not be null!"); + Assert.notNull(environment, "Environment must not be null!"); - this.metadataReaderFactory = metadataReaderFactory; - this.environment = environment; - this.resourceLoader = resourceLoader; - } + this.metadataReaderFactory = metadataReaderFactory; + this.environment = environment; + this.resourceLoader = resourceLoader; + } - /** - * Tries to detect a custom implementation for a repository bean by classpath scanning. - * - * @param className must not be {@literal null}. - * @param basePackages must not be {@literal null}. - * @return the {@code AbstractBeanDefinition} of the custom implementation or {@literal null} if none found - */ - public AbstractBeanDefinition detectCustomImplementation(String className, Iterable basePackages) { + /** + * Tries to detect a custom implementation for a repository bean by classpath scanning. + * + * @param className must not be {@literal null}. + * @param basePackages must not be {@literal null}. + * @return the {@code AbstractBeanDefinition} of the custom implementation or {@literal null} if none found + */ + public AbstractBeanDefinition detectCustomImplementation(String className, Iterable basePackages) { - Assert.notNull(className, "ClassName must not be null!"); - Assert.notNull(basePackages, "BasePackages must not be null!"); + Assert.notNull(className, "ClassName must not be null!"); + Assert.notNull(basePackages, "BasePackages must not be null!"); - // Build pattern to lookup implementation class - Pattern pattern = Pattern.compile(".*\\." + className); + // Build pattern to lookup implementation class + Pattern pattern = Pattern.compile(".*\\." + className); - // Build classpath scanner and lookup bean definition - ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false); - provider.setEnvironment(environment); - provider.setResourceLoader(resourceLoader); - provider.setResourcePattern(String.format(CUSTOM_IMPLEMENTATION_RESOURCE_PATTERN, className)); - provider.setMetadataReaderFactory(metadataReaderFactory); - provider.addIncludeFilter(new RegexPatternTypeFilter(pattern)); + // Build classpath scanner and lookup bean definition + ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false); + provider.setEnvironment(environment); + provider.setResourceLoader(resourceLoader); + provider.setResourcePattern(String.format(CUSTOM_IMPLEMENTATION_RESOURCE_PATTERN, className)); + provider.setMetadataReaderFactory(metadataReaderFactory); + provider.addIncludeFilter(new RegexPatternTypeFilter(pattern)); - Set definitions = new HashSet(); + Set definitions = new HashSet(); - for (String basePackage : basePackages) { - definitions.addAll(provider.findCandidateComponents(basePackage)); - } + for (String basePackage : basePackages) { + definitions.addAll(provider.findCandidateComponents(basePackage)); + } - if (definitions.isEmpty()) { - return null; - } + if (definitions.isEmpty()) { + return null; + } - if (definitions.size() == 1) { - return (AbstractBeanDefinition) definitions.iterator().next(); - } + if (definitions.size() == 1) { + return (AbstractBeanDefinition) definitions.iterator().next(); + } - List implementationClassNames = new ArrayList(); - for (BeanDefinition bean : definitions) { - implementationClassNames.add(bean.getBeanClassName()); - } + List implementationClassNames = new ArrayList(); + for (BeanDefinition bean : definitions) { + implementationClassNames.add(bean.getBeanClassName()); + } - throw new IllegalStateException(String.format( - "Ambiguous custom implementations detected! Found %s but expected a single implementation!", - StringUtils.collectionToCommaDelimitedString(implementationClassNames))); - } + throw new IllegalStateException(String.format( + "Ambiguous custom implementations detected! Found %s but expected a single implementation!", + StringUtils.collectionToCommaDelimitedString(implementationClassNames))); + } } diff --git a/src/test/java/org/springframework/data/repository/cdi/DummyCdiExtension.java b/src/test/java/org/springframework/data/repository/cdi/DummyCdiExtension.java index 54d5bf9de..0450b06d9 100644 --- a/src/test/java/org/springframework/data/repository/cdi/DummyCdiExtension.java +++ b/src/test/java/org/springframework/data/repository/cdi/DummyCdiExtension.java @@ -15,12 +15,6 @@ */ package org.springframework.data.repository.cdi; -import javax.enterprise.context.NormalScope; -import javax.enterprise.context.spi.Contextual; -import javax.enterprise.context.spi.CreationalContext; -import javax.enterprise.event.Observes; -import javax.enterprise.inject.spi.AfterBeanDiscovery; -import javax.enterprise.inject.spi.BeanManager; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -28,6 +22,14 @@ import java.util.HashMap; import java.util.Map.Entry; import java.util.Set; +import javax.enterprise.context.NormalScope; +import javax.enterprise.context.spi.Contextual; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.event.Observes; +import javax.enterprise.inject.spi.AfterBeanDiscovery; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; + import org.apache.webbeans.context.AbstractContext; import org.apache.webbeans.context.creational.BeanInstanceBag; import org.mockito.Mockito; @@ -40,15 +42,14 @@ import org.mockito.Mockito; */ public class DummyCdiExtension extends CdiRepositoryExtensionSupport { - @SuppressWarnings({"rawtypes", "unchecked"}) + @SuppressWarnings({ "rawtypes", "unchecked" }) void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) { afterBeanDiscovery.addContext(new MyCustomScope()); for (Entry, Set> type : getRepositoryTypes()) { - CdiRepositoryConfigurationSource configurationSource = lookupConfiguration(beanManager, type.getValue()); - Object customImplementation = findCustomImplementation(type.getKey(), beanManager, type.getValue()); - - DummyCdiRepositoryBean bean = new DummyCdiRepositoryBean(type.getValue(), type.getKey(), beanManager, customImplementation); + Bean customImplementation = getCustomImplementationBean(type.getKey(), beanManager, type.getValue()); + DummyCdiRepositoryBean bean = new DummyCdiRepositoryBean(type.getValue(), type.getKey(), beanManager, + customImplementation); registerBean(bean); afterBeanDiscovery.addBean(bean); } @@ -60,7 +61,8 @@ public class DummyCdiExtension extends CdiRepositoryExtensionSupport { super(qualifiers, repositoryType, beanManager); } - DummyCdiRepositoryBean(Set qualifiers, Class repositoryType, BeanManager beanManager, Object customImplementation) { + DummyCdiRepositoryBean(Set qualifiers, Class repositoryType, BeanManager beanManager, + Bean customImplementation) { super(qualifiers, repositoryType, beanManager, customImplementation); } @@ -80,6 +82,7 @@ public class DummyCdiExtension extends CdiRepositoryExtensionSupport { } + @SuppressWarnings("serial") static class MyCustomScope extends AbstractContext { MyCustomScope() {