Migrate to JSpecify annotations for nullability constraints.

Closes #528
This commit is contained in:
Mark Paluch
2025-03-05 11:04:35 +01:00
parent 2581bfb6c5
commit 5b82cbcddb
36 changed files with 89 additions and 53 deletions

View File

@@ -95,6 +95,11 @@
<version>${cdi}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>

View File

@@ -1,7 +1,5 @@
/**
* XML configuration support for Spring Data LDAP repositories.
*/
@NonNullApi
@org.jspecify.annotations.NullMarked
package org.springframework.data.ldap.config;
import org.springframework.lang.NonNullApi;

View File

@@ -35,4 +35,5 @@ public class BasicLdapPersistentEntity<T> extends BasicPersistentEntity<T, LdapP
public BasicLdapPersistentEntity(TypeInformation<T> information) {
super(information);
}
}

View File

@@ -45,4 +45,5 @@ public class LdapMappingContext extends AbstractMappingContext<BasicLdapPersiste
SimpleTypeHolder simpleTypeHolder) {
return new LdapPersistentProperty(property, owner, simpleTypeHolder);
}
}

View File

@@ -48,11 +48,12 @@ public class LdapPersistentProperty extends AnnotationBasedPersistentProperty<Ld
@Override
protected Association<LdapPersistentProperty> createAssociation() {
return null;
throw new UnsupportedOperationException("LDAP does not support associations");
}
@Override
public boolean isIdProperty() {
return isId.get();
}
}

View File

@@ -44,4 +44,5 @@ public abstract class LdapSimpleTypes {
public static final SimpleTypeHolder HOLDER = new SimpleTypeHolder(VAULT_SIMPLE_TYPES, true);
private LdapSimpleTypes() {}
}

View File

@@ -1,6 +1,5 @@
/**
* Infrastructure for the LDAP object mapping subsystem.
*/
@org.springframework.lang.NonNullApi
@org.springframework.lang.NonNullFields
@org.jspecify.annotations.NullMarked
package org.springframework.data.ldap.core.mapping;

View File

@@ -49,6 +49,7 @@ public interface LdapEncoder {
public String encode(String value) {
return org.springframework.ldap.support.LdapEncoder.nameEncode(value);
}
}
/**
@@ -79,6 +80,7 @@ public interface LdapEncoder {
return buff.toString();
}
}
}

View File

@@ -48,4 +48,5 @@ public interface LdapRepository<T> extends ListCrudRepository<T, Name> {
* @return the entries matching the query.
*/
List<T> findAll(LdapQuery ldapQuery);
}

View File

@@ -75,4 +75,5 @@ public @interface Query {
* @return the count limit.
*/
int countLimit() default 0;
}

View File

@@ -70,4 +70,5 @@ public class LdapRepositoryBean<T> extends CdiRepositoryBean<T> {
public Class<? extends Annotation> getScope() {
return operations.getScope();
}
}

View File

@@ -115,4 +115,5 @@ public class LdapRepositoryExtension extends CdiRepositoryExtensionSupport {
return new LdapRepositoryBean<>(LdapOperations, qualifiers, repositoryType, beanManager,
Optional.of(getCustomImplementationDetector()));
}
}

View File

@@ -1,5 +1,5 @@
/**
* CDI support for LDAP specific repository implementation.
*/
@org.springframework.lang.NonNullApi
@org.jspecify.annotations.NullMarked
package org.springframework.data.ldap.repository.cdi;

View File

@@ -36,4 +36,5 @@ class LdapRepositoriesRegistrar extends RepositoryBeanDefinitionRegistrarSupport
protected RepositoryConfigurationExtension getExtension() {
return new LdapRepositoryConfigurationExtension();
}
}

View File

@@ -112,4 +112,5 @@ public class LdapRepositoryConfigurationExtension extends RepositoryConfiguratio
protected boolean useRepositoryConfiguration(RepositoryMetadata metadata) {
return !metadata.isReactiveRepository();
}
}

View File

@@ -1,7 +1,5 @@
/**
* Support infrastructure for the configuration of LDAP specific repositories.
*/
@NonNullApi
@org.jspecify.annotations.NullMarked
package org.springframework.data.ldap.repository.config;
import org.springframework.lang.NonNullApi;

View File

@@ -1,5 +1,5 @@
/**
* LDAP specific repository implementation.
*/
@org.springframework.lang.NonNullApi
@org.jspecify.annotations.NullMarked
package org.springframework.data.ldap.repository;

View File

@@ -17,6 +17,8 @@ package org.springframework.data.ldap.repository.query;
import static org.springframework.data.ldap.repository.query.LdapQueryExecution.*;
import org.jspecify.annotations.Nullable;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.ldap.repository.Query;
import org.springframework.data.mapping.PersistentEntity;
@@ -71,7 +73,7 @@ public abstract class AbstractLdapRepositoryQuery implements RepositoryQuery {
@Override
@SuppressWarnings("ConstantConditions")
public final Object execute(Object[] parameters) {
public final @Nullable Object execute(Object[] parameters) {
LdapParametersParameterAccessor parameterAccessor = new LdapParametersParameterAccessor(queryMethod, parameters);
LdapQuery query = createQuery(parameterAccessor);
@@ -117,4 +119,5 @@ public abstract class AbstractLdapRepositoryQuery implements RepositoryQuery {
public final QueryMethod getQueryMethod() {
return queryMethod;
}
}

View File

@@ -31,4 +31,5 @@ public interface LdapParameterAccessor extends ParameterAccessor {
* @return
*/
Object[] getBindableParameterValues();
}

View File

@@ -18,6 +18,8 @@ package org.springframework.data.ldap.repository.query;
import java.lang.reflect.Method;
import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.BeanUtils;
import org.springframework.core.MethodParameter;
import org.springframework.data.geo.Distance;
@@ -27,7 +29,6 @@ import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.ParametersSource;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
/**
* Custom extension of {@link Parameters} discovering additional

View File

@@ -15,11 +15,11 @@
*/
package org.springframework.data.ldap.repository.query;
import static org.springframework.ldap.query.LdapQueryBuilder.*;
import java.util.Iterator;
import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.PropertyPath;
@@ -73,7 +73,7 @@ class LdapQueryCreator extends AbstractQueryCreator<LdapQuery, ContainerCriteria
Entry entry = AnnotatedElementUtils.findMergedAnnotation(entityType, Entry.class);
LdapQueryBuilder query = query();
LdapQueryBuilder query = LdapQueryBuilder.query();
if (entry != null) {
query = query.base(entry.base());
@@ -148,7 +148,8 @@ class LdapQueryCreator extends AbstractQueryCreator<LdapQuery, ContainerCriteria
}
@Override
protected LdapQuery complete(ContainerCriteria criteria, Sort sort) {
return criteria;
protected LdapQuery complete(@Nullable ContainerCriteria criteria, Sort sort) {
return criteria == null ? LdapQueryBuilder.query() : criteria;
}
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.ldap.repository.query;
import org.jspecify.annotations.Nullable;
import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.convert.DtoInstantiatingConverter;
@@ -39,6 +41,7 @@ import org.springframework.util.ClassUtils;
@FunctionalInterface
interface LdapQueryExecution {
@Nullable
Object execute(LdapQuery query);
/**
@@ -57,13 +60,14 @@ interface LdapQueryExecution {
}
@Override
public Object execute(LdapQuery query) {
public @Nullable Object execute(LdapQuery query) {
try {
return operations.findOne(query, entityType);
} catch (EmptyResultDataAccessException e) {
return null;
}
}
}
/**
@@ -85,6 +89,7 @@ interface LdapQueryExecution {
public Object execute(LdapQuery query) {
return operations.find(query, entityType);
}
}
/**
@@ -108,6 +113,7 @@ interface LdapQueryExecution {
public Object execute(LdapQuery query) {
return operations.find(query, entityType).stream().map(resultProcessing::convert);
}
}
/**
@@ -124,9 +130,12 @@ interface LdapQueryExecution {
}
@Override
public Object execute(LdapQuery query) {
return converter.convert(delegate.execute(query));
public @Nullable Object execute(LdapQuery query) {
Object result = delegate.execute(query);
return result != null ? converter.convert(result) : null;
}
}
/**
@@ -149,7 +158,7 @@ interface LdapQueryExecution {
}
@Override
public Object convert(Object source) {
public @Nullable Object convert(@Nullable Object source) {
ReturnedType returnedType = processor.getReturnedType();
@@ -165,5 +174,7 @@ interface LdapQueryExecution {
return processor.processResult(source, converter);
}
}
}

View File

@@ -17,12 +17,13 @@ package org.springframework.data.ldap.repository.query;
import java.lang.reflect.Method;
import org.jspecify.annotations.Nullable;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.ldap.repository.Query;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.lang.Nullable;
/**
* QueryMethod for Ldap Queries.

View File

@@ -74,4 +74,5 @@ public class PartTreeLdapRepositoryQuery extends AbstractLdapRepositoryQuery {
getEntityClass(), objectDirectoryMapper, parameters, inputProperties);
return queryCreator.createQuery();
}
}

View File

@@ -27,6 +27,8 @@ import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jspecify.annotations.Nullable;
import org.springframework.data.expression.ValueExpression;
import org.springframework.data.expression.ValueExpressionParser;
import org.springframework.data.repository.query.Parameter;
@@ -34,7 +36,6 @@ import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.ValueExpressionDelegate;
import org.springframework.data.spel.ExpressionDependencies;
import org.springframework.lang.Nullable;
import org.springframework.ldap.support.LdapEncoder;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -245,6 +246,7 @@ class StringBasedQuery {
return (matcherMap.isEmpty() ? null : matcherMap.values().iterator().next());
}
}
/**
@@ -288,6 +290,7 @@ class StringBasedQuery {
return result.append(input.subSequence(currentPosition, input.length())).toString();
}
}
/**
@@ -349,8 +352,7 @@ class StringBasedQuery {
* @param binding must not be {@literal null}.
* @return the value used for the given {@link ParameterBinding}.
*/
@Nullable
private Object getParameterValueForBinding(ParameterBinding binding) {
private @Nullable Object getParameterValueForBinding(ParameterBinding binding) {
if (binding.isExpression()) {
return evaluator.apply(binding.getRequiredExpression());
@@ -439,6 +441,9 @@ class StringBasedQuery {
return parameterName;
}
}
}
}

View File

@@ -1,7 +1,5 @@
/**
* Query derivation mechanism for LDAP specific repositories.
*/
@NonNullApi
@org.jspecify.annotations.NullMarked
package org.springframework.data.ldap.repository.query;
import org.springframework.lang.NonNullApi;

View File

@@ -22,7 +22,8 @@ import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.VariableElement;
import org.springframework.lang.Nullable;
import org.jspecify.annotations.Nullable;
import org.springframework.ldap.odm.annotations.Id;
import com.querydsl.apt.DefaultConfiguration;
@@ -55,4 +56,5 @@ class DefaultLdapAnnotationProcessorConfiguration extends DefaultConfiguration {
public boolean isValidField(VariableElement field) {
return super.isValidField(field) && field.getAnnotation(Id.class) == null;
}
}

View File

@@ -53,4 +53,5 @@ public class LdapAnnotationProcessor extends AbstractQuerydslProcessor {
return configuration;
}
}

View File

@@ -18,8 +18,9 @@ package org.springframework.data.ldap.repository.support;
import javax.naming.Name;
import org.jspecify.annotations.Nullable;
import org.springframework.data.repository.core.support.AbstractEntityInformation;
import org.springframework.lang.Nullable;
import org.springframework.ldap.odm.core.ObjectDirectoryMapper;
/**
@@ -39,9 +40,8 @@ class LdapEntityInformation<T> extends AbstractEntityInformation<T, Name> {
this.odm = odm;
}
@Nullable
@Override
public Name getId(T entity) {
public @Nullable Name getId(T entity) {
return odm.getId(entity);
}
@@ -49,4 +49,5 @@ class LdapEntityInformation<T> extends AbstractEntityInformation<T, Name> {
public Class<Name> getIdType() {
return Name.class;
}
}

View File

@@ -22,6 +22,8 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Optional;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.ldap.core.mapping.LdapMappingContext;
import org.springframework.data.ldap.repository.query.AnnotatedLdapRepositoryQuery;
@@ -149,7 +151,7 @@ public class LdapRepositoryFactory extends RepositoryFactorySupport {
}
@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key,
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key,
ValueExpressionDelegate valueExpressionDelegate) {
return Optional
.of(new LdapQueryLookupStrategy(ldapOperations, instantiators, mappingContext, valueExpressionDelegate));
@@ -199,5 +201,7 @@ public class LdapRepositoryFactory extends RepositoryFactorySupport {
return new PartTreeLdapRepositoryQuery(queryMethod, domainType, ldapOperations, mappingContext, instantiators);
}
}
}
}

View File

@@ -17,6 +17,8 @@ package org.springframework.data.ldap.repository.support;
import javax.naming.Name;
import org.jspecify.annotations.Nullable;
import org.springframework.data.ldap.core.mapping.LdapMappingContext;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
@@ -24,7 +26,6 @@ import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.lang.Nullable;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.util.Assert;
@@ -87,4 +88,5 @@ public class LdapRepositoryFactoryBean<T extends Repository<S, Name>, S>
setMappingContext(new LdapMappingContext());
}
}
}

View File

@@ -126,4 +126,5 @@ class LdapSerializer implements Visitor<Object, Void> {
public Object visit(TemplateExpression<?> expr, Void context) {
throw new UnsupportedOperationException();
}
}

View File

@@ -26,6 +26,8 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.convert.DtoInstantiatingConverter;
import org.springframework.data.domain.Example;
@@ -43,7 +45,6 @@ import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.lang.Nullable;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.util.Assert;
@@ -247,9 +248,8 @@ public class QuerydslLdapPredicateExecutor<T> implements ListQuerydslPredicateEx
return new FluentQuerydsl<>(predicate, sort, resultType, new ArrayList<>(properties));
}
@Nullable
@Override
public R oneValue() {
public @Nullable R oneValue() {
List<T> results = findTop(2);
@@ -265,9 +265,8 @@ public class QuerydslLdapPredicateExecutor<T> implements ListQuerydslPredicateEx
return getConversionFunction().apply(one);
}
@Nullable
@Override
public R firstValue() {
public @Nullable R firstValue() {
List<T> results = findTop(2);

View File

@@ -133,4 +133,5 @@ public class QuerydslLdapQuery<K> implements FilteredClause<QuerydslLdapQuery<K>
return where != null ? builder.filter(filterGenerator.handle(where)) : builder.filter(new AbsoluteTrueFilter());
}
}

View File

@@ -24,6 +24,8 @@ import java.util.stream.StreamSupport;
import javax.naming.Name;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.domain.Persistable;
import org.springframework.data.ldap.repository.LdapRepository;
@@ -31,7 +33,6 @@ import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.util.Optionals;
import org.springframework.lang.Nullable;
import org.springframework.ldap.NameNotFoundException;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.ldap.core.support.CountNameClassPairCallbackHandler;
@@ -227,16 +228,8 @@ public class SimpleLdapRepository<T> implements LdapRepository<T> {
return ldapOperations.find(ldapQuery, entityType);
}
private <S extends T> boolean isNew(S entity, @Nullable Name id) {
if (entity instanceof Persistable) {
Persistable<?> persistable = (Persistable<?>) entity;
return persistable.isNew();
} else {
return id == null;
}
return entity instanceof Persistable<?> p ? p.isNew() : id == null;
}
}

View File

@@ -1,7 +1,5 @@
/**
* Support infrastructure for query derivation of LDAP specific repositories.
*/
@NonNullApi
@org.jspecify.annotations.NullMarked
package org.springframework.data.ldap.repository.support;
import org.springframework.lang.NonNullApi;