#53 - Tightened nullability contract and added nullability annotations.

This commit is contained in:
Oliver Drotbohm
2019-03-18 19:41:09 +01:00
parent 21cbf686c0
commit ec7ca4fb92
12 changed files with 128 additions and 65 deletions

View File

@@ -86,7 +86,7 @@ public class OrderAwarePluginRegistry<T extends Plugin<S>, S> extends SimplePlug
Assert.notNull(comparator, "Comparator must not be null!");
return create(Collections.emptyList(), comparator);
return of(Collections.emptyList(), comparator);
}
/**
@@ -98,7 +98,7 @@ public class OrderAwarePluginRegistry<T extends Plugin<S>, S> extends SimplePlug
*/
@SafeVarargs
public static <S, T extends Plugin<S>> OrderAwarePluginRegistry<T, S> of(T... plugins) {
return create(Arrays.asList(plugins), DEFAULT_COMPARATOR);
return of(Arrays.asList(plugins), DEFAULT_COMPARATOR);
}
/**
@@ -109,7 +109,7 @@ public class OrderAwarePluginRegistry<T extends Plugin<S>, S> extends SimplePlug
* @since 2.0
*/
public static <S, T extends Plugin<S>> OrderAwarePluginRegistry<T, S> of(List<? extends T> plugins) {
return create(plugins, DEFAULT_COMPARATOR);
return of(plugins, DEFAULT_COMPARATOR);
}
/**
@@ -121,7 +121,7 @@ public class OrderAwarePluginRegistry<T extends Plugin<S>, S> extends SimplePlug
* @since 2.0
*/
public static <S, T extends Plugin<S>> OrderAwarePluginRegistry<T, S> ofReverse(List<? extends T> plugins) {
return create(plugins, DEFAULT_REVERSE_COMPARATOR);
return of(plugins, DEFAULT_REVERSE_COMPARATOR);
}
/**
@@ -226,6 +226,6 @@ public class OrderAwarePluginRegistry<T extends Plugin<S>, S> extends SimplePlug
public OrderAwarePluginRegistry<T, S> reverse() {
List<T> copy = new ArrayList<>(getPlugins());
return create(copy, comparator.reversed());
return of(copy, comparator.reversed());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2008-2012 the original author or authors.
* Copyright 2008-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,15 +20,15 @@ package org.springframework.plugin.core;
* Its core responsibility is to define a delimiter type and a selection callback with the delimiter as parameter. The
* delimiter is some kind of decision object concrete plugin implementations can use to decide if they are capable to be
* executed.
*
*
* @author Oliver Gierke
*/
public interface Plugin<S> {
/**
* Returns if a plugin should be invoked according to the given delimiter.
*
* @param delimiter
*
* @param delimiter must not be {@literal null}.
* @return if the plugin should be invoked
*/
boolean supports(S delimiter);

View File

@@ -101,7 +101,7 @@ public interface PluginRegistry<T extends Plugin<S>, S> extends Iterable<T> {
* Returns the first {@link Plugin} found for the given delimiter. Thus, further configured {@link Plugin}s are
* ignored.
*
* @param delimiter
* @param delimiter must not be {@literal null}.
* @return a plugin for the given delimiter or {@link Optional#empty()} if none found.
*/
Optional<T> getPluginFor(S delimiter);
@@ -110,7 +110,7 @@ public interface PluginRegistry<T extends Plugin<S>, S> extends Iterable<T> {
* Returns the first {@link Plugin} found for the given delimiter. Thus, further configured {@link Plugin}s are
* ignored.
*
* @param delimiter
* @param delimiter must not be {@literal null}.
* @return a {@link Plugin} for the given originating system or {@link Optional#empty()} if none found.
* @throws IllegalArgumentException in case no {@link Plugin} for the given delimiter
*/
@@ -120,7 +120,7 @@ public interface PluginRegistry<T extends Plugin<S>, S> extends Iterable<T> {
* Returns the first {@link Plugin} found for the given delimiter. Thus, further configured {@link Plugin}s are
* ignored.
*
* @param delimiter
* @param delimiter must not be {@literal null}.
* @param message a {@link Supplier} to produce an exception message in case no plugin is found.
* @return a {@link Plugin} for the given originating system or {@link Optional#empty()} if none found.
* @throws IllegalArgumentException in case no {@link Plugin} for the given delimiter
@@ -130,7 +130,7 @@ public interface PluginRegistry<T extends Plugin<S>, S> extends Iterable<T> {
/**
* Returns all plugins for the given delimiter.
*
* @param delimiter
* @param delimiter must not be {@literal null}.
* @return a list of plugins or an empty list if none found
*/
List<T> getPluginsFor(S delimiter);
@@ -140,8 +140,9 @@ public interface PluginRegistry<T extends Plugin<S>, S> extends Iterable<T> {
* plugins are found the first one will be returned.
*
* @param <E> the exception type to be thrown in case no plugin can be found.
* @param delimiter
* @param ex a lazy {@link Supplier} to produce an exception in case no plugin can be found.
* @param delimiter must not be {@literal null}.
* @param ex a lazy {@link Supplier} to produce an exception in case no plugin can be found, must not be
* {@literal null}.
* @return a single plugin for the given delimiter
* @throws E if no plugin can be found for the given delimiter
*/
@@ -151,8 +152,9 @@ public interface PluginRegistry<T extends Plugin<S>, S> extends Iterable<T> {
* Retrieves all plugins for the given delimiter or throws an exception if no plugin can be found.
*
* @param <E> the exception type to be thrown.
* @param delimiter
* @param ex a lazy {@link Supplier} to produce an exception in case no plugin can be found.
* @param delimiter must not be {@literal null}.
* @param ex a lazy {@link Supplier} to produce an exception in case no plugin can be found, must not be
* {@literal null}.
* @return all plugins for the given delimiter
* @throws E if no plugin can be found
*/
@@ -161,8 +163,8 @@ public interface PluginRegistry<T extends Plugin<S>, S> extends Iterable<T> {
/**
* Returns the first {@link Plugin} supporting the given delimiter or the given plugin if none can be found.
*
* @param delimiter
* @param plugin
* @param delimiter must not be {@literal null}.
* @param plugin must not be {@literal null}.
* @return a single {@link Plugin} supporting the given delimiter or the given {@link Plugin} if none found
*/
T getPluginOrDefaultFor(S delimiter, T plugin);
@@ -181,9 +183,10 @@ public interface PluginRegistry<T extends Plugin<S>, S> extends Iterable<T> {
/**
* Returns all {@link Plugin}s supporting the given delimiter or the given plugins if none found.
*
* @param delimiter
* @param plugins
* @return all {@link Plugin}s supporting the given delimiter or the given {@link Plugin}s if none found
* @param delimiter must not be {@literal null}.
* @param plugins must not be {@literal null}.
* @return all {@link Plugin}s supporting the given delimiter or the given {@link Plugin}s if none found, will never
* be {@literal null}.
*/
List<T> getPluginsFor(S delimiter, List<? extends T> plugins);
@@ -197,7 +200,7 @@ public interface PluginRegistry<T extends Plugin<S>, S> extends Iterable<T> {
/**
* Returns whether the registry contains a given plugin.
*
* @param plugin
* @param plugin must not be {@literal null}.
* @return
*/
boolean contains(T plugin);
@@ -205,7 +208,7 @@ public interface PluginRegistry<T extends Plugin<S>, S> extends Iterable<T> {
/**
* Returns whether the registry contains a {@link Plugin} matching the given delimiter.
*
* @param delimiter
* @param delimiter must not be {@literal null}.
* @return
*/
boolean hasPluginFor(S delimiter);
@@ -214,7 +217,7 @@ public interface PluginRegistry<T extends Plugin<S>, S> extends Iterable<T> {
* Returns all {@link Plugin}s contained in this registry. Will return an immutable {@link List} to prevent outside
* modifications of the {@link PluginRegistry} content.
*
* @return
* @return will never be {@literal null}.
*/
List<T> getPlugins();
}

View File

@@ -18,6 +18,7 @@ package org.springframework.plugin.core;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.util.Assert;
@@ -73,15 +74,10 @@ public abstract class PluginRegistrySupport<T extends Plugin<S>, S> implements P
protected synchronized List<T> initialize(List<T> plugins) {
Assert.notNull(plugins, "Plugins must not be null!");
List<T> result = new ArrayList<T>();
for (T plugin : this.plugins) {
if (plugin != null) {
result.add(plugin);
}
}
return result;
return plugins.stream() //
.filter(it -> it != null) //
.collect(Collectors.toList());
}
/*

View File

@@ -78,7 +78,7 @@ public class SimplePluginRegistry<T extends Plugin<S>, S> extends PluginRegistry
*/
@Deprecated
public static <S, T extends Plugin<S>> SimplePluginRegistry<T, S> create() {
return create(Collections.<T> emptyList());
return of(Collections.<T> emptyList());
}
/**
@@ -107,6 +107,8 @@ public class SimplePluginRegistry<T extends Plugin<S>, S> extends PluginRegistry
@Override
public Optional<T> getPluginFor(S delimiter) {
Assert.notNull(delimiter, "Delimiter must not be null!");
return super.getPlugins().stream()//
.filter(it -> it.supports(delimiter))//
.findFirst();
@@ -119,6 +121,8 @@ public class SimplePluginRegistry<T extends Plugin<S>, S> extends PluginRegistry
@Override
public T getRequiredPluginFor(S delimiter) {
Assert.notNull(delimiter, "Delimiter must not be null!");
return getRequiredPluginFor(delimiter,
() -> String.format("No plugin found for delimiter %s! Registered plugins: %s.", delimiter, getPlugins()));
}
@@ -130,6 +134,7 @@ public class SimplePluginRegistry<T extends Plugin<S>, S> extends PluginRegistry
@Override
public T getRequiredPluginFor(S delimiter, Supplier<String> message) throws IllegalArgumentException {
Assert.notNull(delimiter, "Delimiter must not be null!");
Assert.notNull(message, "Message must not be null!");
return getPluginFor(delimiter, () -> new IllegalArgumentException(message.get()));
@@ -142,6 +147,8 @@ public class SimplePluginRegistry<T extends Plugin<S>, S> extends PluginRegistry
@Override
public List<T> getPluginsFor(S delimiter) {
Assert.notNull(delimiter, "Delimiter must not be null!");
return super.getPlugins().stream()//
.filter(it -> it.supports(delimiter))//
.collect(Collectors.toList());
@@ -153,6 +160,10 @@ public class SimplePluginRegistry<T extends Plugin<S>, S> extends PluginRegistry
*/
@Override
public <E extends Exception> T getPluginFor(S delimiter, Supplier<E> ex) throws E {
Assert.notNull(delimiter, "Delimiter must not be null!");
Assert.notNull(ex, "Exception supplier must not be null!");
return getPluginFor(delimiter).orElseThrow(ex);
}
@@ -163,6 +174,9 @@ public class SimplePluginRegistry<T extends Plugin<S>, S> extends PluginRegistry
@Override
public <E extends Exception> List<T> getPluginsFor(S delimiter, Supplier<E> ex) throws E {
Assert.notNull(delimiter, "Delimiter must not be null!");
Assert.notNull(ex, "Exception supplier must not be null!");
List<T> result = getPluginsFor(delimiter);
if (result.isEmpty()) {
@@ -187,6 +201,10 @@ public class SimplePluginRegistry<T extends Plugin<S>, S> extends PluginRegistry
*/
@Override
public T getPluginOrDefaultFor(S delimiter, Supplier<T> defaultSupplier) {
Assert.notNull(delimiter, "Delimiter must not be null!");
Assert.notNull(defaultSupplier, "Default supplier must not be null!");
return getPluginFor(delimiter).orElseGet(defaultSupplier);
}
@@ -197,6 +215,9 @@ public class SimplePluginRegistry<T extends Plugin<S>, S> extends PluginRegistry
@Override
public List<T> getPluginsFor(S delimiter, List<? extends T> plugins) {
Assert.notNull(delimiter, "Delimiter must not be null!");
Assert.notNull(plugins, "Plugins must not be null!");
List<T> candidates = getPluginsFor(delimiter);
return candidates.isEmpty() ? new ArrayList<T>(plugins) : candidates;

View File

@@ -15,6 +15,10 @@
*/
package org.springframework.plugin.core.config;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -39,15 +43,24 @@ import org.springframework.util.StringUtils;
*/
public class PluginRegistriesBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/*
private static final Logger LOG = LoggerFactory.getLogger(PluginRegistriesBeanDefinitionRegistrar.class);
/*
* (non-Javadoc)
* @see org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Class<?>[] types = (Class<?>[]) importingClassMetadata
.getAnnotationAttributes(EnablePluginRegistries.class.getName()).get("value");
Map<String, Object> annotationAttributes = importingClassMetadata
.getAnnotationAttributes(EnablePluginRegistries.class.getName());
if (annotationAttributes == null) {
LOG.info("No EnablePluginRegistries annotation found on type {}!", importingClassMetadata.getClassName());
return;
}
Class<?>[] types = (Class<?>[]) annotationAttributes.get("value");
for (Class<?> type : types) {
@@ -67,8 +80,10 @@ public class PluginRegistriesBeanDefinitionRegistrar implements ImportBeanDefini
}
// Default
String beanName = annotation == null ? StringUtils.uncapitalize(type.getSimpleName() + "Registry")
String beanName = annotation == null //
? StringUtils.uncapitalize(type.getSimpleName() + "Registry") //
: annotation.value();
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
}

View File

@@ -1,5 +1,6 @@
/**
* This package contains the core plugin API. It allows other modules implementing components that extend functionality defined by a plugin interface. Plugin clients can be equipped with plugin implementations.
* This package contains the core plugin API. It allows other modules implementing components that extend functionality
* defined by a plugin interface. Plugin clients can be equipped with plugin implementations.
*/
@org.springframework.lang.NonNullApi
package org.springframework.plugin.core;

View File

@@ -20,6 +20,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.ProxyFactory;
@@ -30,6 +31,8 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -41,10 +44,10 @@ import org.springframework.util.Assert;
public abstract class AbstractTypeAwareSupport<T>
implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, InitializingBean {
private ApplicationContext context;
private Class<T> type;
private BeansOfTypeTargetSource targetSource;
private Collection<Class<?>> exclusions;
private @Nullable ApplicationContext context;
private @Nullable Class<T> type;
private @Nullable BeansOfTypeTargetSource targetSource;
private Collection<Class<?>> exclusions = Collections.emptySet();
/*
* (non-Javadoc)
@@ -80,7 +83,14 @@ public abstract class AbstractTypeAwareSupport<T>
@SuppressWarnings("unchecked")
protected List<T> getBeans() {
TargetSource targetSource = this.targetSource;
if (targetSource == null) {
throw new IllegalStateException("Traget source not initialized!");
}
ProxyFactory factory = new ProxyFactory(List.class, targetSource);
return (List<T>) factory.getProxy();
}
@@ -89,6 +99,19 @@ public abstract class AbstractTypeAwareSupport<T>
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() {
ApplicationContext context = this.context;
if (context == null) {
throw new IllegalStateException("ApplicationContext not set!");
}
Class<?> type = this.type;
if (type == null) {
throw new IllegalStateException("No type configured!");
}
this.targetSource = new BeansOfTypeTargetSource(context, type, false, exclusions);
}
@@ -98,7 +121,7 @@ public abstract class AbstractTypeAwareSupport<T>
*/
public void onApplicationEvent(ContextRefreshedEvent event) {
if (context.equals(event.getApplicationContext())) {
if (event.getApplicationContext().equals(context) && targetSource != null) {
targetSource.freeze();
}
}
@@ -119,7 +142,7 @@ public abstract class AbstractTypeAwareSupport<T>
private final Collection<Class<?>> exclusions;
private boolean frozen = false;
private Collection<Object> components;
private @Nullable Collection<Object> components;
/**
* Creates a new {@link BeansOfTypeTargetSource} using the given {@link ListableBeanFactory} to lookup beans of the
@@ -134,11 +157,13 @@ public abstract class AbstractTypeAwareSupport<T>
Assert.notNull(context, "ListableBeanFactory must not be null!");
Assert.notNull(type, "Type must not be null!");
Assert.notNull(exclusions, "Exclusions must not be null!");
this.context = context;
this.type = type;
this.eagerInit = eagerInit;
this.exclusions = exclusions == null ? Collections.<Class<?>> emptySet() : exclusions;
this.exclusions = exclusions;
this.components = null;
}
/**
@@ -153,6 +178,7 @@ public abstract class AbstractTypeAwareSupport<T>
* (non-Javadoc)
* @see org.springframework.aop.TargetSource#getTargetClass()
*/
@NonNull
public Class<?> getTargetClass() {
return List.class;
}
@@ -169,10 +195,12 @@ public abstract class AbstractTypeAwareSupport<T>
* (non-Javadoc)
* @see org.springframework.aop.TargetSource#getTarget()
*/
@NonNull
@SuppressWarnings({ "rawtypes", "unchecked" })
public synchronized Object getTarget() throws Exception {
Collection<Object> components = this.components == null ? getBeansOfTypeExcept(type, exclusions)
Collection<Object> components = this.components == null //
? getBeansOfTypeExcept(type, exclusions) //
: this.components;
if (frozen && this.components == null) {
@@ -186,22 +214,14 @@ public abstract class AbstractTypeAwareSupport<T>
* (non-Javadoc)
* @see org.springframework.aop.TargetSource#releaseTarget(java.lang.Object)
*/
public void releaseTarget(Object target) throws Exception {
}
public void releaseTarget(Object target) throws Exception {}
private Collection<Object> getBeansOfTypeExcept(Class<?> type, Collection<Class<?>> exceptions) {
List<Object> result = new ArrayList<Object>();
for (String beanName : context.getBeanNamesForType(type, false, eagerInit)) {
if (exceptions.contains(context.getType(beanName))) {
continue;
}
result.add(context.getBean(beanName));
}
return result;
return Arrays.stream(context.getBeanNamesForType(type, false, eagerInit)) //
.filter(it -> !exceptions.contains(context.getType(it))) //
.map(it -> context.getBean(it)) //
.collect(Collectors.toList());
}
}
}

View File

@@ -22,6 +22,7 @@ import java.util.List;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.lang.NonNull;
/**
* Factory to create bean lists for a given type. Exposes all beans of the configured type that can be found in the
@@ -37,6 +38,7 @@ public class BeanListFactoryBean<T> extends AbstractTypeAwareSupport<T> implemen
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
@NonNull
public List<T> getObject() {
List<T> beans = new ArrayList<T>();
@@ -50,6 +52,7 @@ public class BeanListFactoryBean<T> extends AbstractTypeAwareSupport<T> implemen
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
@NonNull
public Class<?> getObjectType() {
return List.class;
}

View File

@@ -16,6 +16,7 @@
package org.springframework.plugin.core.support;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.lang.NonNull;
import org.springframework.plugin.core.OrderAwarePluginRegistry;
import org.springframework.plugin.core.Plugin;
import org.springframework.plugin.core.PluginRegistry;
@@ -32,6 +33,7 @@ public class PluginRegistryFactoryBean<T extends Plugin<S>, S> extends AbstractT
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
@NonNull
public OrderAwarePluginRegistry<T, S> getObject() {
return OrderAwarePluginRegistry.of(getBeans());
}
@@ -40,6 +42,7 @@ public class PluginRegistryFactoryBean<T extends Plugin<S>, S> extends AbstractT
* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
@NonNull
public Class<?> getObjectType() {
return OrderAwarePluginRegistry.class;
}

View File

@@ -1,5 +1,6 @@
/**
* This package contains support classes to create bean lists or plugin registry instances out of beans implementing a certain interface.
* This package contains support classes to create bean lists or plugin registry instances out of beans implementing a
* certain interface.
*/
@org.springframework.lang.NonNullApi
package org.springframework.plugin.core.support;

View File

@@ -100,7 +100,7 @@ public class OrderAwarePluginRegistryUnitTest extends SimplePluginRegistryUnitTe
private static void assertOrder(PluginRegistry<TestPlugin, String> registry, TestPlugin... plugins) {
List<TestPlugin> result = registry.getPluginsFor(null);
List<TestPlugin> result = registry.getPluginsFor("delimiter");
assertThat(plugins.length, is(result.size()));
@@ -108,7 +108,7 @@ public class OrderAwarePluginRegistryUnitTest extends SimplePluginRegistryUnitTe
assertThat(result.get(i), is(plugins[i]));
}
assertThat(registry.getPluginFor(null), is(Optional.of(plugins[0])));
assertThat(registry.getPluginFor("delimiter"), is(Optional.of(plugins[0])));
}
private static void assertDefaultComparator(OrderAwarePluginRegistry<?, ?> registry) {