SLING-6659 detect modification to validator.id, service.ranking and/or validator.severity a run-time

Also did some major refactoring to hold references to validator services only from the ValidationServiceImpl

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1787945 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/sling/validation/impl/ResourceToValidationResultAdapterFactory.java b/src/main/java/org/apache/sling/validation/impl/ResourceToValidationResultAdapterFactory.java
index 0873a7e..c83d883 100644
--- a/src/main/java/org/apache/sling/validation/impl/ResourceToValidationResultAdapterFactory.java
+++ b/src/main/java/org/apache/sling/validation/impl/ResourceToValidationResultAdapterFactory.java
@@ -22,7 +22,6 @@
 
 import org.apache.sling.api.adapter.AdapterFactory;
 import org.apache.sling.api.resource.Resource;
-import org.apache.sling.validation.ValidationFailure;
 import org.apache.sling.validation.ValidationResult;
 import org.osgi.service.component.annotations.Component;
 import org.slf4j.Logger;
diff --git a/src/main/java/org/apache/sling/validation/impl/ValidationModelRetrieverImpl.java b/src/main/java/org/apache/sling/validation/impl/ValidationModelRetrieverImpl.java
index 4742f23..fb25a7c 100644
--- a/src/main/java/org/apache/sling/validation/impl/ValidationModelRetrieverImpl.java
+++ b/src/main/java/org/apache/sling/validation/impl/ValidationModelRetrieverImpl.java
@@ -21,9 +21,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
-import java.util.Map;
 import java.util.SortedMap;
-import java.util.concurrent.ConcurrentHashMap;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
@@ -35,12 +33,9 @@
 import org.apache.sling.api.resource.ResourceResolverFactory;
 import org.apache.sling.validation.impl.model.MergedValidationModel;
 import org.apache.sling.validation.model.ValidationModel;
-import org.apache.sling.validation.model.ValidatorAndSeverity;
 import org.apache.sling.validation.model.spi.ValidationModelProvider;
 import org.apache.sling.validation.model.spi.ValidationModelRetriever;
-import org.apache.sling.validation.spi.Validator;
 import org.osgi.framework.ServiceReference;
-import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.FieldOption;
 import org.osgi.service.component.annotations.Reference;
@@ -64,13 +59,6 @@
     @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.MULTIPLE, policyOption = ReferencePolicyOption.GREEDY, fieldOption = FieldOption.REPLACE)
     protected volatile List<ValidationModelProvider> modelProviders;
 
-    /** List of all known validators (key=id of validator) */
-    @Nonnull
-    Map<String, ValidatorAndSeverity<?>> validators = new ConcurrentHashMap<>();
-
-    @Nonnull
-    Map<String, ServiceReference<Validator<?>>> validatorServiceReferences = new ConcurrentHashMap<>();
-
     @Reference
     ResourceResolverFactory resourceResolverFactory;
 
@@ -145,7 +133,7 @@
         // lowest ranked model provider inserts first (i.e. higher ranked should overwrite)
         for (ValidationModelProvider modelProvider : modelProviders) {
             LOG.debug("Retrieving validation models with resource type {} from provider {}...", resourceType, modelProvider.getClass().getName());
-            List<ValidationModel> models = modelProvider.getModels(resourceType, validators);
+            List<ValidationModel> models = modelProvider.getModels(resourceType);
             for (ValidationModel model : models) {
                 for (String applicablePath : model.getApplicablePaths()) {
                     LOG.debug("Found validation model for resource type {} for applicable path {}", resourceType, applicablePath);
@@ -159,70 +147,4 @@
         return modelsForResourceType;
     }
 
-    @Reference(cardinality = ReferenceCardinality.MULTIPLE, policyOption = ReferencePolicyOption.GREEDY)
-    protected void addValidator(@Nonnull Validator<?> validator, Map<String, Object> properties, ServiceReference<Validator<?>> serviceReference) {
-        String validatorId = getValidatorIdFromServiceProperties(properties, validator, serviceReference);
-        Integer severity = getValidatorSeverityFromServiceProperties(properties, validator, serviceReference);
-        if (validators.containsKey(validatorId)) {
-            ServiceReference<Validator<?>> existingServiceReference = validatorServiceReferences.get(validatorId);
-            if (existingServiceReference == null) {
-                throw new IllegalStateException("Could not find service reference for validator with id " + validatorId);
-            }
-            if (serviceReference.compareTo(existingServiceReference) == 1) {
-                LOG.info("Overwriting already existing validator {} from bundle {} with validator {} from bundle {},"
-                        + " because it has the same id '{}' and a higher service ranking",
-                        validators.get(validatorId), existingServiceReference.getBundle().getBundleId(), validator,
-                        serviceReference.getBundle().getBundleId(), validatorId);
-                validators.put(validatorId, new ValidatorAndSeverity<>(validator, severity));
-                validatorServiceReferences.put(validatorId, serviceReference);
-            } else {
-                LOG.info(
-                        "A Validator for the same id '{}' is already registered with class '{}' from bundle {} and has a higher service ranking",
-                        validatorId, validators.get(validatorId), existingServiceReference.getBundle().getBundleId());
-            }
-        } else {
-            validators.put(validatorId, new ValidatorAndSeverity<>(validator, severity));
-            validatorServiceReferences.put(validatorId, serviceReference);
-        }
-    }
-
-    // no need for an unbind method for validators, as those are static, i.e. component is deactivated first
-    @Activate
-    protected void activate() {
-        LOG.info("Starting service...");
-    }
-
-    private String getValidatorIdFromServiceProperties(Map<String, Object> properties, Validator<?> validator,
-            ServiceReference<Validator<?>> serviceReference) {
-        Object id = properties.get(Validator.PROPERTY_VALIDATOR_ID);
-        if (id == null) {
-            throw new IllegalArgumentException("Validator '" + validator.getClass().getName() + "' provided from bundle "
-                    + serviceReference.getBundle().getBundleId() +
-                    " is lacking the mandatory service property " + Validator.PROPERTY_VALIDATOR_ID);
-        }
-        if (!(id instanceof String)) {
-            throw new IllegalArgumentException("Validator '" + validator.getClass().getName() + "' provided from bundle "
-                    + serviceReference.getBundle().getBundleId() +
-                    " is providing the mandatory service property " + Validator.PROPERTY_VALIDATOR_ID + " with the wrong type "
-                    + id.getClass() + " (must be of type String)");
-        }
-        return (String) id;
-    }
-    
-    private Integer getValidatorSeverityFromServiceProperties(Map<String, Object> properties, Validator<?> validator,
-            ServiceReference<Validator<?>> serviceReference) {
-        Object severity = properties.get(Validator.PROPERTY_VALIDATOR_SEVERITY);
-        if (severity == null) {
-            LOG.debug("Validator '{}' is not setting an explicit severity via the OSGi service property {}", validator.getClass().getName(), Validator.PROPERTY_VALIDATOR_SEVERITY);
-            return null;
-        }
-        if (!(severity instanceof Integer)) {
-            throw new IllegalArgumentException("Validator '" + validator.getClass().getName() + "' provided from bundle "
-                    + serviceReference.getBundle().getBundleId() +
-                    " is providing the optional service property " + Validator.PROPERTY_VALIDATOR_SEVERITY + " with the wrong type "
-                    + severity.getClass() + " (must be of type Integer)");
-        }
-        return (Integer) severity;
-    }
-
 }
diff --git a/src/main/java/org/apache/sling/validation/impl/ValidationServiceImpl.java b/src/main/java/org/apache/sling/validation/impl/ValidationServiceImpl.java
index 764a83e..edf6649 100644
--- a/src/main/java/org/apache/sling/validation/impl/ValidationServiceImpl.java
+++ b/src/main/java/org/apache/sling/validation/impl/ValidationServiceImpl.java
@@ -23,6 +23,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.ResourceBundle;
 import java.util.function.Predicate;
 import java.util.regex.Matcher;
@@ -44,7 +45,7 @@
 import org.apache.sling.validation.ValidationResult;
 import org.apache.sling.validation.ValidationService;
 import org.apache.sling.validation.model.ChildResource;
-import org.apache.sling.validation.model.ParameterizedValidator;
+import org.apache.sling.validation.model.ValidatorInvocation;
 import org.apache.sling.validation.model.ResourceProperty;
 import org.apache.sling.validation.model.ValidationModel;
 import org.apache.sling.validation.model.spi.ValidationModelRetriever;
@@ -78,6 +79,10 @@
     @Reference
     ValidationModelRetriever modelRetriever;
     
+    /** List of all known validators (key=id of validator) */
+    @Nonnull
+    ValidatorMap validatorMap;
+    
     Collection<String> searchPaths;
     
     ValidationServiceConfiguration configuration;
@@ -98,7 +103,8 @@
     private ServiceUserMapped serviceUserMapped;
 
     @Activate
-    public void activate(ValidationServiceConfiguration configuration) {
+    protected void activate(ValidationServiceConfiguration configuration) {
+        this.validatorMap = new ValidatorMap();
         this.configuration = configuration;
         ResourceResolver rr = null;
         try {
@@ -113,6 +119,28 @@
         }
     }
 
+    @Reference(cardinality = ReferenceCardinality.MULTIPLE, policyOption = ReferencePolicyOption.GREEDY, policy=ReferencePolicy.DYNAMIC)
+    protected void addValidator(@Nonnull Validator<?> validator, Map<String, Object> properties, ServiceReference<Validator<?>> serviceReference) {
+        validatorMap.put(properties, validator, serviceReference);
+    }
+
+    protected void removeValidator(@Nonnull Validator<?> validator, Map<String, Object> properties, ServiceReference<Validator<?>> serviceReference) {
+        validatorMap.remove(properties, validator, serviceReference);
+    }
+    
+    /** 
+     * Necessary to deal with property changes which do not lead to service restarts (when a modified method is provided)
+     * 
+     * @param validator
+     * @param properties
+     * @param serviceReference
+     */
+    protected void updatedValidator(@Nonnull Validator<?> validator, Map<String, Object> properties, ServiceReference<Validator<?>> serviceReference) {
+        validatorMap.update(properties, validator, serviceReference);
+    }
+
+
+
     // ValidationService ###################################################################################################################
     
     public @CheckForNull ValidationModel getValidationModel(@Nonnull String validatedResourceType, String resourcePath, boolean considerResourceSuperTypeModels) {
@@ -173,13 +201,14 @@
         throw new IllegalStateException("There is no resource provider in the system, providing a resource bundle for locale");
     }
 
-    private int getSeverityForValidator(ParameterizedValidator validator) {
-        Integer validatorSeverity = validator.getSeverity();
-        if (validatorSeverity == null) {
-            return configuration.defaultSeverity();
-        } else {
-            return validatorSeverity;
+    private int getSeverityForValidator(Integer severityFromModel, Integer severityFromValidator) {
+        if (severityFromModel != null) {
+            return severityFromModel;
         }
+        if (severityFromValidator != null) {
+            return severityFromValidator;
+        }
+        return configuration.defaultSeverity();
     }
 
     protected @Nonnull ValidationResult validate(@Nonnull Resource resource, @Nonnull ValidationModel model, @Nonnull String relativePath) {
@@ -306,7 +335,7 @@
             }
             return;
         }
-        List<ParameterizedValidator> validators = resourceProperty.getValidators();
+        List<ValidatorInvocation> validatorInvocations = resourceProperty.getValidatorInvocations();
         if (resourceProperty.isMultiple()) {
             if (!fieldValues.getClass().isArray()) {
                 result.addFailure(relativePath + property, configuration.defaultSeverity(), defaultResourceBundle, I18N_KEY_EXPECTED_MULTIVALUE_PROPERTY);
@@ -314,10 +343,16 @@
             }
         }
         
-        for (ParameterizedValidator validator : validators) {
-            int severity = getSeverityForValidator(validator);
+        for (ValidatorInvocation validatorInvocation : validatorInvocations) {
+            // lookup validator by id
+            ValidatorMap.ValidatorMetaData validatorMetaData = validatorMap.get(validatorInvocation.getValidatorId());
+            if (validatorMetaData == null) {
+                throw new IllegalStateException("Could not find validator with id '" + validatorInvocation.getValidatorId() + "'");
+            }
+            int severity = getSeverityForValidator(validatorInvocation.getSeverity(), validatorMetaData.getSeverity());
+            
             // convert the type always to an array
-            Class<?> type = validator.getType();
+            Class<?> type = validatorMetaData.getType();
             if (!type.isArray()) {
                 try {
                     // https://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#getName%28%29 has some hints on class names
@@ -331,23 +366,23 @@
             // see https://issues.apache.org/jira/browse/SLING-4178 for why the second check is necessary
             if (typedValue == null || (typedValue.length > 0 && typedValue[0] == null)) {
                 // here the missing required property case was already treated in validateValueMap
-                result.addFailure(relativePath + property, severity, defaultResourceBundle, I18N_KEY_WRONG_PROPERTY_TYPE, validator.getType());
+                result.addFailure(relativePath + property, severity, defaultResourceBundle, I18N_KEY_WRONG_PROPERTY_TYPE, validatorMetaData.getType());
                 return;
             }
             
             // see https://issues.apache.org/jira/browse/SLING-662 for a description on how multivalue properties are treated with ValueMap
-            if (validator.getType().isArray()) {
+            if (validatorMetaData.getType().isArray()) {
                 // ValueMap already returns an array in both cases (property is single value or multivalue)
-                validateValue(result, typedValue, property, relativePath, valueMap, resource, validator, defaultResourceBundle, severity);
+                validateValue(result, typedValue, property, relativePath, valueMap, resource, validatorMetaData.getValidator(), validatorInvocation.getParameters(), defaultResourceBundle, severity);
             } else {
                 // call validate for each entry in the array (supports both singlevalue and multivalue)
                 @Nonnull Object[] array = (Object[])typedValue;
                 if (array.length == 1) {
-                   validateValue(result, array[0], property, relativePath, valueMap, resource, validator, defaultResourceBundle, severity);
+                   validateValue(result, array[0], property, relativePath, valueMap, resource, validatorMetaData.getValidator(), validatorInvocation.getParameters(), defaultResourceBundle, severity);
                 } else {
                     int n = 0;
                     for (Object item : array) {
-                        validateValue(result, item, property + "[" + n++ + "]", relativePath, valueMap, resource, validator, defaultResourceBundle, severity);
+                        validateValue(result, item, property + "[" + n++ + "]", relativePath, valueMap, resource, validatorMetaData.getValidator(), validatorInvocation.getParameters(), defaultResourceBundle, severity);
                     }
                 }
             }
@@ -355,10 +390,10 @@
     }
     
     @SuppressWarnings({ "rawtypes", "unchecked" })
-    private void validateValue(CompositeValidationResult result, @Nonnull Object value, String property, String relativePath, @Nonnull ValueMap valueMap, Resource resource, ParameterizedValidator validator, @Nonnull ResourceBundle defaultResourceBundle, int severity) {
+    private void validateValue(CompositeValidationResult result, @Nonnull Object value, String property, String relativePath, @Nonnull ValueMap valueMap, Resource resource, @Nonnull Validator validator, ValueMap validatorParameters, @Nonnull ResourceBundle defaultResourceBundle, int severity) {
         try {
             ValidationContext validationContext = new ValidationContextImpl(relativePath + property, severity, valueMap, resource, defaultResourceBundle);
-            ValidationResult validatorResult = ((Validator)validator.getValidator()).validate(value, validationContext, validator.getParameters());
+            ValidationResult validatorResult = ((Validator)validator).validate(value, validationContext, validatorParameters);
             result.addValidationResult(validatorResult);
         } catch (SlingValidationException e) {
             // wrap in another SlingValidationException to include information about the property
diff --git a/src/main/java/org/apache/sling/validation/impl/ValidatorMap.java b/src/main/java/org/apache/sling/validation/impl/ValidatorMap.java
new file mode 100644
index 0000000..bc00177
--- /dev/null
+++ b/src/main/java/org/apache/sling/validation/impl/ValidatorMap.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.sling.validation.impl;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+import org.apache.sling.validation.impl.util.ValidatorTypeUtil;
+import org.apache.sling.validation.spi.Validator;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Helper class which encapsulates a map of {@link Validator}s with their meta information.
+ *
+ */
+public class ValidatorMap {
+
+    final static class ValidatorMetaData implements Comparable<ValidatorMetaData> {
+        protected final @Nonnull Validator<?> validator;
+        // default severity of the validator
+        protected final Integer severity;
+        protected final @Nonnull Class<?> type;
+        /** used for comparison, to sort by service ranking and id */
+        protected final @Nonnull ServiceReference<Validator<?>> serviceReference;
+
+        public ValidatorMetaData(Validator<?> validator, ServiceReference<Validator<?>> serviceReference, Integer severity) {
+            this.validator = validator;
+            this.severity = severity;
+            this.serviceReference = serviceReference;
+            // cache validator's type (as this is using reflection)
+            type = ValidatorTypeUtil.getValidatorType(validator);
+        }
+
+        @Override
+        public int compareTo(ValidatorMetaData o) {
+            return serviceReference.compareTo(o.serviceReference);
+        }
+
+        
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((serviceReference == null) ? 0 : serviceReference.hashCode());
+            result = prime * result + ((severity == null) ? 0 : severity.hashCode());
+            result = prime * result + ((validator == null) ? 0 : validator.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            ValidatorMetaData other = (ValidatorMetaData) obj;
+            if (serviceReference == null) {
+                if (other.serviceReference != null)
+                    return false;
+            } else if (!serviceReference.equals(other.serviceReference))
+                return false;
+            if (severity == null) {
+                if (other.severity != null)
+                    return false;
+            } else if (!severity.equals(other.severity))
+                return false;
+            if (validator == null) {
+                if (other.validator != null)
+                    return false;
+            } else if (!validator.equals(other.validator))
+                return false;
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "Entry [validator=" + validator + ", severity=" + severity + ", type=" + type + ", from bundle '" + serviceReference.getBundle().getSymbolicName() + "'"
+                    + "]";
+        }
+
+        public @Nonnull Validator<?> getValidator() {
+            return validator;
+        }
+
+        public @CheckForNull Integer getSeverity() {
+            return severity;
+        }
+
+        public @Nonnull Class<?> getType() {
+            return type;
+        }
+        
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(ValidationServiceImpl.class);
+    private final Map<String, ValidatorMetaData> validatorMap;
+    
+    public ValidatorMap() {
+        validatorMap = new ConcurrentHashMap<>();
+    }
+
+    private String getValidatorIdFromServiceProperties(Map<String, Object> properties, @SuppressWarnings("rawtypes") Class<? extends Validator> validatorClass,
+            ServiceReference<Validator<?>> serviceReference) {
+        Object id = properties.get(Validator.PROPERTY_VALIDATOR_ID);
+        if (id == null) {
+            throw new IllegalArgumentException("Validator '" + validatorClass.getName() + "' provided from bundle "
+                    + serviceReference.getBundle().getBundleId() +
+                    " is lacking the mandatory service property " + Validator.PROPERTY_VALIDATOR_ID);
+        }
+        if (!(id instanceof String)) {
+            throw new IllegalArgumentException("Validator '" + validatorClass.getName() + "' provided from bundle "
+                    + serviceReference.getBundle().getBundleId() +
+                    " is providing the mandatory service property " + Validator.PROPERTY_VALIDATOR_ID + " with the wrong type "
+                    + id.getClass() + " (must be of type String)");
+        }
+        return (String) id;
+    }
+    
+    private Integer getValidatorSeverityFromServiceProperties(Map<String, Object> properties, Validator<?> validator,
+            ServiceReference<Validator<?>> serviceReference) {
+        Object severity = properties.get(Validator.PROPERTY_VALIDATOR_SEVERITY);
+        if (severity == null) {
+            LOG.debug("Validator '{}' is not setting an explicit severity via the OSGi service property {}", validator.getClass().getName(), Validator.PROPERTY_VALIDATOR_SEVERITY);
+            return null;
+        }
+        if (!(severity instanceof Integer)) {
+            throw new IllegalArgumentException("Validator '" + validator.getClass().getName() + "' provided from bundle "
+                    + serviceReference.getBundle().getBundleId() +
+                    " is providing the optional service property " + Validator.PROPERTY_VALIDATOR_SEVERITY + " with the wrong type "
+                    + severity.getClass() + " (must be of type Integer)");
+        }
+        return (Integer) severity;
+    }
+
+    public void put(@Nonnull Map<String, Object> properties, @Nonnull Validator<?> validator, ServiceReference<Validator<?>> serviceReference) {
+        String validatorId = getValidatorIdFromServiceProperties(properties, validator.getClass(), serviceReference);
+        Integer severity = getValidatorSeverityFromServiceProperties(properties, validator, serviceReference);
+        put(validatorId, validator, serviceReference, severity);
+    }
+
+    void put(@Nonnull String id, @Nonnull Validator<?> validator, ServiceReference<Validator<?>> serviceReference, Integer severity) {
+        // create new entry
+        ValidatorMetaData entry = new ValidatorMetaData(validator, serviceReference, severity);
+        if (validatorMap.containsKey(id)) {
+            ValidatorMetaData existingEntry = validatorMap.get(id);
+            if (entry.compareTo(existingEntry) > 0) {
+                LOG.info("Overwriting already existing validator {} with {}, because it has the same id '{}' and a higher service ranking",
+                        existingEntry, entry, id);
+            } else {
+                LOG.info(
+                        "A Validator for the same id '{}' is already registered {} and it has a higher service ranking, therefore ignoring {}",
+                        id, existingEntry, entry);
+                return;
+            }
+        } else {
+            LOG.debug("New validator with id '{}' added: {}", id, entry);
+        }
+        validatorMap.put(id, entry);
+    }
+
+    public void update(@Nonnull Map<String, Object> properties, @Nonnull Validator<?> validator, ServiceReference<Validator<?>> serviceReference) {
+        String validatorId = getValidatorIdFromServiceProperties(properties, validator.getClass(), serviceReference);
+        Integer severity = getValidatorSeverityFromServiceProperties(properties, validator, serviceReference);
+        update(validatorId, validator, serviceReference, severity);
+    }
+
+    void update(@Nonnull String id, @Nonnull Validator<?> validator, ServiceReference<Validator<?>> serviceReference, Integer severity) {
+        LOG.info("Updating validator with id '{}'", id);
+        // the id might have been changed, therefore remove old entry by looking up the service reference!
+        remove(serviceReference);
+        put(id, validator, serviceReference, severity);
+    }
+
+    private boolean remove(ServiceReference<Validator<?>> serviceReference) {
+        for (java.util.Iterator<ValidatorMetaData> iterator = validatorMap.values().iterator(); iterator.hasNext();) {
+            ValidatorMetaData value = iterator.next();
+            if (value.serviceReference.equals(serviceReference)) {
+               iterator.remove();
+               return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean remove(@Nonnull Map<String, Object> properties, @Nonnull Validator<?> validator, ServiceReference<Validator<?>> serviceReference) {
+        String validatorId = getValidatorIdFromServiceProperties(properties, validator.getClass(), serviceReference);
+        return remove(validatorId, serviceReference);
+    }
+    
+    public boolean remove(String id, ServiceReference<Validator<?>> serviceReference) {
+        // only actually remove if the service reference is equal
+        if (id == null) {
+            // find by service reference
+        }
+        ValidatorMetaData entry = validatorMap.get(id);
+        if (entry == null) {
+            LOG.warn("Could not remove validator with id '{}' from map, because it is not there!", id);
+            return false;
+        } else {
+            // only actually remove if the service reference is equal
+            if (entry.serviceReference.equals(serviceReference)) {
+                return true;
+            } else {
+                LOG.warn("Could not remove validator with id '{}' from map because it is only contained with a different service reference!", id);
+                return false;
+            }
+        }
+    }
+
+    public ValidatorMetaData get(String id) {
+        return validatorMap.get(id);
+    }
+}
diff --git a/src/main/java/org/apache/sling/validation/impl/model/ParameterizedValidatorImpl.java b/src/main/java/org/apache/sling/validation/impl/model/ParameterizedValidatorImpl.java
index e51a395..1dbceb2 100644
--- a/src/main/java/org/apache/sling/validation/impl/model/ParameterizedValidatorImpl.java
+++ b/src/main/java/org/apache/sling/validation/impl/model/ParameterizedValidatorImpl.java
@@ -25,15 +25,11 @@
 
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.api.wrappers.ValueMapDecorator;
-import org.apache.sling.validation.impl.util.ValidatorTypeUtil;
-import org.apache.sling.validation.model.ParameterizedValidator;
-import org.apache.sling.validation.model.ValidatorAndSeverity;
-import org.apache.sling.validation.spi.Validator;
+import org.apache.sling.validation.model.ValidatorInvocation;
 
-public class ParameterizedValidatorImpl implements ParameterizedValidator {
-    private final @Nonnull Validator<?> validator;
+public class ParameterizedValidatorImpl implements ValidatorInvocation {
+    private final @Nonnull String id; 
     private final @Nonnull Map<String, Object> parameters;
-    private final @Nonnull Class<?> type;
     private final Integer severity;
     
     /**
@@ -43,27 +39,20 @@
      * @param parameters
      * @param severity
      */
-    public ParameterizedValidatorImpl(@Nonnull ValidatorAndSeverity<?> validator, @Nonnull Map<String, Object> parameters, Integer severity) {
+    public ParameterizedValidatorImpl(@Nonnull String id, @Nonnull Map<String, Object> parameters, Integer severity) {
         super();
-        this.validator = validator.getValidator();
+        this.id = id;
         this.parameters = parameters;
-        // cache type information as this is using reflection
-        this.type = ValidatorTypeUtil.getValidatorType(this.validator);
-        if (severity == null) {
-            this.severity = validator.getSeverity();
-        } else {
-            this.severity = severity;
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see org.apache.sling.validation.impl.ParameterizedValidator#getValidator()
-     */
-    @Override
-    public @Nonnull Validator<?> getValidator() {
-        return validator;
+        this.severity = severity;
     }
     
+
+    @Override
+    public String getValidatorId() {
+        return id;
+    }
+
+
     /* (non-Javadoc)
      * @see org.apache.sling.validation.impl.ParameterizedValidator#getParameters()
      */
@@ -71,14 +60,6 @@
     public @Nonnull ValueMap getParameters() {
         return new ValueMapDecorator(parameters);
     }
-    
-    /* (non-Javadoc)
-     * @see org.apache.sling.validation.impl.ParameterizedValidator#getType()
-     */
-    @Override
-    public @Nonnull Class<?> getType() {
-        return type;
-    }
 
     @Override
     @CheckForNull
@@ -86,23 +67,18 @@
         return severity;
     }
 
-    @Override
-    public String toString() {
-        return "ParameterizedValidatorImpl [validator=" + validator + ", parameters=" + parameters + ", type=" + type
-                + ", severity=" + severity + "]";
-    }
 
-     @Override
+    @Override
     public int hashCode() {
         final int prime = 31;
         int result = 1;
+        result = prime * result + ((id == null) ? 0 : id.hashCode());
         result = prime * result + ((parameters == null) ? 0 : parameters.hashCode());
-        result = prime * result + ((type == null) ? 0 : type.hashCode());
-        result = prime * result + ((validator == null) ? 0 : validator.hashCode());
         result = prime * result + ((severity == null) ? 0 : severity.hashCode());
         return result;
     }
 
+
     @Override
     public boolean equals(Object obj) {
         if (this == obj)
@@ -112,19 +88,28 @@
         if (getClass() != obj.getClass())
             return false;
         ParameterizedValidatorImpl other = (ParameterizedValidatorImpl) obj;
-        if (!parameters.equals(other.parameters))
+        if (id == null) {
+            if (other.id != null)
+                return false;
+        } else if (!id.equals(other.id))
             return false;
-        if (!type.equals(other.type))
+        if (parameters == null) {
+            if (other.parameters != null)
+                return false;
+        } else if (!parameters.equals(other.parameters))
             return false;
-        if (!validator.getClass().getName().equals(other.validator.getClass().getName()))
+        if (severity == null) {
+            if (other.severity != null)
+                return false;
+        } else if (!severity.equals(other.severity))
             return false;
-        if (severity == null && other.severity != null) {
-            return false;
-        } else if (severity != null && other.severity == null) {
-            return false;
-        } else if (severity != null && other.severity != null && !severity.equals(other.severity)) {
-            return false;
-        }
         return true;
     }
+
+
+    @Override
+    public String toString() {
+        return "ParameterizedValidatorImpl [id=" + id + ", parameters=" + parameters + ", severity=" + severity + "]";
+    }
+
 }
diff --git a/src/main/java/org/apache/sling/validation/impl/model/ResourcePropertyBuilder.java b/src/main/java/org/apache/sling/validation/impl/model/ResourcePropertyBuilder.java
index d4a0dbe..1c121b9 100644
--- a/src/main/java/org/apache/sling/validation/impl/model/ResourcePropertyBuilder.java
+++ b/src/main/java/org/apache/sling/validation/impl/model/ResourcePropertyBuilder.java
@@ -25,19 +25,18 @@
 
 import javax.annotation.Nonnull;
 
-import org.apache.sling.validation.model.ParameterizedValidator;
+import org.apache.sling.validation.model.ValidatorInvocation;
 import org.apache.sling.validation.model.ResourceProperty;
-import org.apache.sling.validation.model.ValidatorAndSeverity;
 
 public class ResourcePropertyBuilder {
 
     private boolean optional;
     private boolean multiple;
     private String nameRegex;
-    private final @Nonnull List<ParameterizedValidator> validators;
+    private final @Nonnull List<ValidatorInvocation> validators;
 
     public ResourcePropertyBuilder() {
-        validators = new ArrayList<ParameterizedValidator>();
+        validators = new ArrayList<ValidatorInvocation>();
         this.nameRegex = null;
         this.optional = false;
         this.multiple = false;
@@ -51,15 +50,7 @@
     /** 
      * should only be used from test classes 
      */
-    public @Nonnull ResourcePropertyBuilder validator(@Nonnull ValidatorAndSeverity<?> validator) {
-        validators.add(new ParameterizedValidatorImpl(validator, new HashMap<String, Object>(), null));
-        return this;
-    }
-
-    /** 
-     * should only be used from test classes 
-     */
-    public @Nonnull ResourcePropertyBuilder validator(@Nonnull ValidatorAndSeverity<?> validator, Integer severity, String... parametersNamesAndValues) {
+    public @Nonnull ResourcePropertyBuilder validator(@Nonnull String id, Integer severity, String... parametersNamesAndValues) {
         if (parametersNamesAndValues.length % 2 != 0) {
             throw new IllegalArgumentException("array parametersNamesAndValues must be even! (first specify name then value, separated by comma)");
         }
@@ -68,11 +59,11 @@
         for (int i=0; i<parametersNamesAndValues.length; i=i+2) {
             parameterMap.put(parametersNamesAndValues[i], parametersNamesAndValues[i+1]);
         }
-        return validator(validator, severity, parameterMap);
+        return validator(id, severity, parameterMap);
     }
     
-    public @Nonnull ResourcePropertyBuilder validator(@Nonnull ValidatorAndSeverity<?> validator, Integer severity, @Nonnull Map<String, Object> parameters) {
-        validators.add(new ParameterizedValidatorImpl(validator, parameters, severity));
+    public @Nonnull ResourcePropertyBuilder validator(@Nonnull String id, Integer severity, @Nonnull Map<String, Object> parameters) {
+        validators.add(new ParameterizedValidatorImpl(id, parameters, severity));
         return this;
     }
 
diff --git a/src/main/java/org/apache/sling/validation/impl/model/ResourcePropertyImpl.java b/src/main/java/org/apache/sling/validation/impl/model/ResourcePropertyImpl.java
index 74c5a66..e3b13a4 100644
--- a/src/main/java/org/apache/sling/validation/impl/model/ResourcePropertyImpl.java
+++ b/src/main/java/org/apache/sling/validation/impl/model/ResourcePropertyImpl.java
@@ -24,7 +24,7 @@
 
 import javax.annotation.Nonnull;
 
-import org.apache.sling.validation.model.ParameterizedValidator;
+import org.apache.sling.validation.model.ValidatorInvocation;
 import org.apache.sling.validation.model.ResourceProperty;
 
 public class ResourcePropertyImpl implements ResourceProperty {
@@ -32,7 +32,7 @@
     private final @Nonnull String name;
     private final boolean isMultiple;
     private final boolean isRequired;
-    private final @Nonnull List<ParameterizedValidator> validators;
+    private final @Nonnull List<ValidatorInvocation> validators;
     private final Pattern namePattern;
 
     /**
@@ -45,7 +45,7 @@
      * @throws IllegalArgumentException
      */
     protected ResourcePropertyImpl(@Nonnull String name, String nameRegex, boolean isMultiple, boolean isRequired,
-            @Nonnull List<ParameterizedValidator> validators) throws IllegalArgumentException {
+            @Nonnull List<ValidatorInvocation> validators) throws IllegalArgumentException {
         if (nameRegex != null) {
             try {
                 this.namePattern = Pattern.compile(nameRegex);
@@ -82,7 +82,7 @@
     }
 
     @Override
-    public @Nonnull List<ParameterizedValidator> getValidators() {
+    public @Nonnull List<ValidatorInvocation> getValidatorInvocations() {
         return validators;
     }
 
diff --git a/src/main/java/org/apache/sling/validation/impl/resourcemodel/ResourceValidationModelProviderImpl.java b/src/main/java/org/apache/sling/validation/impl/resourcemodel/ResourceValidationModelProviderImpl.java
index ff0e9d2..f931bcc 100644
--- a/src/main/java/org/apache/sling/validation/impl/resourcemodel/ResourceValidationModelProviderImpl.java
+++ b/src/main/java/org/apache/sling/validation/impl/resourcemodel/ResourceValidationModelProviderImpl.java
@@ -46,9 +46,7 @@
 import org.apache.sling.validation.model.ChildResource;
 import org.apache.sling.validation.model.ResourceProperty;
 import org.apache.sling.validation.model.ValidationModel;
-import org.apache.sling.validation.model.ValidatorAndSeverity;
 import org.apache.sling.validation.model.spi.ValidationModelProvider;
-import org.apache.sling.validation.spi.Validator;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.component.ComponentContext;
 import org.osgi.service.component.annotations.Activate;
@@ -215,14 +213,13 @@
     /*
      * (non-Javadoc)
      * 
-     * @see org.apache.sling.validation.model.spi.ValidationModelProvider#getModels(java.lang.String, java.util.Map)
+     * @see org.apache.sling.validation.model.spi.ValidationModelProvider#getModels(java.lang.String)
      */
     @Override
-    public @Nonnull List<ValidationModel> getModels(@Nonnull String relativeResourceType,
-            @Nonnull Map<String, ValidatorAndSeverity<?>> validatorsMap) {
+    public @Nonnull List<ValidationModel> getModels(@Nonnull String relativeResourceType) {
         List<ValidationModel> cacheEntry = validationModelCacheByResourceType.get(relativeResourceType);
         if (cacheEntry == null) {
-            cacheEntry = doGetModels(relativeResourceType, validatorsMap);
+            cacheEntry = doGetModels(relativeResourceType);
             validationModelCacheByResourceType.put(relativeResourceType, cacheEntry);
         } else {
             LOG.debug("Found entry in cache for resource type {}", relativeResourceType);
@@ -233,13 +230,11 @@
     /** Searches for validation models bound to a specific resource type through a search query.
      *
      * @param relativeResourceType the resource type to look for
-     * @param validatorsMap all known validators in a map (key=id of validator). Only one of those should be used in the returned validation
-     *            models.
      * @return a List of {@link ValidationModel}s. Never {@code null}, but might be empty collection in case no model for the given resource
      *         type could be found. Returns the models below "/apps" before the models below "/libs".
      * @throws IllegalStateException in case a validation model is found but it is invalid */
     @Nonnull
-    private List<ValidationModel> doGetModels(@Nonnull String relativeResourceType, @Nonnull Map<String, ValidatorAndSeverity<?>> validatorsMap) {
+    private List<ValidationModel> doGetModels(@Nonnull String relativeResourceType) {
         List<ValidationModel> validationModels = new ArrayList<ValidationModel>();
         ResourceResolver resourceResolver = null;
         try {
@@ -258,8 +253,8 @@
                         ValueMap validationModelProperties = model.getValueMap();
                         modelBuilder.addApplicablePaths(validationModelProperties.get(ResourceValidationModelProviderImpl.APPLICABLE_PATHS, new String[] {}));
                         Resource propertiesResource = model.getChild(ResourceValidationModelProviderImpl.PROPERTIES);
-                        modelBuilder.resourceProperties(buildProperties(validatorsMap, propertiesResource));
-                        modelBuilder.childResources(buildChildren(model, model, validatorsMap));
+                        modelBuilder.resourceProperties(buildProperties(propertiesResource));
+                        modelBuilder.childResources(buildChildren(model, model));
                         ValidationModel vm = modelBuilder.build(relativeResourceType, resourcePath);
                         validationModels.add(vm);
                     } catch (IllegalArgumentException e) {
@@ -286,11 +281,10 @@
 
     /** Creates a set of the properties that a resource is expected to have, together with the associated validators.
      *
-     * @param validatorsMap a map containing {@link Validator}s as values and their id's as keys
      * @param propertiesResource the resource identifying the properties node from a validation model's structure (might be {@code null})
      * @return a set of properties or an empty set if no properties are defined
      * @see ResourceProperty */
-    private @Nonnull List<ResourceProperty> buildProperties(@Nonnull Map<String, ValidatorAndSeverity<?>> validatorsMap, Resource propertiesResource) {
+    private @Nonnull List<ResourceProperty> buildProperties(@Nonnull Resource propertiesResource) {
         List<ResourceProperty> properties = new ArrayList<ResourceProperty>();
         if (propertiesResource != null) {
             for (Resource propertyResource : propertiesResource.getChildren()) {
@@ -318,10 +312,7 @@
                                     "Could not adapt resource at '" + validatorResource.getPath() + "' to ValueMap");
                         }
                         String validatorId = validatorResource.getName();
-                        ValidatorAndSeverity<?> validator = validatorsMap.get(validatorId);
-                        if (validator == null) {
-                            throw new IllegalArgumentException("Could not find validator with id '" + validatorId + "'");
-                        }
+                        
                         // get arguments for validator
                         String[] validatorArguments = validatorProperties.get(ResourceValidationModelProviderImpl.VALIDATOR_ARGUMENTS,
                                 String[].class);
@@ -357,7 +348,7 @@
                         }
                         // get severity
                         Integer severity = validatorProperties.get(SEVERITY, Integer.class);
-                        resourcePropertyBuilder.validator(validator, severity, validatorArgumentsMap);
+                        resourcePropertyBuilder.validator(validatorId, severity, validatorArgumentsMap);
                     }
                 }
                 properties.add(resourcePropertyBuilder.build(fieldName));
@@ -372,10 +363,8 @@
      * @param modelResource the resource describing a {@link org.apache.sling.validation.api.ValidationModel}
      * @param rootResource the model's resource from which to search for children (this resource has to have a
      *            {@link ResourceValidationModelProviderImpl#CHILDREN} node directly underneath it)
-     * @param validatorsMap a map containing {@link Validator}s as values and their class names as values
      * @return a list of all the children resources; the list will be empty if there are no children resources */
-    private @Nonnull List<ChildResource> buildChildren(@Nonnull Resource modelResource, @Nonnull Resource rootResource,
-            @Nonnull Map<String, ValidatorAndSeverity<?>> validatorsMap) {
+    private @Nonnull List<ChildResource> buildChildren(@Nonnull Resource modelResource, @Nonnull Resource rootResource) {
         List<ChildResource> children = new ArrayList<ChildResource>();
         Resource childrenResource = rootResource.getChild(ResourceValidationModelProviderImpl.CHILDREN);
         if (childrenResource != null) {
@@ -395,8 +384,8 @@
                 }
                 boolean isRequired = !childrenProperties.get(ResourceValidationModelProviderImpl.OPTIONAL, false);
                 ChildResource childResource = new ChildResourceImpl(name, nameRegex, isRequired,
-                        buildProperties(validatorsMap, child.getChild(ResourceValidationModelProviderImpl.PROPERTIES)),
-                        buildChildren(modelResource, child, validatorsMap));
+                        buildProperties(child.getChild(ResourceValidationModelProviderImpl.PROPERTIES)),
+                        buildChildren(modelResource, child));
                 children.add(childResource);
             }
         }
diff --git a/src/test/java/org/apache/sling/validation/impl/ValidationModelRetrieverImplTest.java b/src/test/java/org/apache/sling/validation/impl/ValidationModelRetrieverImplTest.java
index a2dcbfc..0a93d14 100644
--- a/src/test/java/org/apache/sling/validation/impl/ValidationModelRetrieverImplTest.java
+++ b/src/test/java/org/apache/sling/validation/impl/ValidationModelRetrieverImplTest.java
@@ -21,7 +21,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -36,11 +35,8 @@
 import org.apache.sling.validation.impl.model.ResourcePropertyBuilder;
 import org.apache.sling.validation.impl.model.ValidationModelBuilder;
 import org.apache.sling.validation.impl.util.ResourcePropertyNameMatcher;
-import org.apache.sling.validation.impl.util.examplevalidators.DateValidator;
-import org.apache.sling.validation.impl.util.examplevalidators.StringValidator;
 import org.apache.sling.validation.model.ResourceProperty;
 import org.apache.sling.validation.model.ValidationModel;
-import org.apache.sling.validation.model.ValidatorAndSeverity;
 import org.apache.sling.validation.model.spi.ValidationModelProvider;
 import org.apache.sling.validation.spi.Validator;
 import org.hamcrest.Matchers;
@@ -51,14 +47,11 @@
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.runners.MockitoJUnitRunner;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.ServiceReference;
 
 @RunWith(MockitoJUnitRunner.class)
 public class ValidationModelRetrieverImplTest {
 
     private ValidationModelRetrieverImpl validationModelRetriever;
-    private Validator<Date> dateValidator;
     private MultiValuedMap<String, String> applicablePathPerResourceType;
     private TestModelProvider modelProvider;
     
@@ -66,15 +59,9 @@
     private ResourceResolver resourceResolver;
     @Mock
     private ResourceResolverFactory resourceResolverFactory;
-    @Mock
-    private ServiceReference<Validator<?>> validatorServiceReference;
-    @Mock
-    private ServiceReference<Validator<?>> newValidatorServiceReference;
-    @Mock
-    private Bundle providingBundle;
-    
-    private static final String DATE_VALIDATOR_ID = "DateValidator";
 
+
+    private static final String DATE_VALIDATOR_ID = "DateValidator";
     /**
      * Test model provider which only provides models for all resource types in map applicablePathPerResourceType with their according applicablePath!
      * In addition those models have an (empty) resource property with a name equal to validated resource type.
@@ -87,12 +74,7 @@
         }
         
         @Override
-        public @Nonnull List<ValidationModel> getModels(@Nonnull String relativeResourceType,
-                @Nonnull Map<String, ValidatorAndSeverity<?>> validatorsMap) {
-            // make sure the date validator is passed along
-            Assert.assertThat(validatorsMap,
-                    Matchers.<String, ValidatorAndSeverity<?>> hasEntry(DATE_VALIDATOR_ID, new ValidatorAndSeverity<Date>(dateValidator, 1)));
-
+        public @Nonnull List<ValidationModel> getModels(@Nonnull String relativeResourceType) {
             List<ValidationModel> models = new ArrayList<ValidationModel>();
             Collection<String> applicablePaths = applicablePathPerResourceType.get(relativeResourceType);
             if (applicablePaths != null) {
@@ -111,60 +93,19 @@
 
     @Before
     public void setup() throws LoginException {
-        dateValidator =  new DateValidator();
         applicablePathPerResourceType = new ArrayListValuedHashMap<>();
         validationModelRetriever = new ValidationModelRetrieverImpl();
         modelProvider = new TestModelProvider("source1");
         validationModelRetriever.modelProviders = new ArrayList<>();
         validationModelRetriever.modelProviders.add(modelProvider);
-        Mockito.doReturn(1l).when(providingBundle).getBundleId();
-        Mockito.doReturn(providingBundle).when(validatorServiceReference).getBundle();
-        Mockito.doReturn(providingBundle).when(newValidatorServiceReference).getBundle();
+        
         Map<String, Object> validatorProperties = new HashMap<>();
         validatorProperties.put(Validator.PROPERTY_VALIDATOR_ID, DATE_VALIDATOR_ID);
         validatorProperties.put(Validator.PROPERTY_VALIDATOR_SEVERITY, 1);
-        validationModelRetriever.addValidator(dateValidator, validatorProperties, validatorServiceReference);
         validationModelRetriever.resourceResolverFactory = resourceResolverFactory;
         Mockito.when(resourceResolverFactory.getServiceResourceResolver(Mockito.anyObject())).thenReturn(resourceResolver);
     }
     
-    @Test(expected=IllegalArgumentException.class)
-    public void testAddValidatorWithoutValidatorIdProperty() {
-        Map<String, Object> validatorProperties = new HashMap<>();
-        validationModelRetriever.addValidator(dateValidator, validatorProperties, validatorServiceReference);
-    }
-    
-    @Test(expected=IllegalArgumentException.class)
-    public void testAddValidatorWithWronglyTypedValidatorId() {
-        Map<String, Object> validatorProperties = new HashMap<>();
-        validatorProperties.put(Validator.PROPERTY_VALIDATOR_ID, new String[]{"some", "value"});
-        validationModelRetriever.addValidator(dateValidator, validatorProperties, validatorServiceReference);
-    }
-
-    @Test
-    public void testAddOverloadingValidatorWithSameValidatorIdAndHigherRanking() {
-        Map<String, Object> validatorProperties = new HashMap<>();
-        validatorProperties.put(Validator.PROPERTY_VALIDATOR_ID, DATE_VALIDATOR_ID);
-        validatorProperties.put(Validator.PROPERTY_VALIDATOR_SEVERITY, 2);
-        Mockito.doReturn(1).when(newValidatorServiceReference).compareTo(Mockito.anyObject());
-        Validator<String> stringValidator = new StringValidator();
-        validationModelRetriever.addValidator(stringValidator, validatorProperties, newValidatorServiceReference);
-        Assert.assertEquals(new ValidatorAndSeverity<>(stringValidator, 2), validationModelRetriever.validators.get(DATE_VALIDATOR_ID));
-        Assert.assertEquals(newValidatorServiceReference, validationModelRetriever.validatorServiceReferences.get(DATE_VALIDATOR_ID));
-    }
-    
-    @Test
-    public void testAddOverloadingValidatorWithSameValidatorIdAndLowerRanking() {
-        Map<String, Object> validatorProperties = new HashMap<>();
-        validatorProperties.put(Validator.PROPERTY_VALIDATOR_ID, DATE_VALIDATOR_ID);
-        validatorProperties.put(Validator.PROPERTY_VALIDATOR_SEVERITY, 2);
-        Mockito.doReturn(-1).when(newValidatorServiceReference).compareTo(Mockito.anyObject());
-        Validator<String> stringValidator = new StringValidator();
-        validationModelRetriever.addValidator(stringValidator, validatorProperties, newValidatorServiceReference);
-        Assert.assertEquals(new ValidatorAndSeverity<>(dateValidator, 1), validationModelRetriever.validators.get(DATE_VALIDATOR_ID));
-        Assert.assertEquals(validatorServiceReference, validationModelRetriever.validatorServiceReferences.get(DATE_VALIDATOR_ID));
-    }
-
     @Test
     public void testGetModel() {
         applicablePathPerResourceType.put("test/type", "/content/site1");
diff --git a/src/test/java/org/apache/sling/validation/impl/ValidationServiceImplTest.java b/src/test/java/org/apache/sling/validation/impl/ValidationServiceImplTest.java
index b2198e7..232b88b 100644
--- a/src/test/java/org/apache/sling/validation/impl/ValidationServiceImplTest.java
+++ b/src/test/java/org/apache/sling/validation/impl/ValidationServiceImplTest.java
@@ -1,20 +1,20 @@
 /*
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you 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
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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
+ * 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.
+ * 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.apache.sling.validation.impl;
 
@@ -44,9 +44,9 @@
 import org.apache.sling.i18n.ResourceBundleProvider;
 import org.apache.sling.jcr.resource.JcrResourceConstants;
 import org.apache.sling.testing.mock.sling.junit.SlingContext;
+import org.apache.sling.validation.SlingValidationException;
 import org.apache.sling.validation.ValidationFailure;
 import org.apache.sling.validation.ValidationResult;
-import org.apache.sling.validation.SlingValidationException;
 import org.apache.sling.validation.impl.model.ChildResourceImpl;
 import org.apache.sling.validation.impl.model.ResourcePropertyBuilder;
 import org.apache.sling.validation.impl.model.ValidationModelBuilder;
@@ -55,7 +55,6 @@
 import org.apache.sling.validation.model.ChildResource;
 import org.apache.sling.validation.model.ResourceProperty;
 import org.apache.sling.validation.model.ValidationModel;
-import org.apache.sling.validation.model.ValidatorAndSeverity;
 import org.apache.sling.validation.model.spi.ValidationModelRetriever;
 import org.apache.sling.validation.spi.DefaultValidationFailure;
 import org.apache.sling.validation.spi.DefaultValidationResult;
@@ -70,6 +69,8 @@
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
 
 @RunWith(MockitoJUnitRunner.class)
 public class ValidationServiceImplTest {
@@ -94,27 +95,75 @@
     
     @Mock
     private ResourceBundleProvider resourceBundleProvider;
-
+    
+    @Mock
+    private ValidationModelRetriever modelRetriever;
+    private Validator<Date> dateValidator;
+    
+    @Mock
+    private ServiceReference<Validator<?>> validatorServiceReference;
+    @Mock
+    private ServiceReference<Validator<?>> newValidatorServiceReference;
+    @Mock
+    private Bundle providingBundle;
+    
+    private static final String DATE_VALIDATOR_ID = "DateValidator";
+    private static final String REGEX_VALIDATOR_ID = "RegexValidator";
+    
     @Before
     public void setUp() throws LoginException, PersistenceException, RepositoryException {
         validationService = new ValidationServiceImpl();
         validationService.searchPaths = Arrays.asList(context.resourceResolver().getSearchPath());
         validationService.configuration = configuration;
+        validationService.validatorMap = new ValidatorMap();
         Mockito.doReturn(20).when(configuration).defaultSeverity();
         validationService.resourceBundleProviders = Collections.singletonList(resourceBundleProvider);
         Mockito.doReturn(defaultResourceBundle).when(resourceBundleProvider).getResourceBundle(Mockito.anyObject());
         modelBuilder = new ValidationModelBuilder();
         propertyBuilder = new ResourcePropertyBuilder();
+        dateValidator =  new DateValidator();
+        Mockito.doReturn(1l).when(providingBundle).getBundleId();
+        Mockito.doReturn(providingBundle).when(validatorServiceReference).getBundle();
+        Mockito.doReturn(providingBundle).when(newValidatorServiceReference).getBundle();
+        validationService.validatorMap.put(DATE_VALIDATOR_ID, dateValidator, validatorServiceReference, 10);
+        validationService.validatorMap.put(REGEX_VALIDATOR_ID, new RegexValidator(), validatorServiceReference, 10);
+        validationService.modelRetriever = modelRetriever;
     }
 
     @Test
-    public void testGetValidationModel() throws Exception {
+    public void testGetValidationModelWithAbsolutePath() throws Exception {
+        // check conversion to relative resource type
+        validationService.getValidationModel("/libs/some/type", "some path", true);
+        Mockito.verify(modelRetriever).getModel("some/type", "some path", true);
+    }
 
+    @Test
+    public void testGetValidationModelWithRelativePath() throws Exception {
+        // check conversion to relative resource type
+        validationService.getValidationModel("some/type", "some path", true);
+        Mockito.verify(modelRetriever).getModel("some/type", "some path", true);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testGetValidationModelWithAbsolutePathOutsideSearchPath() throws Exception {
+        // check conversion to relative resource type
+        validationService.getValidationModel("/content/some/type", "some path", true);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testValidateWithInvalidValidatorId() throws Exception {
+        propertyBuilder.validator("invalidid", 10);
+        modelBuilder.resourceProperty(propertyBuilder.build("field1"));
+        ValidationModel vm = modelBuilder.build("sling/validation/test", "some source");
+
+        HashMap<String, Object> hashMap = new HashMap<String, Object>();
+        hashMap.put("field1", "1");
+        validationService.validate(new ValueMapDecorator(hashMap), vm);
     }
 
     @Test()
     public void testValueMapWithWrongDataType() throws Exception {
-        propertyBuilder.validator(new ValidatorAndSeverity<Date>(new DateValidator(), 10));
+        propertyBuilder.validator(DATE_VALIDATOR_ID, 10);
         modelBuilder.resourceProperty(propertyBuilder.build("field1"));
         ValidationModel vm = modelBuilder.build("sling/validation/test", "some source");
 
@@ -138,7 +187,8 @@
                 return DefaultValidationResult.VALID;
             }
         };
-        propertyBuilder.validator(new ValidatorAndSeverity<String>(myValidator, 10));
+        validationService.validatorMap.put("someId", myValidator, validatorServiceReference, 10);
+        propertyBuilder.validator("someId", 20);
         modelBuilder.resourceProperty(propertyBuilder.build("field1"));
         ValidationModel vm = modelBuilder.build("sling/validation/test", "some source");
 
@@ -182,7 +232,7 @@
     @Test()
     public void testValueMapWithEmptyOptionalValue() throws Exception {
         propertyBuilder.optional();
-        propertyBuilder.validator(new ValidatorAndSeverity<String>(new RegexValidator(), 2), null, RegexValidator.REGEX_PARAM, "abc");
+        propertyBuilder.validator(REGEX_VALIDATOR_ID, null, RegexValidator.REGEX_PARAM, "abc");
         modelBuilder.resourceProperty(propertyBuilder.build("field1"));
         ValidationModel vm = modelBuilder.build("sling/validation/test", "some source");
 
@@ -190,17 +240,17 @@
         hashMap.put("field1", "");
         ValidationResult vr = validationService.validate(new ValueMapDecorator(hashMap), vm);
 
-        Assert.assertFalse(vr.isValid()); // check for correct error message Map<String, List<String>>
-        Assert.assertThat(vr.getFailures(), Matchers.<ValidationFailure>contains(new DefaultValidationFailure("field1", 2, defaultResourceBundle, RegexValidator.I18N_KEY_PATTERN_DOES_NOT_MATCH, "abc")));
+        Assert.assertFalse(vr.isValid());
+        Assert.assertThat(vr.getFailures(), Matchers.<ValidationFailure>contains(new DefaultValidationFailure("field1", 10, defaultResourceBundle, RegexValidator.I18N_KEY_PATTERN_DOES_NOT_MATCH, "abc")));
     }
 
     @Test
     public void testValueMapWithCorrectDataType() throws Exception {
-        propertyBuilder.validator(new ValidatorAndSeverity<String>(new RegexValidator(), 2), 0, RegexValidator.REGEX_PARAM, "abc");
+        propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, "abc");
         modelBuilder.resourceProperty(propertyBuilder.build("field1"));
         propertyBuilder = new ResourcePropertyBuilder();
         final String TEST_REGEX = "^test$";
-        propertyBuilder.validator(new ValidatorAndSeverity<String>(new RegexValidator(), 2), 0, RegexValidator.REGEX_PARAM, TEST_REGEX);
+        propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, TEST_REGEX);
         modelBuilder.resourceProperty(propertyBuilder.build("field2"));
         ValidationModel vm = modelBuilder.build("sling/validation/test", "some source");
 
@@ -217,7 +267,7 @@
     // see https://issues.apache.org/jira/browse/SLING-5674
     @Test
     public void testNonExistingResource() throws Exception {
-        propertyBuilder.validator(new ValidatorAndSeverity<String>(new RegexValidator(), 2), 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits
+        propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits
         ResourceProperty property = propertyBuilder.build("field1");
         modelBuilder.resourceProperty(property);
         
@@ -241,7 +291,7 @@
     // see https://issues.apache.org/jira/browse/SLING-5749
     @Test
     public void testSyntheticResource() throws Exception {
-        propertyBuilder.validator(new ValidatorAndSeverity<String>(new RegexValidator(), 2), 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits
+        propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits
         ResourceProperty property = propertyBuilder.build("field1");
         modelBuilder.resourceProperty(property);
         
@@ -264,7 +314,7 @@
 
     @Test
     public void testResourceWithMissingGrandChildProperty() throws Exception {
-        propertyBuilder.validator(new ValidatorAndSeverity<String>(new RegexValidator(), 2), 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits
+        propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits
         ResourceProperty property = propertyBuilder.build("field1");
         modelBuilder.resourceProperty(property);
 
@@ -296,7 +346,7 @@
 
     @Test
     public void testResourceWithMissingOptionalChildResource() throws Exception {
-        propertyBuilder.validator(new ValidatorAndSeverity<String>(new RegexValidator(), 2), 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits
+        propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits
         ResourceProperty property = propertyBuilder.build("field1");
 
         ChildResource child = new ChildResourceImpl("child", null, false, Collections.singletonList(property),
@@ -316,7 +366,7 @@
 
     @Test
     public void testResourceWithNestedChildren() throws Exception {
-        propertyBuilder.validator(new ValidatorAndSeverity<String>(new RegexValidator(), 2), 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits
+        propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits
         ResourceProperty property = propertyBuilder.build("field1");
 
         ChildResource modelGrandChild = new ChildResourceImpl("grandchild", null, true,
@@ -342,7 +392,7 @@
 
     @Test
     public void testResourceWithValidatorLeveragingTheResource() throws Exception {
-        ValidatorAndSeverity<String> extendedValidator = new ValidatorAndSeverity<String>(new Validator<String>() {
+        Validator<String> extendedValidator = new Validator<String>() {
             @Override
             @Nonnull
             public ValidationResult validate(@Nonnull String data, @Nonnull ValidationContext context, @Nonnull ValueMap arguments)
@@ -356,8 +406,10 @@
                 return DefaultValidationResult.VALID;
             }
             
-        }, 0);
-        propertyBuilder.validator(extendedValidator); // accept any digits
+        };
+        // register validator
+        validationService.validatorMap.put("myid", extendedValidator, newValidatorServiceReference, null);
+        propertyBuilder.validator("myid", null); // accept any digits
         modelBuilder.resourceProperty(propertyBuilder.build("field1"));
         ValidationModel vm = modelBuilder.build("sometype", "some source");
 
@@ -373,7 +425,7 @@
 
     @Test
     public void testResourceWithNestedChildrenAndPatternMatching() throws Exception {
-        propertyBuilder.validator(new ValidatorAndSeverity<String>(new RegexValidator(), 2), 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits
+        propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits
         ResourceProperty property = propertyBuilder.build("field1");
 
         ChildResource modelGrandChild = new ChildResourceImpl("grandchild", "grandchild.*", true,
@@ -398,7 +450,7 @@
         // child2 is lacking its mandatory sub resource
         rr.create(testResource, "child2", properties);
         rr.create(testResource, "child3", null);
-        // siblingchild is not there at all (although mandatory)
+        // sibling child is not there at all (although mandatory)
 
         ValidationResult vr = validationService.validate(testResource, vm);
         Assert.assertFalse("resource should have been considered invalid", vr.isValid());
@@ -412,7 +464,7 @@
 
     @Test
     public void testResourceWithPropertyPatternMatching() throws Exception {
-        propertyBuilder.validator(new ValidatorAndSeverity<String>(new RegexValidator(), 2), 1, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits
+        propertyBuilder.validator(REGEX_VALIDATOR_ID, 1, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits
         propertyBuilder.nameRegex("field.*");
         modelBuilder.resourceProperty(propertyBuilder.build("field"));
         propertyBuilder.nameRegex("otherfield.*");
@@ -441,7 +493,7 @@
 
     @Test
     public void testResourceWithMultivalueProperties() throws Exception {
-        propertyBuilder.validator(new ValidatorAndSeverity<String>(new RegexValidator(), 2), 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits
+        propertyBuilder.validator(REGEX_VALIDATOR_ID, 0, RegexValidator.REGEX_PARAM, "\\d"); // accept any digits
         propertyBuilder.multiple();
         modelBuilder.resourceProperty(propertyBuilder.build("field"));
         ValidationModel vm = modelBuilder.build("type", "some source");
diff --git a/src/test/java/org/apache/sling/validation/impl/ValidatorMapTest.java b/src/test/java/org/apache/sling/validation/impl/ValidatorMapTest.java
new file mode 100644
index 0000000..3bbed54
--- /dev/null
+++ b/src/test/java/org/apache/sling/validation/impl/ValidatorMapTest.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.sling.validation.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.sling.validation.impl.util.examplevalidators.DateValidator;
+import org.apache.sling.validation.impl.util.examplevalidators.StringValidator;
+import org.apache.sling.validation.spi.Validator;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ValidatorMapTest {
+
+    private ValidatorMap validatorMap;
+    private DateValidator dateValidator;
+    
+    @Mock
+    private ServiceReference<Validator<?>> validatorServiceReference;
+    @Mock
+    private ServiceReference<Validator<?>> newValidatorServiceReference;
+    @Mock
+    private Bundle providingBundle;
+
+    private static final String DATE_VALIDATOR_ID = "DateValidator";
+    
+    @Before
+    public void setUp() {
+        validatorMap = new ValidatorMap();
+        dateValidator =  new DateValidator();
+        Mockito.doReturn("some name").when(providingBundle).getSymbolicName();
+        Mockito.doReturn(providingBundle).when(validatorServiceReference).getBundle();
+        Mockito.doReturn(providingBundle).when(newValidatorServiceReference).getBundle();
+        validatorMap.put(DATE_VALIDATOR_ID, dateValidator, validatorServiceReference, 10);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testPutWithoutValidatorIdProperty() {
+        Map<String, Object> validatorProperties = new HashMap<>();
+        validatorMap.put(validatorProperties, dateValidator, validatorServiceReference);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testPutWithWronglyTypedValidatorId() {
+        Map<String, Object> validatorProperties = new HashMap<>();
+        validatorProperties.put(Validator.PROPERTY_VALIDATOR_ID, new String[]{"some", "value"});
+        validatorMap.put(validatorProperties, dateValidator, validatorServiceReference);
+    }
+
+    @Test
+    public void testPutValidatorWithSameValidatorIdAndHigherRanking() {
+        Map<String, Object> validatorProperties = new HashMap<>();
+        validatorProperties.put(Validator.PROPERTY_VALIDATOR_ID, DATE_VALIDATOR_ID);
+        validatorProperties.put(Validator.PROPERTY_VALIDATOR_SEVERITY, 2);
+        Mockito.doReturn(1).when(newValidatorServiceReference).compareTo(Mockito.anyObject());
+        Validator<String> stringValidator = new StringValidator();
+        validatorMap.put(validatorProperties,stringValidator, newValidatorServiceReference);
+        Assert.assertEquals(new ValidatorMap.ValidatorMetaData(stringValidator, newValidatorServiceReference, 2), validatorMap.get(DATE_VALIDATOR_ID));
+    }
+
+    @Test
+    public void testPutValidatorWithSameValidatorIdAndLowerRanking() {
+        Map<String, Object> validatorProperties = new HashMap<>();
+        validatorProperties.put(Validator.PROPERTY_VALIDATOR_ID, DATE_VALIDATOR_ID);
+        validatorProperties.put(Validator.PROPERTY_VALIDATOR_SEVERITY, 2);
+        Mockito.doReturn(-1).when(newValidatorServiceReference).compareTo(Mockito.anyObject());
+        Validator<String> stringValidator = new StringValidator();
+        validatorMap.put(validatorProperties, stringValidator, newValidatorServiceReference);
+        Assert.assertEquals(new ValidatorMap.ValidatorMetaData(dateValidator, validatorServiceReference, 10), validatorMap.get(DATE_VALIDATOR_ID));
+    }
+
+    @Test
+    public void testUpdateChangingValidatorId() {
+        Map<String, Object> validatorProperties = new HashMap<>();
+        String newId = "newId";
+        validatorProperties.put(Validator.PROPERTY_VALIDATOR_ID, newId);
+        validatorProperties.put(Validator.PROPERTY_VALIDATOR_SEVERITY, 1);
+        Mockito.doReturn(-1).when(newValidatorServiceReference).compareTo(Mockito.anyObject());
+        validatorMap.update(validatorProperties, dateValidator, validatorServiceReference);
+        Assert.assertEquals(new ValidatorMap.ValidatorMetaData(dateValidator, validatorServiceReference, 1), validatorMap.get(newId));
+        // make sure that the old validator id is no longer in the list
+        Assert.assertNull(validatorMap.get(DATE_VALIDATOR_ID));
+    }
+}
diff --git a/src/test/java/org/apache/sling/validation/impl/resourcemodel/ResourceValidationModelProviderImplTest.java b/src/test/java/org/apache/sling/validation/impl/resourcemodel/ResourceValidationModelProviderImplTest.java
index 514d6f5..6a3fe61 100644
--- a/src/test/java/org/apache/sling/validation/impl/resourcemodel/ResourceValidationModelProviderImplTest.java
+++ b/src/test/java/org/apache/sling/validation/impl/resourcemodel/ResourceValidationModelProviderImplTest.java
@@ -54,10 +54,9 @@
 import org.apache.sling.validation.impl.model.ValidationModelBuilder;
 import org.apache.sling.validation.impl.validators.RegexValidator;
 import org.apache.sling.validation.model.ChildResource;
-import org.apache.sling.validation.model.ParameterizedValidator;
+import org.apache.sling.validation.model.ValidatorInvocation;
 import org.apache.sling.validation.model.ResourceProperty;
 import org.apache.sling.validation.model.ValidationModel;
-import org.apache.sling.validation.model.ValidatorAndSeverity;
 import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Assert;
@@ -130,7 +129,6 @@
     private ResourceResolverFactory resourceResolverFactory;
     private MockQueryResultHandler prefixBasedResultHandler;
     private Map<PrefixAndResourceType, List<Node>> validatorModelNodesPerPrefixAndResourceType;
-    private Map<String, ValidatorAndSeverity<?>> validatorMap;
     private ValidationModelBuilder modelBuilder;
 
     // extract resource type from strings like
@@ -146,14 +144,12 @@
         primaryTypeUnstructuredMap.put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
         
         modelProvider = new ResourceValidationModelProviderImpl();
-        validatorMap = new HashMap<>();
-        validatorMap.put("org.apache.sling.validation.impl.validators.RegexValidator", new ValidatorAndSeverity<String>(new RegexValidator(), 2));
 
         // one default model
         modelBuilder = new ValidationModelBuilder();
         modelBuilder.setApplicablePath("/content/site1");
         ResourcePropertyBuilder propertyBuilder = new ResourcePropertyBuilder();
-        propertyBuilder.validator(new ValidatorAndSeverity<String>(new RegexValidator(), 2), 10, RegexValidator.REGEX_PARAM, "prefix.*");
+        propertyBuilder.validator("validatorId", 10, RegexValidator.REGEX_PARAM, "prefix.*");
         ResourceProperty property = propertyBuilder.build("field1");
         modelBuilder.resourceProperty(property);
 
@@ -230,7 +226,7 @@
         createValidationModelResource(rr, libsValidatorsRoot.getPath(), "testValidationModel2", model2);
 
         // check that both models are returned
-        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test", validatorMap);
+        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test");
         Assert.assertThat(models, Matchers.containsInAnyOrder(model1, model2));
     }
 
@@ -247,7 +243,7 @@
         properties.remove("applicablePaths");
 
         // check that both models are returned
-        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test", validatorMap);
+        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test");
         Assert.assertThat(models, Matchers.containsInAnyOrder(model1));
     }
     
@@ -262,7 +258,7 @@
         createValidationModelResource(rr, libsValidatorsRoot.getPath(), "testValidationModel1", model1);
         
         // check that both models are returned
-        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test", validatorMap);
+        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test");
         Assert.assertThat(models, Matchers.containsInAnyOrder(model1));
     }
     
@@ -278,7 +274,7 @@
             createValidationModelResource(rr, contentValidatorsRoot.getPath(), "testValidationModel1", model1);
 
             // check that no model is found
-            Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test", validatorMap);
+            Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test");
             Assert.assertThat("Model was placed outside resource resolver search path but still found", models, Matchers.empty());
         } finally {
             rr.delete(contentValidatorsRoot);
@@ -300,7 +296,7 @@
         createValidationModelResource(rr, libsValidatorsRoot.getPath(), "testValidationModel1", model1);
 
         // compare both models
-        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test", validatorMap);
+        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test");
         Assert.assertThat(models, Matchers.contains(model1));
     }
 
@@ -316,7 +312,7 @@
         createValidationModelResource(rr, appsValidatorsRoot.getPath(), "testValidationModel1", model2);
 
         // only the apps model should be returned
-        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test", validatorMap);
+        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test");
         Assert.assertThat(models, Matchers.contains(model2));
     }
 
@@ -331,18 +327,7 @@
         Resource resource = createValidationModelResource(rr, libsValidatorsRoot.getPath(), "testValidationModel1", model1);
         // make created model invalid by removing the properties sub resource
         rr.delete(resource.getChild("properties"));
-        modelProvider.getModels("sling/validation/test", validatorMap);
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void testGetValidationModelsWithInvalidValidatorId() throws Exception {
-        // create one default model
-        ValidationModel model1 = modelBuilder.build("sling/validation/test", libsValidatorsRoot.getPath() + "/testValidationModel1");
-        createValidationModelResource(rr, libsValidatorsRoot.getPath(), "testValidationModel1", model1);
-
-        // clear validator map to make the referenced validator unknown
-        validatorMap.clear();
-        modelProvider.getModels("sling/validation/test", validatorMap);
+        modelProvider.getModels("sling/validation/test");
     }
 
     @Test
@@ -355,11 +340,11 @@
         validatorArguments.put("key2", "value1");
         validatorArguments.put("key3", "value1=value2");
         modelBuilder = new ValidationModelBuilder();
-        modelBuilder.resourceProperty(new ResourcePropertyBuilder().validator(new ValidatorAndSeverity<String>(new RegexValidator(), 2), 10, validatorArguments).build("field1"));
+        modelBuilder.resourceProperty(new ResourcePropertyBuilder().validator("validatorId", 10, validatorArguments).build("field1"));
         modelBuilder.addApplicablePath("content/site1");
         ValidationModel model1 = modelBuilder.build("sling/validation/test", libsValidatorsRoot.getPath() + "/testValidationModel1");
         createValidationModelResource(rr, libsValidatorsRoot.getPath(), "testValidationModel1", model1);
-        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test", validatorMap);
+        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test");
         Assert.assertThat(models, Matchers.contains(model1));
     }
 
@@ -370,10 +355,10 @@
         // create valid model first
         Resource modelResource = createValidationModelResource(rr, libsValidatorsRoot.getPath(), "testValidationModel1", model1);
         // and make parametrization of validator invalid afterwards
-        Resource validatorResource = modelResource.getChild("properties/field1/validators/" + RegexValidator.class.getName());
+        Resource validatorResource = modelResource.getChild("properties/field1/validators/validatorId");
         ModifiableValueMap validatorArguments = validatorResource.adaptTo(ModifiableValueMap.class);
         validatorArguments.put("validatorArguments", "key1"); // value without "="
-        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test", validatorMap);
+        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test");
         Assert.assertThat(models, Matchers.contains(model1));
     }
 
@@ -384,10 +369,10 @@
         // create valid model first
         Resource modelResource = createValidationModelResource(rr, libsValidatorsRoot.getPath(), "testValidationModel1", model1);
         // and make parametrization of validator invalid afterwards
-        Resource validatorResource = modelResource.getChild("properties/field1/validators/" + RegexValidator.class.getName());
+        Resource validatorResource = modelResource.getChild("properties/field1/validators/validatorId");
         ModifiableValueMap validatorArguments = validatorResource.adaptTo(ModifiableValueMap.class);
         validatorArguments.put("validatorArguments", "=value2"); // starting with "="
-        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test", validatorMap);
+        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test");
         Assert.assertThat(models, Matchers.contains(model1));
     }
 
@@ -398,10 +383,10 @@
         // create valid model first
         Resource modelResource = createValidationModelResource(rr, libsValidatorsRoot.getPath(), "testValidationModel1", model1);
         // and make parametrization of validator invalid afterwards
-        Resource validatorResource = modelResource.getChild("properties/field1/validators/" + RegexValidator.class.getName());
+        Resource validatorResource = modelResource.getChild("properties/field1/validators/validatorId");
         ModifiableValueMap validatorArguments = validatorResource.adaptTo(ModifiableValueMap.class);
         validatorArguments.put("validatorArguments", "key1="); // ending with "="
-        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test", validatorMap);
+        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test");
         Assert.assertThat(models, Matchers.contains(model1));
     }
 
@@ -416,11 +401,11 @@
         createValidationModelResource(rr, libsValidatorsRoot.getPath(), "testValidationModel1", model1);
 
         // check that both models are returned
-        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test", validatorMap);
+        Collection<ValidationModel> models = modelProvider.getModels("sling/validation/test");
         Assert.assertThat(models, Matchers.containsInAnyOrder(model1));
         
         // the 2nd time the same instance should be returned
-        Collection<ValidationModel> models2 = modelProvider.getModels("sling/validation/test", validatorMap);
+        Collection<ValidationModel> models2 = modelProvider.getModels("sling/validation/test");
         Assert.assertEquals("Due to caching both models should be actually the same instance", System.identityHashCode(models), System.identityHashCode(models2));
     }
 
@@ -485,22 +470,21 @@
                 Resource validators = ResourceUtil.getOrCreateResource(rr, propertyResource.getPath() + "/"
                         + ResourceValidationModelProviderImpl.VALIDATORS, JcrConstants.NT_UNSTRUCTURED, null, true);
                 if (validators != null) {
-                    for (ParameterizedValidator validator : property.getValidators()) {
+                    for (ValidatorInvocation validatorIncovation : property.getValidatorInvocations()) {
                         Map<String, Object> validatorProperties = new HashMap<String, Object>();
                         validatorProperties.put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
-                        ValueMap parameters = validator.getParameters();
+                        ValueMap parameters = validatorIncovation.getParameters();
                         if (!parameters.isEmpty()) {
                             // convert to right format
                             validatorProperties.put(ResourceValidationModelProviderImpl.VALIDATOR_ARGUMENTS,
                                     convertMapToJcrValidatorArguments(parameters));
                         }
-                        Integer severity = validator.getSeverity();
+                        Integer severity = validatorIncovation.getSeverity();
                         if (severity != null) {
                             validatorProperties.put(ResourceValidationModelProviderImpl.SEVERITY, severity);
                         }
-                        // TODO: get real validator id here!
                         ResourceUtil.getOrCreateResource(rr, validators.getPath() + "/"
-                                + validator.getValidator().getClass().getName(), validatorProperties, null, true);
+                                + validatorIncovation.getValidatorId(), validatorProperties, null, true);
                     }
                 }
             }