diff --git a/src/main/java/org/springframework/data/ldap/core/mapping/BasicLdapPersistentEntity.java b/src/main/java/org/springframework/data/ldap/core/mapping/BasicLdapPersistentEntity.java new file mode 100644 index 0000000..b1052a4 --- /dev/null +++ b/src/main/java/org/springframework/data/ldap/core/mapping/BasicLdapPersistentEntity.java @@ -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 extends BasicPersistentEntity + implements LdapPersistentEntity { + + /** + * Creates a new {@link BasicLdapPersistentEntity}. + * + * @param information must not be {@literal null}. + */ + public BasicLdapPersistentEntity(TypeInformation information) { + super(information); + } +} diff --git a/src/main/java/org/springframework/data/ldap/core/mapping/LdapMappingContext.java b/src/main/java/org/springframework/data/ldap/core/mapping/LdapMappingContext.java new file mode 100644 index 0000000..9ddce2d --- /dev/null +++ b/src/main/java/org/springframework/data/ldap/core/mapping/LdapMappingContext.java @@ -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, 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 BasicLdapPersistentEntity createPersistentEntity(TypeInformation 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); + } +} diff --git a/src/main/java/org/springframework/data/ldap/core/mapping/LdapPersistentEntity.java b/src/main/java/org/springframework/data/ldap/core/mapping/LdapPersistentEntity.java new file mode 100644 index 0000000..8930bbb --- /dev/null +++ b/src/main/java/org/springframework/data/ldap/core/mapping/LdapPersistentEntity.java @@ -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 extends PersistentEntity {} diff --git a/src/main/java/org/springframework/data/ldap/core/mapping/LdapPersistentProperty.java b/src/main/java/org/springframework/data/ldap/core/mapping/LdapPersistentProperty.java new file mode 100644 index 0000000..058c1c1 --- /dev/null +++ b/src/main/java/org/springframework/data/ldap/core/mapping/LdapPersistentProperty.java @@ -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 { + + private final Lazy 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 owner, + SimpleTypeHolder simpleTypeHolder) { + super(property, owner, simpleTypeHolder); + } + + /* (non-Javadoc) + * @see org.springframework.data.mapping.model.AbstractPersistentProperty#createAssociation() + */ + @Override + protected Association createAssociation() { + return null; + } + + /* (non-Javadoc) + * @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#isIdProperty() + */ + @Override + public boolean isIdProperty() { + return isId.get(); + } +} diff --git a/src/main/java/org/springframework/data/ldap/core/mapping/LdapSimpleTypes.java b/src/main/java/org/springframework/data/ldap/core/mapping/LdapSimpleTypes.java new file mode 100644 index 0000000..cda10ea --- /dev/null +++ b/src/main/java/org/springframework/data/ldap/core/mapping/LdapSimpleTypes.java @@ -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> simpleTypes = new HashSet<>(); + simpleTypes.add(Name.class); + + VAULT_SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes); + } + + private static final Set> VAULT_SIMPLE_TYPES; + + public static final SimpleTypeHolder HOLDER = new SimpleTypeHolder(VAULT_SIMPLE_TYPES, true); + + private LdapSimpleTypes() {} +} diff --git a/src/main/java/org/springframework/data/ldap/core/mapping/package-info.java b/src/main/java/org/springframework/data/ldap/core/mapping/package-info.java new file mode 100644 index 0000000..6313446 --- /dev/null +++ b/src/main/java/org/springframework/data/ldap/core/mapping/package-info.java @@ -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; diff --git a/src/main/java/org/springframework/data/ldap/repository/config/LdapRepositoryConfigurationExtension.java b/src/main/java/org/springframework/data/ldap/repository/config/LdapRepositoryConfigurationExtension.java index afc9b06..3c9d18d 100644 --- a/src/main/java/org/springframework/data/ldap/repository/config/LdapRepositoryConfigurationExtension.java +++ b/src/main/java/org/springframework/data/ldap/repository/config/LdapRepositoryConfigurationExtension.java @@ -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(); } } diff --git a/src/main/java/org/springframework/data/ldap/repository/query/LdapQueryCreator.java b/src/main/java/org/springframework/data/ldap/repository/query/LdapQueryCreator.java index 8f430f9..a140b01 100644 --- a/src/main/java/org/springframework/data/ldap/repository/query/LdapQueryCreator.java +++ b/src/main/java/org/springframework/data/ldap/repository/query/LdapQueryCreator.java @@ -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, S> extends RepositoryFactoryBeanSupport { 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, 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, S> Assert.notNull(ldapOperations, "LdapOperations must be set"); super.afterPropertiesSet(); + + if (!mappingContextConfigured) { + setMappingContext(new LdapMappingContext()); + } } } diff --git a/src/test/java/org/springframework/data/ldap/core/mapping/LdapMappingContextUnitTests.java b/src/test/java/org/springframework/data/ldap/core/mapping/LdapMappingContextUnitTests.java new file mode 100644 index 0000000..f734d2c --- /dev/null +++ b/src/test/java/org/springframework/data/ldap/core/mapping/LdapMappingContextUnitTests.java @@ -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; + + } +} diff --git a/src/test/java/org/springframework/data/ldap/core/mapping/LdapPersistentPropertyUnitTests.java b/src/test/java/org/springframework/data/ldap/core/mapping/LdapPersistentPropertyUnitTests.java new file mode 100644 index 0000000..add5bbf --- /dev/null +++ b/src/test/java/org/springframework/data/ldap/core/mapping/LdapPersistentPropertyUnitTests.java @@ -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; + } +} diff --git a/src/test/java/org/springframework/data/ldap/repository/config/LdapRepositoryConfigurationExtensionUnitTests.java b/src/test/java/org/springframework/data/ldap/repository/config/LdapRepositoryConfigurationExtensionUnitTests.java new file mode 100644 index 0000000..16280bf --- /dev/null +++ b/src/test/java/org/springframework/data/ldap/repository/config/LdapRepositoryConfigurationExtensionUnitTests.java @@ -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> 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> 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 {} + + interface UnannotatedRepository extends Repository {} + + interface StoreRepository extends LdapRepository {} +}