diff --git a/core/src/main/java/org/springframework/ldap/odm/core/impl/ObjectMetaData.java b/core/src/main/java/org/springframework/ldap/odm/core/impl/ObjectMetaData.java index 951a076272..ea7bbdeff1 100755 --- a/core/src/main/java/org/springframework/ldap/odm/core/impl/ObjectMetaData.java +++ b/core/src/main/java/org/springframework/ldap/odm/core/impl/ObjectMetaData.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2013 the original author or authors. + * Copyright 2005-2022 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. @@ -18,9 +18,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.ldap.odm.annotations.Entry; import org.springframework.ldap.odm.annotations.Id; import org.springframework.ldap.support.LdapUtils; +import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; import javax.naming.Name; @@ -85,29 +87,33 @@ public AttributeMetaData getAttribute(Field field) { return fieldToAttribute.get(field); } - public ObjectMetaData(Class clazz) { + public ObjectMetaData(final Class clazz) { if (LOG.isDebugEnabled()) { LOG.debug(String.format("Extracting metadata from %1$s", clazz)); } // Get object class metadata - the @Entity annotation - Entry entity = clazz.getAnnotation(Entry.class); - if (entity != null) { + // findAllMergedAnnotations will return set of inherited annotations and apply them from superclass down to subclass + Set entities = AnnotatedElementUtils.findAllMergedAnnotations(clazz,Entry.class); + if (entities != null && !entities.isEmpty()) { // Default objectclass name to the class name unless it's specified // in @Entity(name={objectclass1, objectclass2}); - String[] localObjectClasses = entity.objectClasses(); - if (localObjectClasses != null && localObjectClasses.length > 0 && localObjectClasses[0].length() > 0) { - for (String localObjectClass:localObjectClasses) { - objectClasses.add(new CaseIgnoreString(localObjectClass)); + for (Entry entity: entities) { + String[] localObjectClasses = entity.objectClasses(); + if (localObjectClasses.length > 0 && localObjectClasses[0].length() > 0) { + for (String localObjectClass : localObjectClasses) { + objectClasses.add(new CaseIgnoreString(localObjectClass)); + } } - } else { + String base = entity.base(); + if (StringUtils.hasText(base)) { + this.base = LdapUtils.newLdapName(base); + } + } + if(objectClasses.isEmpty()) { objectClasses.add(new CaseIgnoreString(clazz.getSimpleName())); } - String base = entity.base(); - if(StringUtils.hasText(base)) { - this.base = LdapUtils.newLdapName(base); - } } else { throw new MetaDataException(String.format("Class %1$s must have a class level %2$s annotation", clazz, Entry.class)); @@ -119,31 +125,34 @@ public ObjectMetaData(Class clazz) { } // Get field meta-data - the @Attribute annotation - Field[] fields = clazz.getDeclaredFields(); - for (Field field : fields) { - // So we can write to private fields - field.setAccessible(true); - - // Skip synthetic or static fields - if (Modifier.isStatic(field.getModifiers()) || field.isSynthetic()) { - continue; - } - - AttributeMetaData currentAttributeMetaData=new AttributeMetaData(field); - if (currentAttributeMetaData.isId()) { - if (idAttribute!=null) { - // There can be only one id field - throw new MetaDataException( - String.format("You man have only one field with the %1$s annotation in class %2$s", Id.class, clazz)); + ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback() { + @Override + public void doWith(Field field) throws IllegalArgumentException { + // So we can write to private fields + field.setAccessible(true); + + // Skip synthetic or static fields + if (Modifier.isStatic(field.getModifiers()) || field.isSynthetic()) { + return; + } + + AttributeMetaData currentAttributeMetaData=new AttributeMetaData(field); + if (currentAttributeMetaData.isId()) { + if (idAttribute!=null) { + // There can be only one id field + throw new MetaDataException( + String.format("You man have only one field with the %1$s annotation in class %2$s", Id.class, clazz)); + } + idAttribute=currentAttributeMetaData; + } + fieldToAttribute.put(field, currentAttributeMetaData); + + if(currentAttributeMetaData.isDnAttribute()) { + dnAttributes.add(currentAttributeMetaData); + } } - idAttribute=currentAttributeMetaData; - } - fieldToAttribute.put(field, currentAttributeMetaData); - - if(currentAttributeMetaData.isDnAttribute()) { - dnAttributes.add(currentAttributeMetaData); } - } + ); if (idAttribute == null) { throw new MetaDataException( diff --git a/core/src/test/java/org/springframework/ldap/odm/core/impl/DefaultObjectDirectoryMapperTest.java b/core/src/test/java/org/springframework/ldap/odm/core/impl/DefaultObjectDirectoryMapperTest.java index 051e93ddd3..2bab9cc7c0 100644 --- a/core/src/test/java/org/springframework/ldap/odm/core/impl/DefaultObjectDirectoryMapperTest.java +++ b/core/src/test/java/org/springframework/ldap/odm/core/impl/DefaultObjectDirectoryMapperTest.java @@ -77,6 +77,42 @@ public void testMapping() { assertField(entityData, "entryUUID", "entryUUID", null, false, false, false, true); } + @Test + public void testMappingInherited() { + assertThat(tested.manageClass(UnitTestWorker.class)) + .containsOnlyElementsOf(Arrays.asList("dn", "cn", "sn", "description", "telephoneNumber", "entryUUID", "objectclass", "workerId")); + + DefaultObjectDirectoryMapper.EntityData entityData = tested.getMetaDataMap().get(UnitTestWorker.class); + + assertThat(entityData).isNotNull(); + assertThat(entityData.ocFilter).isEqualTo(query(). + where("objectclass").is("inetOrgPerson") + .and("objectclass").is("organizationalPerson") + .and("objectclass").is("person") + .and("objectclass").is("top") + .and("objectclass").is("worker") + .filter()); + + assertThat(entityData.metaData).hasSize(9); + + AttributeMetaData idAttribute = entityData.metaData.getIdAttribute(); + assertThat(idAttribute.getField().getName()).isEqualTo("dn"); + assertThat(idAttribute.isId()).isTrue(); + assertThat(idAttribute.isBinary()).isFalse(); + assertThat(idAttribute.isDnAttribute()).isFalse(); + assertThat(idAttribute.isTransient()).isFalse(); + assertThat(idAttribute.isCollection()).isFalse(); + + assertField(entityData, "fullName", "cn", "cn", false, false, false, false); + assertField(entityData, "lastName", "sn", null, false, false, false, false); + assertField(entityData, "description", "description", null, false, false, true, false); + assertField(entityData, "country", null, "c", false, true, false, false); + assertField(entityData, "company", null, "ou", false, true, false, false); + assertField(entityData, "telephoneNumber", "telephoneNumber", null, false, false, false, false); + assertField(entityData, "entryUUID", "entryUUID", null, false, false, false, true); + assertField(entityData, "workerId", "workerId", null, false, false, false, false); + } + @Test public void testInvalidType() { try { diff --git a/core/src/test/java/org/springframework/ldap/odm/core/impl/UnitTestWorker.java b/core/src/test/java/org/springframework/ldap/odm/core/impl/UnitTestWorker.java new file mode 100644 index 0000000000..7b1247c1ba --- /dev/null +++ b/core/src/test/java/org/springframework/ldap/odm/core/impl/UnitTestWorker.java @@ -0,0 +1,30 @@ +/* + * Copyright 2005-2022 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 + * + * https://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.ldap.odm.core.impl; + +import org.springframework.ldap.odm.annotations.Attribute; +import org.springframework.ldap.odm.annotations.Entry; + +/** + * @author Robert Wilson + */ +@Entry(objectClasses = {"worker"}) +public class UnitTestWorker extends UnitTestPerson{ + @Attribute(name = "workerId") + private String workerId; +} +