DATALDAP-45 - Introduced usage of nullable annotations for API validation.

Mark all packages with Spring Frameworks @NonNullApi. Add Spring's @Nullable to methods, parameters and fields that take or produce null values. Adapt using code to make sure the IDE can evaluate the null flow properly. Introduce methods that return required (non-null) values.
This commit is contained in:
Mark Paluch
2017-07-28 10:46:12 +02:00
parent 1a10d2ffa1
commit b3f1bbc882
19 changed files with 197 additions and 59 deletions

View File

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

View File

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

View File

@@ -38,7 +38,7 @@ public abstract class AbstractLdapRepositoryQuery implements RepositoryQuery {
/**
* Creates a new {@link AbstractLdapRepositoryQuery} instance given {@link LdapQuery}, {@link Class} and
* {@link LdapOperations}.
*
*
* @param queryMethod must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param ldapOperations must not be {@literal null}.
@@ -58,6 +58,7 @@ public abstract class AbstractLdapRepositoryQuery implements RepositoryQuery {
* @see org.springframework.data.repository.query.RepositoryQuery#execute(java.lang.Object[])
*/
@Override
@SuppressWarnings("ConstantConditions")
public final Object execute(Object[] parameters) {
LdapQuery query = createQuery(parameters);
@@ -75,7 +76,7 @@ public abstract class AbstractLdapRepositoryQuery implements RepositoryQuery {
/**
* Creates a {@link Query} instance using the given {@literal parameters}.
*
*
* @param parameters must not be {@literal null}.
* @return
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2017 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.
@@ -26,6 +26,7 @@ import org.springframework.util.Assert;
* Handles queries for repository methods annotated with {@link org.springframework.data.ldap.repository.Query}.
*
* @author Mattias Hellborg Arthursson
* @author Mark Paluch
*/
public class AnnotatedLdapRepositoryQuery extends AbstractLdapRepositoryQuery {
@@ -33,7 +34,7 @@ public class AnnotatedLdapRepositoryQuery extends AbstractLdapRepositoryQuery {
/**
* Construct a new instance.
*
*
* @param queryMethod the QueryMethod.
* @param entityType the managed class.
* @param ldapOperations the LdapOperations instance to use.
@@ -45,7 +46,7 @@ public class AnnotatedLdapRepositoryQuery extends AbstractLdapRepositoryQuery {
Assert.notNull(queryMethod.getQueryAnnotation(), "Annotation must be present");
Assert.hasLength(queryMethod.getQueryAnnotation().value(), "Query filter must be specified");
queryAnnotation = queryMethod.getQueryAnnotation();
queryAnnotation = queryMethod.getRequiredQueryAnnotation();
}
/* (non-Javadoc)

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2017 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.
@@ -19,6 +19,7 @@ import static org.springframework.ldap.query.LdapQueryBuilder.*;
import java.util.Iterator;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.repository.query.Parameters;
@@ -31,6 +32,7 @@ import org.springframework.ldap.odm.core.ObjectDirectoryMapper;
import org.springframework.ldap.query.ConditionCriteria;
import org.springframework.ldap.query.ContainerCriteria;
import org.springframework.ldap.query.LdapQuery;
import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.util.Assert;
/**
@@ -71,8 +73,15 @@ class LdapQueryCreator extends AbstractQueryCreator<LdapQuery, ContainerCriteria
@Override
protected ContainerCriteria create(Part part, Iterator<Object> iterator) {
String base = entityType.getAnnotation(Entry.class).base();
ConditionCriteria criteria = query().base(base).where(getAttribute(part));
Entry entry = AnnotatedElementUtils.findMergedAnnotation(entityType, Entry.class);
LdapQueryBuilder query = query();
if (entry != null) {
query = query.base(entry.base());
}
ConditionCriteria criteria = query.where(getAttribute(part));
return appendCondition(part, iterator, criteria);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2017 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.
@@ -22,12 +22,14 @@ 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.
*
* @author Mattias Hellborg Arthursson
* @author Eddu Melendez
* @author Mark Paluch
*/
public class LdapQueryMethod extends QueryMethod {
@@ -48,7 +50,7 @@ public class LdapQueryMethod extends QueryMethod {
/**
* Check whether the target method is annotated with {@link org.springframework.data.ldap.repository.Query}.
*
*
* @return <code>true</code> if the target method is annotated with
* {@link org.springframework.data.ldap.repository.Query}, <code>false</code> otherwise.
*/
@@ -58,11 +60,29 @@ public class LdapQueryMethod extends QueryMethod {
/**
* Get the {@link org.springframework.data.ldap.repository.Query} annotation of the target method (if any).
*
*
* @return the {@link org.springframework.data.ldap.repository.Query} annotation of the target method if present, or
* <code>null</code> otherwise.
*/
@Nullable
Query getQueryAnnotation() {
return AnnotationUtils.getAnnotation(method, Query.class);
}
/**
* Get the required {@link org.springframework.data.ldap.repository.Query} annotation of the target method.
*
* @return the {@link org.springframework.data.ldap.repository.Query} annotation of the target method if present, or
* {@link IllegalStateException} otherwise.
*/
Query getRequiredQueryAnnotation() {
Query queryAnnotation = getQueryAnnotation();
if (queryAnnotation != null) {
return queryAnnotation;
}
throw new IllegalStateException("Required @Query annotation is not present!");
}
}

View File

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

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2017 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.
@@ -22,22 +22,24 @@ import java.util.Map;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.VariableElement;
import org.springframework.lang.Nullable;
import org.springframework.ldap.odm.annotations.Id;
import com.querydsl.apt.DefaultConfiguration;
/**
* Configuration for {@link LdapAnnotationProcessor}.
*
*
* @author Mattias Hellborg Arthursson
* @author Eddu Melendez
* @author Mark Paluch
*/
class DefaultLdapAnnotationProcessorConfiguration extends DefaultConfiguration {
public DefaultLdapAnnotationProcessorConfiguration(RoundEnvironment roundEnv, Map<String, String> options,
Collection<String> keywords, Class<? extends Annotation> entitiesAnn, Class<? extends Annotation> entityAnn,
Class<? extends Annotation> superTypeAnn, Class<? extends Annotation> embeddableAnn,
Class<? extends Annotation> embeddedAnn, Class<? extends Annotation> skipAnn) {
@Nullable Class<? extends Annotation> superTypeAnn, @Nullable Class<? extends Annotation> embeddableAnn,
@Nullable Class<? extends Annotation> embeddedAnn, Class<? extends Annotation> skipAnn) {
super(roundEnv, options, keywords, entitiesAnn, entityAnn, superTypeAnn, embeddableAnn, embeddedAnn, skipAnn);
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2017 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.ldap.repository.support;
import javax.naming.Name;
import org.springframework.data.repository.core.support.AbstractEntityInformation;
import org.springframework.lang.Nullable;
import org.springframework.ldap.odm.core.impl.DefaultObjectDirectoryMapper;
/**
* ODM-based {@link org.springframework.data.repository.core.EntityInformation} for LDAP entities.
*
* @author Mark Paluch
* @since 2.0
* @see org.springframework.ldap.odm.core.ObjectDirectoryMapper
* @see org.springframework.ldap.odm.annotations.Entry
*/
class LdapEntityInformation<T> extends AbstractEntityInformation<T, Name> {
private final DefaultObjectDirectoryMapper MAPPER = new DefaultObjectDirectoryMapper();
public LdapEntityInformation(Class<T> domainClass) {
super(domainClass);
}
@Nullable
@Override
public Name getId(T entity) {
return MAPPER.getId(entity);
}
@Override
public Class<Name> getIdType() {
return Name.class;
}
}

View File

@@ -17,7 +17,6 @@ package org.springframework.data.ldap.repository.support;
import static org.springframework.data.querydsl.QuerydslUtils.*;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Optional;
@@ -35,6 +34,7 @@ import org.springframework.data.repository.query.EvaluationContextProvider;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.lang.Nullable;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.util.Assert;
@@ -68,8 +68,9 @@ public class LdapRepositoryFactory extends RepositoryFactorySupport {
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class)
*/
@Override
@SuppressWarnings("unchecked")
public <T, ID> EntityInformation<T, ID> getEntityInformation(Class<T> domainClass) {
return null;
return new LdapEntityInformation(domainClass);
}
/*
@@ -99,7 +100,7 @@ public class LdapRepositoryFactory extends RepositoryFactorySupport {
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(org.springframework.data.repository.query.QueryLookupStrategy.Key, org.springframework.data.repository.query.EvaluationContextProvider)
*/
@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key,
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key,
EvaluationContextProvider evaluationContextProvider) {
return Optional.of(queryLookupStrategy);
}
@@ -109,9 +110,9 @@ public class LdapRepositoryFactory extends RepositoryFactorySupport {
private LdapOperations ldapOperations;
/**
* @param ldapOperations
* @param ldapOperations must not be {@literal null}.
*/
public LdapQueryLookupStrategy(LdapOperations ldapOperations) {
LdapQueryLookupStrategy(LdapOperations ldapOperations) {
this.ldapOperations = ldapOperations;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2017 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,6 +20,7 @@ import javax.naming.Name;
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;
@@ -29,15 +30,16 @@ import org.springframework.util.Assert;
*
* @author Mattias Hellborg Arthursson
* @author Oliver Gierke
* @author Mark Paluch
*/
public class LdapRepositoryFactoryBean<T extends Repository<S, Name>, S>
extends RepositoryFactoryBeanSupport<T, S, Name> {
private LdapOperations ldapOperations;
private @Nullable LdapOperations ldapOperations;
/**
* Creates a new {@link LdapRepositoryFactoryBean} for the given repository interface.
*
*
* @param repositoryInterface must not be {@literal null}.
*/
public LdapRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
@@ -54,6 +56,9 @@ public class LdapRepositoryFactoryBean<T extends Repository<S, Name>, S>
*/
@Override
protected RepositoryFactorySupport createRepositoryFactory() {
Assert.state(ldapOperations != null, "LdapOperations must be set");
return new LdapRepositoryFactory(ldapOperations);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2017 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.
@@ -41,7 +41,7 @@ class LdapSerializer implements Visitor<Object, Void> {
/**
* Creates a new {@link LdapSerializer}.
*
*
* @param odm
* @param entityType
*/

View File

@@ -42,8 +42,7 @@ public class QuerydslLdapQuery<K> implements FilteredClause<QuerydslLdapQuery<K>
private final Class<? extends K> entityType;
private final LdapSerializer filterGenerator;
private QueryMixin<QuerydslLdapQuery<K>> queryMixin = new QueryMixin<>(this,
new DefaultQueryMetadata().noValidate());
private QueryMixin<QuerydslLdapQuery<K>> queryMixin = new QueryMixin<>(this, new DefaultQueryMetadata().noValidate());
/**
* Creates a new {@link QuerydslLdapQuery}.
@@ -88,8 +87,13 @@ public class QuerydslLdapQuery<K> implements FilteredClause<QuerydslLdapQuery<K>
return ldapOperations.findOne(buildQuery(), entityType);
}
LdapQuery buildQuery() {
return query().filter(filterGenerator.handle(queryMixin.getMetadata().getWhere()));
}
private LdapQuery buildQuery() {
Predicate where = queryMixin.getMetadata().getWhere();
if (where != null) {
return query().filter(filterGenerator.handle(where));
}
return query();
}
}

View File

@@ -28,6 +28,7 @@ import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.domain.Persistable;
import org.springframework.data.ldap.repository.LdapRepository;
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;
@@ -83,7 +84,7 @@ public class SimpleLdapRepository<T> implements LdapRepository<T> {
return callback.getNoOfRows();
}
private <S extends T> boolean isNew(S entity, Name id) {
private <S extends T> boolean isNew(S entity, @Nullable Name id) {
if (entity instanceof Persistable) {
Persistable<?> persistable = (Persistable<?>) entity;
@@ -171,7 +172,7 @@ public class SimpleLdapRepository<T> implements LdapRepository<T> {
Assert.notNull(name, "Id must not be null");
return findById(name) != null;
return findById(name).isPresent();
}
/* (non-Javadoc)

View File

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