You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by bd...@apache.org on 2022/08/23 18:41:54 UTC

[directory-scimple] 02/02: Don't expose Schema.Attribute.field directly

This is an automated email from the ASF dual-hosted git repository.

bdemers pushed a commit to branch attribute-accessor
in repository https://gitbox.apache.org/repos/asf/directory-scimple.git

commit f6aa6bdcacb3c4f8bb0e6f9343db313f47b17a09
Author: Brian Demers <bd...@apache.org>
AuthorDate: Tue Aug 23 12:30:06 2022 -0400

    Don't expose Schema.Attribute.field directly
    
    The use of reflection is an implementation detail. This change moves this behind a new interface `AttributeAccessor` which provides similar functional.
    In the future, implementations could use bean accessors instead of private fields directly
    NOTE: the current implementation still access fields directly.
---
 .../scim/server/provider/ProviderRegistry.java     |  12 +-
 .../scim/server/provider/UpdateRequest.java        |  14 +-
 .../scim/server/rest/BulkResourceImpl.java         | 235 +++++++++------------
 .../apache/directory/scim/spec/schema/Schema.java  |  70 ++++--
 4 files changed, 164 insertions(+), 167 deletions(-)

diff --git a/scim-server/src/main/java/org/apache/directory/scim/server/provider/ProviderRegistry.java b/scim-server/src/main/java/org/apache/directory/scim/server/provider/ProviderRegistry.java
index ac39bcb..1ad3f73 100644
--- a/scim-server/src/main/java/org/apache/directory/scim/server/provider/ProviderRegistry.java
+++ b/scim-server/src/main/java/org/apache/directory/scim/server/provider/ProviderRegistry.java
@@ -191,19 +191,23 @@ public class ProviderRegistry implements ScimConfiguration {
     return resourceType;
   }
 
+  public static Schema generateSchema(Class<? extends ScimResource> clazz) throws InvalidProviderException {
+    return generateBaseSchema(clazz);
+  }
+
   private static Schema generateBaseSchema(Class<?> clazz) throws InvalidProviderException {
     List<Field> fieldList = ReflectionUtils.getFieldsUpTo(clazz, BaseResource.class);
 
     return generateSchema(clazz, fieldList);
   }
   
-  private static Schema generateExtensionSchema(Class<?> clazz) throws InvalidProviderException {
+  public static Schema generateExtensionSchema(Class<?> clazz) throws InvalidProviderException {
     log.debug("----> In generateExtensionSchema");
     
     return generateSchema(clazz, ReflectionUtils.getFieldsUpTo(clazz, Object.class));
   }
   
-  public static Schema generateSchema(Class<?> clazz, List<Field> fieldList) throws InvalidProviderException {
+  private static Schema generateSchema(Class<?> clazz, List<Field> fieldList) throws InvalidProviderException {
 
     // Field [] fieldList = clazz.getDeclaredFields();
 
@@ -277,7 +281,7 @@ public class ProviderRegistry implements ScimConfiguration {
 
       //TODO - Fix this to look for the two types of canonical attributes
       Attribute attribute = new Attribute();
-      attribute.setField(f);
+      attribute.setAccessor(Schema.AttributeAccessor.forField(f));
       attribute.setName(attributeName);
       attribute.setUrn(urn);
       
@@ -385,7 +389,7 @@ public class ProviderRegistry implements ScimConfiguration {
         if (attributeIsAString) {
           attribute.setScimResourceIdReference(true);
         } else {
-          log.warn("Field annotated with @edu.psu.swe.scim.spec.annotation.ScimResourceIdReference must be a string: {}", f);
+          log.warn("Field annotated with @ScimResourceIdReference must be a string: {}", f);
         }
       }
       attribute.setMutability(sa.mutability());
diff --git a/scim-server/src/main/java/org/apache/directory/scim/server/provider/UpdateRequest.java b/scim-server/src/main/java/org/apache/directory/scim/server/provider/UpdateRequest.java
index 8dae3dc..856e3b4 100644
--- a/scim-server/src/main/java/org/apache/directory/scim/server/provider/UpdateRequest.java
+++ b/scim-server/src/main/java/org/apache/directory/scim/server/provider/UpdateRequest.java
@@ -54,7 +54,6 @@ import org.apache.directory.scim.spec.schema.AttributeContainer;
 import org.apache.directory.scim.spec.schema.Schema;
 import org.apache.directory.scim.spec.schema.Schema.Attribute;
 
-import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -149,12 +148,12 @@ public class UpdateRequest<T extends ScimResource> {
 
   private void sortMultiValuedCollections(Object obj1, Object obj2, AttributeContainer ac) throws IllegalArgumentException, IllegalAccessException {
     for (Attribute attribute : ac.getAttributes()) {
-      Field field = attribute.getField();
+      Schema.AttributeAccessor accessor = attribute.getAccessor();
       if (attribute.isMultiValued()) {
         @SuppressWarnings("unchecked")
-        List<Object> collection1 = obj1 != null ? (List<Object>) field.get(obj1) : null;
+        List<Object> collection1 = obj1 != null ? (List<Object>) accessor.get(obj1) : null;
         @SuppressWarnings("unchecked")
-        List<Object> collection2 = obj2 != null ? (List<Object>) field.get(obj2) : null;
+        List<Object> collection2 = obj2 != null ? (List<Object>) accessor.get(obj2) : null;
         
         Set<Object> priorities = findCommonElements(collection1, collection2);
         PrioritySortingComparitor prioritySortingComparitor = new PrioritySortingComparitor(priorities);
@@ -166,8 +165,8 @@ public class UpdateRequest<T extends ScimResource> {
           Collections.sort(collection2, prioritySortingComparitor);
         }
       } else if (attribute.getType() == Attribute.Type.COMPLEX) {
-        Object nextObj1 = obj1 != null ? field.get(obj1) : null;
-        Object nextObj2 = obj2 != null ? field.get(obj2) : null;
+        Object nextObj1 = obj1 != null ? accessor.get(obj1) : null;
+        Object nextObj2 = obj2 != null ? accessor.get(obj2) : null;
         sortMultiValuedCollections(nextObj1, nextObj2, attribute);
       }
     }
@@ -595,8 +594,7 @@ public class UpdateRequest<T extends ScimResource> {
       }
 
       Attribute attribute = ac.getAttribute(attributeName);
-      Field field = attribute.getField();
-      return field.get(object);
+      return attribute.getAccessor().get(object);
     }
   }
 
diff --git a/scim-server/src/main/java/org/apache/directory/scim/server/rest/BulkResourceImpl.java b/scim-server/src/main/java/org/apache/directory/scim/server/rest/BulkResourceImpl.java
index f301878..0ffd13c 100644
--- a/scim-server/src/main/java/org/apache/directory/scim/server/rest/BulkResourceImpl.java
+++ b/scim-server/src/main/java/org/apache/directory/scim/server/rest/BulkResourceImpl.java
@@ -19,7 +19,6 @@
 
 package org.apache.directory.scim.server.rest;
 
-import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -502,7 +501,7 @@ public class BulkResourceImpl implements BulkResource {
   @AllArgsConstructor
   private static class UnresolvedComplex {
     private final Object object;
-    private final Field field;
+    private final Schema.AttributeAccessor accessor;
     private final String bulkIdKey;
 
     public void resolve(Map<String, BulkOperation> bulkIdKeyToOperationResult) throws UnresolvableOperationException {
@@ -512,12 +511,7 @@ public class BulkResourceImpl implements BulkResource {
 
       if ((response == null || !(response instanceof ErrorResponse)) && resolvedResource != null) {
         String resolvedId = resolvedResource.getId();
-
-        try {
-          this.field.set(this.object, resolvedId);
-        } catch (IllegalAccessException illegalAccessException) {
-          log.error("Failed to access bulkId field", illegalAccessException);
-        }
+        this.accessor.set(this.object, resolvedId);
       } else {
         throw new UnresolvableOperationException(String.format(BULK_ID_REFERS_TO_FAILED_RESOURCE, this.bulkIdKey));
       }
@@ -526,7 +520,7 @@ public class BulkResourceImpl implements BulkResource {
 
   @AllArgsConstructor
   private static abstract class UnresolvedTopLevel {
-    protected final Field field;
+    protected final Schema.AttributeAccessor accessor;
 
     public abstract void resolve(ScimResource scimResource, Map<String, BulkOperation> bulkIdKeyToOperationResult) throws UnresolvableOperationException;
   }
@@ -534,8 +528,8 @@ public class BulkResourceImpl implements BulkResource {
   private static class UnresolvedTopLevelBulkId extends UnresolvedTopLevel {
     private final String unresolvedBulkIdKey;
 
-    public UnresolvedTopLevelBulkId(Field field, String bulkIdKey) {
-      super(field);
+    public UnresolvedTopLevelBulkId(Schema.AttributeAccessor accessor, String bulkIdKey) {
+      super(accessor);
       this.unresolvedBulkIdKey = bulkIdKey;
     }
 
@@ -548,11 +542,7 @@ public class BulkResourceImpl implements BulkResource {
       if ((response == null || !(response instanceof ErrorResponse)) && resolvedResource != null) {
         String resolvedId = resolvedResource.getId();
 
-        try {
-          super.field.set(scimResource, resolvedId);
-        } catch (IllegalAccessException illegalAccessException) {
-          log.error("Failed to access bulkId field", illegalAccessException);
-        }
+        super.accessor.set(scimResource, resolvedId);
       } else {
         throw new UnresolvableOperationException("Bulk ID cannot be resolved because the resource it refers to had failed to be created: " + this.unresolvedBulkIdKey);
       }
@@ -563,22 +553,18 @@ public class BulkResourceImpl implements BulkResource {
     public final Object complex;
     public final List<UnresolvedComplex> unresolveds;
 
-    public UnresolvedTopLevelComplex(Field field, Object complex, List<UnresolvedComplex> unresolveds) {
-      super(field);
+    public UnresolvedTopLevelComplex(Schema.AttributeAccessor accessor, Object complex, List<UnresolvedComplex> unresolveds) {
+      super(accessor);
       this.complex = complex;
       this.unresolveds = unresolveds;
     }
 
     @Override
     public void resolve(ScimResource scimResource, Map<String, BulkOperation> bulkIdKeyToOperationResult) throws UnresolvableOperationException {
-      try {
-        for (UnresolvedComplex unresolved : this.unresolveds) {
-          unresolved.resolve(bulkIdKeyToOperationResult);
-        }
-        this.field.set(scimResource, this.complex);
-      } catch (IllegalAccessException illegalAccessException) {
-        log.error("Could not resolve top level SCIM resource", illegalAccessException);
+      for (UnresolvedComplex unresolved : this.unresolveds) {
+        unresolved.resolve(bulkIdKeyToOperationResult);
       }
+      this.accessor.set(scimResource, this.complex);
     }
   }
 
@@ -600,54 +586,50 @@ public class BulkResourceImpl implements BulkResource {
     List<Schema.Attribute> attributes = attribute.getAttributes();
 
     for (Schema.Attribute subAttribute : attributes) {
-      Field attributeField = subAttribute.getField();
+      Schema.AttributeAccessor accessor = subAttribute.getAccessor();
 
-      try {
-        if (subAttribute.isScimResourceIdReference()) {
-          // TODO - This will fail if field is a char or Character array
-          String bulkIdKey = (String) attributeField.get(attributeValue);
-
-          if (bulkIdKey != null && bulkIdKey.startsWith("bulkId:")) {
-            log.debug("Found bulkId: {}", bulkIdKey);
-            if (bulkIdKeyToOperationResult.containsKey(bulkIdKey)) {
-              BulkOperation resolvedOperationResult = bulkIdKeyToOperationResult.get(bulkIdKey);
-              BaseResource response = resolvedOperationResult.getResponse();
-              ScimResource resolvedResource = resolvedOperationResult.getData();
-
-              if ((response == null || !(response instanceof ErrorResponse)) && resolvedResource != null && resolvedResource.getId() != null) {
-                String resolvedId = resolvedResource.getId();
-
-                attributeField.set(attributeValue, resolvedId);
-              } else {
-                UnresolvedComplex unresolved = new UnresolvedComplex(attributeValue, attributeField, bulkIdKey);
-
-                unresolveds.add(unresolved);
-              }
+      if (subAttribute.isScimResourceIdReference()) {
+        // TODO - This will fail if field is a char or Character array
+        String bulkIdKey = accessor.get(attributeValue);
+
+        if (bulkIdKey != null && bulkIdKey.startsWith("bulkId:")) {
+          log.debug("Found bulkId: {}", bulkIdKey);
+          if (bulkIdKeyToOperationResult.containsKey(bulkIdKey)) {
+            BulkOperation resolvedOperationResult = bulkIdKeyToOperationResult.get(bulkIdKey);
+            BaseResource response = resolvedOperationResult.getResponse();
+            ScimResource resolvedResource = resolvedOperationResult.getData();
+
+            if ((response == null || !(response instanceof ErrorResponse)) && resolvedResource != null && resolvedResource.getId() != null) {
+              String resolvedId = resolvedResource.getId();
+
+              accessor.set(attributeValue, resolvedId);
             } else {
-              throw new UnresolvableOperationException(String.format(BULK_ID_DOES_NOT_EXIST, bulkIdKey));
+              UnresolvedComplex unresolved = new UnresolvedComplex(attributeValue, accessor, bulkIdKey);
+
+              unresolveds.add(unresolved);
             }
+          } else {
+            throw new UnresolvableOperationException(String.format(BULK_ID_DOES_NOT_EXIST, bulkIdKey));
           }
-        } else if (subAttribute.getType() == Schema.Attribute.Type.COMPLEX) {
-          Object subFieldValue = attributeField.get(attributeValue);
+        }
+      } else if (subAttribute.getType() == Schema.Attribute.Type.COMPLEX) {
+        Object subFieldValue = accessor.get(attributeValue);
 
-          if (subFieldValue != null) {
-            Class<?> subFieldClass = subFieldValue.getClass();
-            boolean isCollection = Collection.class.isAssignableFrom(subFieldClass);
+        if (subFieldValue != null) {
+          Class<?> subFieldClass = subFieldValue.getClass();
+          boolean isCollection = Collection.class.isAssignableFrom(subFieldClass);
 
-            if (isCollection || subFieldClass.isArray()) {
-              @SuppressWarnings("unchecked")
-              Collection<Object> subFieldValues = isCollection ? (Collection<Object>) subFieldValue : Arrays.asList((Object[]) subFieldValue);
+          if (isCollection || subFieldClass.isArray()) {
+            @SuppressWarnings("unchecked")
+            Collection<Object> subFieldValues = isCollection ? (Collection<Object>) subFieldValue : Arrays.asList((Object[]) subFieldValue);
 
-              for (Object subArrayFieldValue : subFieldValues) {
-                resolveAttribute(unresolveds, subArrayFieldValue, subAttribute, bulkIdKeyToOperationResult);
-              }
-            } else {
-              resolveAttribute(unresolveds, subFieldValue, subAttribute, bulkIdKeyToOperationResult);
+            for (Object subArrayFieldValue : subFieldValues) {
+              resolveAttribute(unresolveds, subArrayFieldValue, subAttribute, bulkIdKeyToOperationResult);
             }
+          } else {
+            resolveAttribute(unresolveds, subFieldValue, subAttribute, bulkIdKeyToOperationResult);
           }
         }
-      } catch (IllegalAccessException illegalAccessException) {
-        log.error("Could not resolve bulkId within ScimResource attribute", illegalAccessException);
       }
     }
     log.debug("Resolved attribute had {} unresolved fields", unresolveds.size());
@@ -671,61 +653,57 @@ public class BulkResourceImpl implements BulkResource {
     List<UnresolvedTopLevel> unresolvedTopLevels = new ArrayList<>();
 
     for (Schema.Attribute attribute : schema.getAttributes()) {
-      Field attributeField = attribute.getField();
+      Schema.AttributeAccessor accessor = attribute.getAccessor();
 
-      try {
-        if (attribute.isScimResourceIdReference()) {
-          String bulkIdKey = (String) attributeField.get(scimResource);
+      if (attribute.isScimResourceIdReference()) {
+        String bulkIdKey = accessor.get(scimResource);
 
-          if (bulkIdKey != null && bulkIdKey.startsWith("bulkId:")) {
-            if (bulkIdKeyToOperationResult.containsKey(bulkIdKey)) {
-              BulkOperation resolvedOperationResult = bulkIdKeyToOperationResult.get(bulkIdKey);
-              BaseResource response = resolvedOperationResult.getResponse();
-              ScimResource resolvedResource = resolvedOperationResult.getData();
+        if (bulkIdKey != null && bulkIdKey.startsWith("bulkId:")) {
+          if (bulkIdKeyToOperationResult.containsKey(bulkIdKey)) {
+            BulkOperation resolvedOperationResult = bulkIdKeyToOperationResult.get(bulkIdKey);
+            BaseResource response = resolvedOperationResult.getResponse();
+            ScimResource resolvedResource = resolvedOperationResult.getData();
 
-              if ((response == null || !(response instanceof ErrorResponse)) && resolvedResource != null) {
-                String resolvedId = resolvedResource.getId();
+            if ((response == null || !(response instanceof ErrorResponse)) && resolvedResource != null) {
+              String resolvedId = resolvedResource.getId();
 
-                attributeField.set(scimResource, resolvedId);
-              } else {
-                UnresolvedTopLevel unresolved = new UnresolvedTopLevelBulkId(attributeField, bulkIdKey);
-
-                attributeField.set(scimResource, null);
-                unresolvedTopLevels.add(unresolved);
-              }
+              accessor.set(scimResource, resolvedId);
             } else {
-              throw new UnresolvableOperationException(String.format(BULK_ID_DOES_NOT_EXIST, bulkIdKey));
+              UnresolvedTopLevel unresolved = new UnresolvedTopLevelBulkId(accessor, bulkIdKey);
+
+              accessor.set(scimResource, null);
+              unresolvedTopLevels.add(unresolved);
             }
+          } else {
+            throw new UnresolvableOperationException(String.format(BULK_ID_DOES_NOT_EXIST, bulkIdKey));
           }
-        } else if (attribute.getType() == Schema.Attribute.Type.COMPLEX) {
-          Object attributeFieldValue = attributeField.get(scimResource);
+        }
+      } else if (attribute.getType() == Schema.Attribute.Type.COMPLEX) {
+        Object attributeFieldValue = accessor.get(scimResource);
 
-          if (attributeFieldValue != null) {
-            List<UnresolvedComplex> subUnresolveds = new ArrayList<>();
-            Class<?> subFieldClass = attributeFieldValue.getClass();
-            boolean isCollection = Collection.class.isAssignableFrom(subFieldClass);
+        if (attributeFieldValue != null) {
+          List<UnresolvedComplex> subUnresolveds = new ArrayList<>();
+          Class<?> subFieldClass = attributeFieldValue.getClass();
+          boolean isCollection = Collection.class.isAssignableFrom(subFieldClass);
 
-            if (isCollection || subFieldClass.isArray()) {
-              @SuppressWarnings("unchecked")
-              Collection<Object> subFieldValues = isCollection ? (Collection<Object>) attributeFieldValue : Arrays.asList((Object[]) attributeFieldValue);
+          if (isCollection || subFieldClass.isArray()) {
+            @SuppressWarnings("unchecked")
+            Collection<Object> subFieldValues = isCollection ? (Collection<Object>) attributeFieldValue : Arrays.asList((Object[]) attributeFieldValue);
 
-              for (Object subArrayFieldValue : subFieldValues) {
-                resolveAttribute(subUnresolveds, subArrayFieldValue, attribute, bulkIdKeyToOperationResult);
-              }
-            } else {
-              resolveAttribute(subUnresolveds, attributeFieldValue, attribute, bulkIdKeyToOperationResult);
+            for (Object subArrayFieldValue : subFieldValues) {
+              resolveAttribute(subUnresolveds, subArrayFieldValue, attribute, bulkIdKeyToOperationResult);
             }
+          } else {
+            resolveAttribute(subUnresolveds, attributeFieldValue, attribute, bulkIdKeyToOperationResult);
+          }
 
-            if (subUnresolveds.size() > 0) {
-              UnresolvedTopLevel unresolved = new UnresolvedTopLevelComplex(attributeField, attributeFieldValue, subUnresolveds);
+          if (subUnresolveds.size() > 0) {
+            UnresolvedTopLevel unresolved = new UnresolvedTopLevelComplex(accessor, attributeFieldValue, subUnresolveds);
 
-              attributeField.set(scimResource, null);
-              unresolvedTopLevels.add(unresolved);
-            }
+            accessor.set(scimResource, null);
+            unresolvedTopLevels.add(unresolved);
           }
         }
-      } catch (IllegalAccessException illegalAccessException) {
-        log.error("Failed to access a ScimResource ID reference field to resolve it", illegalAccessException);
       }
     }
     if (unresolvedTopLevels.size() > 0) {
@@ -781,39 +759,32 @@ public class BulkResourceImpl implements BulkResource {
 
   private static void generateReverseDependenciesGraph(Map<String, Set<String>> reverseDependenciesGraph, String dependentBulkId, Object scimObject, List<Schema.Attribute> scimObjectAttributes) {
     for (Schema.Attribute scimObjectAttribute : scimObjectAttributes)
-      try {
-        if (scimObjectAttribute.isScimResourceIdReference()) {
-          String reference = (String) scimObjectAttribute.getField()
-                                                         .get(scimObject);
-
-          if (reference != null && reference.startsWith("bulkId:")) {
-            Set<String> dependents = reverseDependenciesGraph.computeIfAbsent(reference, (unused) -> new HashSet<>());
+      if (scimObjectAttribute.isScimResourceIdReference()) {
+        String reference = scimObjectAttribute.getAccessor().get(scimObject);
 
-            dependents.add("bulkId:" + dependentBulkId);
-          }
-        } else if (scimObjectAttribute.isMultiValued()) { // all multiValueds
-                                                          // are COMPLEX, not
-                                                          // all COMPLEXES are
-                                                          // multiValued
-          Object attributeObject = scimObjectAttribute.getField()
-                                                      .get(scimObject);
-          Class<?> attributeObjectClass = attributeObject.getClass();
-          boolean isCollection = Collection.class.isAssignableFrom(attributeObjectClass);
-          Collection<?> attributeValues = isCollection ? (Collection<?>) attributeObject : Arrays.asList(attributeObject);
-          List<Schema.Attribute> subAttributes = scimObjectAttribute.getAttributes();
-
-          for (Object attributeValue : attributeValues) {
-            generateReverseDependenciesGraph(reverseDependenciesGraph, dependentBulkId, attributeValue, subAttributes);
-          }
-        } else if (scimObjectAttribute.getType() == Schema.Attribute.Type.COMPLEX) {
-          Object attributeValue = scimObjectAttribute.getField()
-                                                     .get(scimObject);
-          List<Schema.Attribute> subAttributes = scimObjectAttribute.getAttributes();
+        if (reference != null && reference.startsWith("bulkId:")) {
+          Set<String> dependents = reverseDependenciesGraph.computeIfAbsent(reference, (unused) -> new HashSet<>());
 
+          dependents.add("bulkId:" + dependentBulkId);
+        }
+      } else if (scimObjectAttribute.isMultiValued()) { // all multiValueds
+                                                        // are COMPLEX, not
+                                                        // all COMPLEXES are
+                                                        // multiValued
+        Object attributeObject = scimObjectAttribute.getAccessor().get(scimObject);
+        Class<?> attributeObjectClass = attributeObject.getClass();
+        boolean isCollection = Collection.class.isAssignableFrom(attributeObjectClass);
+        Collection<?> attributeValues = isCollection ? (Collection<?>) attributeObject : Arrays.asList(attributeObject);
+        List<Schema.Attribute> subAttributes = scimObjectAttribute.getAttributes();
+
+        for (Object attributeValue : attributeValues) {
           generateReverseDependenciesGraph(reverseDependenciesGraph, dependentBulkId, attributeValue, subAttributes);
         }
-      } catch (IllegalAccessException illegalAccessException) {
-        log.error("Resolving reverse dependencies", illegalAccessException);
+      } else if (scimObjectAttribute.getType() == Schema.Attribute.Type.COMPLEX) {
+        Object attributeValue = scimObjectAttribute.getAccessor().get(scimObject);
+        List<Schema.Attribute> subAttributes = scimObjectAttribute.getAttributes();
+
+        generateReverseDependenciesGraph(reverseDependenciesGraph, dependentBulkId, attributeValue, subAttributes);
       }
   }
 
diff --git a/scim-spec/scim-spec-schema/src/main/java/org/apache/directory/scim/spec/schema/Schema.java b/scim-spec/scim-spec-schema/src/main/java/org/apache/directory/scim/spec/schema/Schema.java
index 7844550..e13852b 100644
--- a/scim-spec/scim-spec-schema/src/main/java/org/apache/directory/scim/spec/schema/Schema.java
+++ b/scim-spec/scim-spec-schema/src/main/java/org/apache/directory/scim/spec/schema/Schema.java
@@ -19,34 +19,21 @@
 
 package org.apache.directory.scim.spec.schema;
 
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Pattern;
 import jakarta.validation.constraints.Size;
-import jakarta.xml.bind.annotation.XmlAccessType;
-import jakarta.xml.bind.annotation.XmlAccessorType;
-import jakarta.xml.bind.annotation.XmlElement;
-import jakarta.xml.bind.annotation.XmlElementWrapper;
-import jakarta.xml.bind.annotation.XmlEnum;
-import jakarta.xml.bind.annotation.XmlEnumValue;
-import jakarta.xml.bind.annotation.XmlRootElement;
-import jakarta.xml.bind.annotation.XmlType;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.directory.scim.spec.validator.Urn;
+import jakarta.xml.bind.annotation.*;
 import lombok.AccessLevel;
 import lombok.Data;
 import lombok.Getter;
 import lombok.Setter;
+import org.apache.directory.scim.spec.exception.ScimResourceInvalidException;
+import org.apache.directory.scim.spec.validator.Urn;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Field;
+import java.util.*;
 
 /**
  * Defines the structure of the SCIM schemas as defined by section 7 of the SCIM
@@ -163,8 +150,8 @@ public class Schema implements AttributeContainer {
     
     @XmlElement
     List<String> referenceTypes;
-    
-    Field field;
+
+    AttributeAccessor accessor;
 
     private boolean scimResourceIdReference;
 
@@ -256,4 +243,41 @@ public class Schema implements AttributeContainer {
     }
     return attributeNamesMap.get(name.toLowerCase());
   }
+
+  public interface AttributeAccessor {
+    <T> T get(Object resource);
+
+    void set(Object resource, Object value);
+
+    Class<?> getType();
+
+    static AttributeAccessor forField(Field field) {
+      return new AttributeAccessor() {
+        @Override
+        public <T> T get(Object resource) {
+          try {
+            field.setAccessible(true);
+            return (T) field.get(resource);
+          } catch (IllegalAccessException e) {
+            throw new ScimResourceInvalidException("Schema definition is invalid", e);
+          }
+        }
+
+        @Override
+        public void set(Object resource, Object value) {
+          try {
+            field.setAccessible(true);
+            field.set(resource, value);
+          } catch (IllegalAccessException e) {
+            throw new ScimResourceInvalidException("Schema definition is invalid", e);
+          }
+        }
+
+        @Override
+        public Class<?> getType() {
+          return field.getType();
+        }
+      };
+    }
+  }
 }