DATALDAP-60 - Add LdapMappingContext and LdapPersistentEntity.

Add mapping context infrastructure for usage with Spring Data REST.
This commit is contained in:
Mark Paluch
2018-02-19 10:12:15 +01:00
parent 282b32834c
commit 00f9ede74c
12 changed files with 490 additions and 4 deletions

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2018 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.core.mapping;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.util.TypeInformation;
/**
* {@link LdapPersistentEntity} implementation.
*
* @author Mark Paluch
* @since 2.0.4
*/
public class BasicLdapPersistentEntity<T> extends BasicPersistentEntity<T, LdapPersistentProperty>
implements LdapPersistentEntity<T> {
/**
* Creates a new {@link BasicLdapPersistentEntity}.
*
* @param information must not be {@literal null}.
*/
public BasicLdapPersistentEntity(TypeInformation<T> information) {
super(information);
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2018 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.core.mapping;
import org.springframework.data.mapping.context.AbstractMappingContext;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.TypeInformation;
/**
* Mapping context for {@link LdapPersistentEntity LDAP-specific entities}.
*
* @author Mark Paluch
* @since 2.0.4
*/
public class LdapMappingContext extends AbstractMappingContext<BasicLdapPersistentEntity<?>, LdapPersistentProperty> {
/**
* Creates a new {@link LdapMappingContext}.
*/
public LdapMappingContext() {
setSimpleTypeHolder(LdapSimpleTypes.HOLDER);
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation)
*/
@Override
protected <T> BasicLdapPersistentEntity<?> createPersistentEntity(TypeInformation<T> typeInformation) {
return new BasicLdapPersistentEntity<>(typeInformation);
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentProperty(org.springframework.data.mapping.model.Property, org.springframework.data.mapping.model.MutablePersistentEntity, org.springframework.data.mapping.model.SimpleTypeHolder)
*/
@Override
protected LdapPersistentProperty createPersistentProperty(Property property, BasicLdapPersistentEntity<?> owner,
SimpleTypeHolder simpleTypeHolder) {
return new LdapPersistentProperty(property, owner, simpleTypeHolder);
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2018 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.core.mapping;
import org.springframework.data.mapping.PersistentEntity;
/**
* LDAP specific {@link PersistentEntity}.
*
* @author Mark Paluch
* @since 2.0.4
*/
public interface LdapPersistentEntity<T> extends PersistentEntity<T, LdapPersistentProperty> {}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2018 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.core.mapping;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.Lazy;
import org.springframework.ldap.odm.annotations.Id;
/**
* LDAP-specific {@link AnnotationBasedPersistentProperty}. By default, if a property is named {@code id} it's used as
* Id property.
*
* @author Mark Paluch
* @since 2.0.4
*/
public class LdapPersistentProperty extends AnnotationBasedPersistentProperty<LdapPersistentProperty> {
private final Lazy<Boolean> isId = Lazy.of(() -> isAnnotationPresent(Id.class));
/**
* Create a new {@link LdapPersistentProperty}.
*
* @param property must not be {@literal null}.
* @param owner must not be {@literal null}.
* @param simpleTypeHolder must not be {@literal null}.
*/
public LdapPersistentProperty(Property property, PersistentEntity<?, LdapPersistentProperty> owner,
SimpleTypeHolder simpleTypeHolder) {
super(property, owner, simpleTypeHolder);
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.model.AbstractPersistentProperty#createAssociation()
*/
@Override
protected Association<LdapPersistentProperty> createAssociation() {
return null;
}
/* (non-Javadoc)
* @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#isIdProperty()
*/
@Override
public boolean isIdProperty() {
return isId.get();
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2018 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.core.mapping;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.naming.Name;
import org.springframework.data.mapping.model.SimpleTypeHolder;
/**
* Simple constant holder for a {@link SimpleTypeHolder} enriched with LDAP-specific simple types.
*
* @author Mark Paluch
* @since 2.0.4
*/
public abstract class LdapSimpleTypes {
static {
Set<Class<?>> simpleTypes = new HashSet<>();
simpleTypes.add(Name.class);
VAULT_SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes);
}
private static final Set<Class<?>> VAULT_SIMPLE_TYPES;
public static final SimpleTypeHolder HOLDER = new SimpleTypeHolder(VAULT_SIMPLE_TYPES, true);
private LdapSimpleTypes() {}
}

View File

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

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 the original author or authors.
* Copyright 2016-2018 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,14 +19,20 @@ import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Collections;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.data.ldap.core.mapping.LdapMappingContext;
import org.springframework.data.ldap.repository.LdapRepository;
import org.springframework.data.ldap.repository.support.LdapRepositoryFactoryBean;
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
import org.springframework.data.repository.config.RepositoryConfigurationSource;
import org.springframework.data.repository.config.XmlRepositoryConfigurationSource;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.ldap.odm.annotations.Entry;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
@@ -40,6 +46,7 @@ import org.w3c.dom.Element;
public class LdapRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport {
private static final String ATT_LDAP_TEMPLATE_REF = "ldap-template-ref";
private static final String MAPPING_CONTEXT_BEAN_NAME = "ldapMappingContext";
/*
* (non-Javadoc)
@@ -99,6 +106,7 @@ public class LdapRepositoryConfigurationExtension extends RepositoryConfiguratio
}
builder.addPropertyReference("ldapOperations", ldapTemplateRef);
builder.addPropertyReference("mappingContext", MAPPING_CONTEXT_BEAN_NAME);
}
/* (non-Javadoc)
@@ -110,5 +118,28 @@ public class LdapRepositoryConfigurationExtension extends RepositoryConfiguratio
AnnotationAttributes attributes = config.getAttributes();
builder.addPropertyReference("ldapOperations", attributes.getString("ldapTemplateRef"));
builder.addPropertyReference("mappingContext", MAPPING_CONTEXT_BEAN_NAME);
}
@Override
public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource configurationSource) {
if (!registry.containsBeanDefinition(MAPPING_CONTEXT_BEAN_NAME)) {
RootBeanDefinition definition = new RootBeanDefinition(LdapMappingContext.class);
definition.setRole(AbstractBeanDefinition.ROLE_INFRASTRUCTURE);
definition.setSource(configurationSource.getSource());
registry.registerBeanDefinition(MAPPING_CONTEXT_BEAN_NAME, definition);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#useRepositoryConfiguration(org.springframework.data.repository.core.RepositoryMetadata)
*/
@Override
protected boolean useRepositoryConfiguration(RepositoryMetadata metadata) {
return !metadata.isReactiveRepository();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 the original author or authors.
* Copyright 2016-2018 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.
@@ -92,7 +92,8 @@ class LdapQueryCreator extends AbstractQueryCreator<LdapQuery, ContainerCriteria
String value = null;
if (iterator.hasNext()) {
value = iterator.next().toString();
Object next = iterator.next();
value = next != null ? next.toString() : null;
}
switch (type) {
case NEGATING_SIMPLE_PROPERTY:

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 the original author or authors.
* Copyright 2016-2018 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.
@@ -17,6 +17,8 @@ package org.springframework.data.ldap.repository.support;
import javax.naming.Name;
import org.springframework.data.ldap.core.mapping.LdapMappingContext;
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;
@@ -36,6 +38,7 @@ public class LdapRepositoryFactoryBean<T extends Repository<S, Name>, S>
extends RepositoryFactoryBeanSupport<T, S, Name> {
private @Nullable LdapOperations ldapOperations;
private boolean mappingContextConfigured = false;
/**
* Creates a new {@link LdapRepositoryFactoryBean} for the given repository interface.
@@ -46,10 +49,24 @@ public class LdapRepositoryFactoryBean<T extends Repository<S, Name>, S>
super(repositoryInterface);
}
/**
* @param ldapOperations
*/
public void setLdapOperations(LdapOperations ldapOperations) {
this.ldapOperations = ldapOperations;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#setMappingContext(org.springframework.data.mapping.context.MappingContext)
*/
@Override
public void setMappingContext(MappingContext<?, ?> mappingContext) {
super.setMappingContext(mappingContext);
this.mappingContextConfigured = true;
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#createRepositoryFactory()
@@ -72,5 +89,9 @@ public class LdapRepositoryFactoryBean<T extends Repository<S, Name>, S>
Assert.notNull(ldapOperations, "LdapOperations must be set");
super.afterPropertiesSet();
if (!mappingContextConfigured) {
setMappingContext(new LdapMappingContext());
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2018 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.core.mapping;
import static org.assertj.core.api.Assertions.*;
import org.junit.Test;
import org.springframework.ldap.odm.annotations.Id;
/**
* Unit tests for {@link LdapMappingContext}.
*
* @author Mark Paluch
*/
public class LdapMappingContextUnitTests {
@Test // DATALDAP-60
public void shouldCreatePersistentEntities() {
LdapMappingContext context = new LdapMappingContext();
BasicLdapPersistentEntity<?> entity = context.getPersistentEntity(Person.class);
assertThat(entity).isNotNull();
}
static class Person {
@Id String id;
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2018 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.core.mapping;
import static org.assertj.core.api.Assertions.*;
import org.junit.Test;
import org.springframework.ldap.odm.annotations.Id;
/**
* Unit tests for {@link LdapPersistentProperty}.
*
* @author Mark Paluch
*/
public class LdapPersistentPropertyUnitTests {
@Test // DATALDAP-60
public void shouldConsiderOdmIdAsIdentifier() {
LdapMappingContext context = new LdapMappingContext();
BasicLdapPersistentEntity<?> entity = context.getRequiredPersistentEntity(Person.class);
assertThat(entity.getRequiredPersistentProperty("odmId").isIdProperty()).isTrue();
assertThat(entity.getRequiredPersistentProperty("dataId").isIdProperty()).isFalse();
}
static class Person {
@Id String odmId;
@org.springframework.data.annotation.Id String dataId;
}
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright 2018 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.config;
import static org.junit.Assert.*;
import java.util.Collection;
import org.junit.Test;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.data.ldap.repository.LdapRepository;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
import org.springframework.data.repository.config.RepositoryConfiguration;
import org.springframework.data.repository.config.RepositoryConfigurationSource;
import org.springframework.ldap.odm.annotations.Entry;
/**
* Unit tests for {@link LdapRepositoryConfigurationExtension}.
*
* @author Mark Paluch
*/
public class LdapRepositoryConfigurationExtensionUnitTests {
StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(Config.class, true);
ResourceLoader loader = new PathMatchingResourcePatternResolver();
Environment environment = new StandardEnvironment();
BeanDefinitionRegistry registry = new DefaultListableBeanFactory();
RepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(metadata,
EnableLdapRepositories.class, loader, environment, registry);
@Test // DATALDAP-60
public void isStrictMatchIfDomainTypeIsAnnotatedWithEntry() {
LdapRepositoryConfigurationExtension extension = new LdapRepositoryConfigurationExtension();
assertHasRepo(SampleRepository.class, extension.getRepositoryConfigurations(configurationSource, loader, true));
}
@Test // DATALDAP-60
public void isStrictMatchIfRepositoryExtendsStoreSpecificBase() {
LdapRepositoryConfigurationExtension extension = new LdapRepositoryConfigurationExtension();
assertHasRepo(StoreRepository.class, extension.getRepositoryConfigurations(configurationSource, loader, true));
}
@Test // DATALDAP-60
public void isNotStrictMatchIfDomainTypeIsNotAnnotatedWithEntry() {
LdapRepositoryConfigurationExtension extension = new LdapRepositoryConfigurationExtension();
assertDoesNotHaveRepo(UnannotatedRepository.class,
extension.getRepositoryConfigurations(configurationSource, loader, true));
}
private static void assertHasRepo(Class<?> repositoryInterface,
Collection<RepositoryConfiguration<RepositoryConfigurationSource>> configs) {
for (RepositoryConfiguration<?> config : configs) {
if (config.getRepositoryInterface().equals(repositoryInterface.getName())) {
return;
}
}
fail("Expected to find config for repository interface ".concat(repositoryInterface.getName()).concat(" but got ")
.concat(configs.toString()));
}
private static void assertDoesNotHaveRepo(Class<?> repositoryInterface,
Collection<RepositoryConfiguration<RepositoryConfigurationSource>> configs) {
for (RepositoryConfiguration<?> config : configs) {
if (config.getRepositoryInterface().equals(repositoryInterface.getName())) {
fail("Expected not to find config for repository interface ".concat(repositoryInterface.getName()));
}
}
}
@EnableLdapRepositories(considerNestedRepositories = true)
static class Config {}
@Entry(objectClasses = "person")
static class Sample {}
interface SampleRepository extends Repository<Sample, Long> {}
interface UnannotatedRepository extends Repository<Object, Long> {}
interface StoreRepository extends LdapRepository<Object> {}
}