You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by jg...@apache.org on 2021/04/30 20:37:06 UTC

[tomee-jakarta] branch master updated: Add patch and new XSDs for bval-jsr

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

jgallimore pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomee-jakarta.git


The following commit(s) were added to refs/heads/master by this push:
     new dbb0c0f  Add patch and new XSDs for bval-jsr
dbb0c0f is described below

commit dbb0c0f3f9ef2075eae58ebc92c7c07002d934fa
Author: Jonathan Gallimore <jo...@jrg.me.uk>
AuthorDate: Fri Apr 30 21:35:41 2021 +0100

    Add patch and new XSDs for bval-jsr
---
 .../org/apache/bval/jsr/metadata/XmlBuilder.java   | 708 +++++++++++++++++++++
 .../bval/jsr/xml/ValidationMappingParser.java      | 167 +++++
 .../org/apache/bval/jsr/xml/ValidationParser.java  | 154 +++++
 .../META-INF/validation-configuration-3.0.xsd      |  81 +++
 .../resources/META-INF/validation-mapping-3.0.xsd  | 295 +++++++++
 5 files changed, 1405 insertions(+)

diff --git a/transform/src/patch/java/org/apache/bval/jsr/metadata/XmlBuilder.java b/transform/src/patch/java/org/apache/bval/jsr/metadata/XmlBuilder.java
new file mode 100644
index 0000000..5a03087
--- /dev/null
+++ b/transform/src/patch/java/org/apache/bval/jsr/metadata/XmlBuilder.java
@@ -0,0 +1,708 @@
+/*
+ *  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.bval.jsr.metadata;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.AnnotatedParameterizedType;
+import java.lang.reflect.AnnotatedType;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.validation.ConstraintDeclarationException;
+import javax.validation.ConstraintTarget;
+import javax.validation.Payload;
+import javax.validation.ValidationException;
+import javax.validation.groups.Default;
+import javax.xml.bind.JAXBElement;
+
+import org.apache.bval.jsr.ApacheValidatorFactory;
+import org.apache.bval.jsr.ConstraintAnnotationAttributes;
+import org.apache.bval.jsr.groups.GroupConversion;
+import org.apache.bval.jsr.util.AnnotationProxyBuilder;
+import org.apache.bval.jsr.util.ToUnmodifiable;
+import org.apache.bval.jsr.xml.AnnotationType;
+import org.apache.bval.jsr.xml.BeanType;
+import org.apache.bval.jsr.xml.ClassType;
+import org.apache.bval.jsr.xml.ConstraintMappingsType;
+import org.apache.bval.jsr.xml.ConstraintType;
+import org.apache.bval.jsr.xml.ConstructorType;
+import org.apache.bval.jsr.xml.ContainerElementTypeType;
+import org.apache.bval.jsr.xml.CrossParameterType;
+import org.apache.bval.jsr.xml.ElementType;
+import org.apache.bval.jsr.xml.FieldType;
+import org.apache.bval.jsr.xml.GetterType;
+import org.apache.bval.jsr.xml.GroupConversionType;
+import org.apache.bval.jsr.xml.GroupSequenceType;
+import org.apache.bval.jsr.xml.GroupsType;
+import org.apache.bval.jsr.xml.MappingValidator;
+import org.apache.bval.jsr.xml.MethodType;
+import org.apache.bval.jsr.xml.ParameterType;
+import org.apache.bval.jsr.xml.PayloadType;
+import org.apache.bval.jsr.xml.ReturnValueType;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Lazy;
+import org.apache.bval.util.ObjectUtils;
+import org.apache.bval.util.StringUtils;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+
+@Privilizing(@CallTo(Reflection.class))
+public class XmlBuilder {
+    //@formatter:off
+    public enum Version {
+        v10("1.0"), v11("1.1"), v20("2.0"), v30("3.0");
+        //@formatter:on
+
+        static Version of(ConstraintMappingsType constraintMappings) {
+            Validate.notNull(constraintMappings);
+            String version = constraintMappings.getVersion();
+            if (StringUtils.isBlank(version)) {
+                return v10;
+            }
+            version = version.trim();
+            for (Version candidate : values()) {
+                if (candidate.id.equals(version)) {
+                    return candidate;
+                }
+            }
+            throw new ValidationException("Unknown schema version: " + version);
+        }
+
+        private final String id;
+
+        private Version(String number) {
+            this.id = number;
+        }
+
+        public String getId() {
+            return id;
+        }
+    }
+
+    private class ForBean<T> implements MetadataBuilder.ForBean<T> {
+
+        private final BeanType descriptor;
+
+        ForBean(BeanType descriptor) {
+            super();
+            this.descriptor = Validate.notNull(descriptor, "descriptor");
+        }
+
+        Class<?> getBeanClass() {
+            return resolveClass(descriptor.getClazz());
+        }
+
+        @Override
+        public MetadataBuilder.ForClass<T> getClass(Meta<Class<T>> meta) {
+            final ClassType classType = descriptor.getClassType();
+            return classType == null ? EmptyBuilder.instance().<T> forBean().getClass(meta)
+                    : new XmlBuilder.ForClass<T>(classType);
+        }
+
+        @Override
+        public Map<String, MetadataBuilder.ForContainer<Field>> getFields(Meta<Class<T>> meta) {
+            return descriptor.getField().stream()
+                    .collect(ToUnmodifiable.map(FieldType::getName, XmlBuilder.ForField::new));
+        }
+
+        @Override
+        public Map<String, MetadataBuilder.ForContainer<Method>> getGetters(Meta<Class<T>> meta) {
+            return descriptor.getGetter().stream()
+                    .collect(ToUnmodifiable.map(GetterType::getName, XmlBuilder.ForGetter::new));
+        }
+
+        @Override
+        public Map<Signature, MetadataBuilder.ForExecutable<Constructor<? extends T>>> getConstructors(Meta<Class<T>> meta) {
+            if (!atLeast(Version.v11)) {
+                return Collections.emptyMap();
+            }
+            final Function<ConstructorType, Class<?>[]> params = ct -> ct.getParameter().stream()
+                    .map(ParameterType::getType).map(XmlBuilder.this::resolveClass).toArray(Class[]::new);
+
+            final Function<ConstructorType, Signature> signature =
+                    ct -> new Signature(meta.getHost().getName(), params.apply(ct));
+
+            return descriptor.getConstructor().stream()
+                    .collect(Collectors.toMap(signature, XmlBuilder.ForConstructor::new));
+        }
+
+        @Override
+        public Map<Signature, MetadataBuilder.ForExecutable<Method>> getMethods(Meta<Class<T>> meta) {
+            if (!atLeast(Version.v11)) {
+                return Collections.emptyMap();
+            }
+            final Function<MethodType, Class<?>[]> params = mt -> mt.getParameter().stream().map(ParameterType::getType)
+                    .map(XmlBuilder.this::resolveClass).toArray(Class[]::new);
+
+            final Function<MethodType, Signature> signature = mt -> new Signature(mt.getName(), params.apply(mt));
+
+            return descriptor.getMethod().stream().collect(Collectors.toMap(signature, XmlBuilder.ForMethod::new));
+        }
+
+        @Override
+        public final AnnotationBehavior getAnnotationBehavior() {
+            return descriptor.getIgnoreAnnotations() ? AnnotationBehavior.EXCLUDE : AnnotationBehavior.INCLUDE;
+        }
+    }
+
+    private class NonRootLevel<SELF extends NonRootLevel<SELF, D>, D> implements HasAnnotationBehavior {
+        protected final D descriptor;
+        private Lazy<Boolean> getIgnoreAnnotations;
+
+        public NonRootLevel(D descriptor) {
+            super();
+            this.descriptor = Validate.notNull(descriptor, "descriptor");
+        }
+
+        @Override
+        public final AnnotationBehavior getAnnotationBehavior() {
+            return Optional.ofNullable(getIgnoreAnnotations).map(Lazy::get)
+                    .map(b -> b.booleanValue() ? AnnotationBehavior.EXCLUDE : AnnotationBehavior.INCLUDE)
+                    .orElse(AnnotationBehavior.ABSTAIN);
+        }
+
+        @SuppressWarnings("unchecked")
+        final SELF withGetIgnoreAnnotations(Function<D, Boolean> getIgnoreAnnotations) {
+            Validate.notNull(getIgnoreAnnotations);
+            this.getIgnoreAnnotations = new Lazy<>(() -> getIgnoreAnnotations.apply(descriptor));
+            return (SELF) this;
+        }
+    }
+
+    private class ForElement<SELF extends XmlBuilder.ForElement<SELF, E, D>, E extends AnnotatedElement, D>
+            extends NonRootLevel<SELF, D> implements MetadataBuilder.ForElement<E> {
+
+        private Lazy<Annotation[]> getDeclaredConstraints;
+
+        ForElement(D descriptor) {
+            super(descriptor);
+        }
+
+        @Override
+        public final Annotation[] getDeclaredConstraints(Meta<E> meta) {
+            return lazy(getDeclaredConstraints, "getDeclaredConstraints");
+        }
+
+        final SELF withGetConstraintTypes(Function<D, List<ConstraintType>> getConstraintTypes) {
+            return withGetDeclaredConstraints(getConstraintTypes
+                    .andThen(l -> l.stream().map(XmlBuilder.this::createConstraint).toArray(Annotation[]::new)));
+        }
+
+        @SuppressWarnings("unchecked")
+        final SELF withGetDeclaredConstraints(Function<D, Annotation[]> getDeclaredConstraints) {
+            this.getDeclaredConstraints = new Lazy<>(() -> getDeclaredConstraints.apply(descriptor));
+            return (SELF) this;
+        }
+    }
+
+    private class ForClass<T> extends ForElement<ForClass<T>, Class<T>, ClassType> implements MetadataBuilder.ForClass<T> {
+
+        ForClass(ClassType descriptor) {
+            super(descriptor);
+            this.withGetConstraintTypes(ClassType::getConstraint)
+                    .withGetIgnoreAnnotations(ClassType::getIgnoreAnnotations);
+        }
+
+        @Override
+        public List<Class<?>> getGroupSequence(Meta<Class<T>> meta) {
+            final GroupSequenceType groupSequence = descriptor.getGroupSequence();
+            return groupSequence == null ? null
+                    : groupSequence.getValue().stream().map(XmlBuilder.this::resolveClass).collect(ToUnmodifiable.list());
+        }
+    }
+
+    private class ForContainer<SELF extends XmlBuilder.ForContainer<SELF, E, D>, E extends AnnotatedElement, D>
+            extends XmlBuilder.ForElement<SELF, E, D> implements MetadataBuilder.ForContainer<E> {
+
+        private Lazy<Boolean> isCascade;
+        private Lazy<Set<GroupConversion>> getGroupConversions;
+        private Lazy<List<ContainerElementTypeType>> getContainerElementTypes;
+
+        ForContainer(D descriptor) {
+            super(descriptor);
+        }
+
+        @Override
+        public boolean isCascade(Meta<E> meta) {
+            return Boolean.TRUE.equals(lazy(isCascade, "isCascade"));
+        }
+
+        @Override
+        public Set<GroupConversion> getGroupConversions(Meta<E> meta) {
+            return lazy(getGroupConversions, "getGroupConversions");
+        }
+
+        @Override
+        public Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> getContainerElementTypes(
+                Meta<E> meta) {
+            if (!atLeast(Version.v20)) {
+                return Collections.emptyMap();
+            }
+            final List<ContainerElementTypeType> elements = lazy(getContainerElementTypes, "getContainerElementTypes");
+            final AnnotatedType annotatedType = meta.getAnnotatedType();
+            final E host = meta.getHost();
+
+            if (annotatedType instanceof AnnotatedParameterizedType) {
+                final AnnotatedType[] actualTypeArguments =
+                        ((AnnotatedParameterizedType) annotatedType).getAnnotatedActualTypeArguments();
+
+                return elements.stream().collect(ToUnmodifiable.map(cet -> {
+                    Integer typeArgumentIndex = cet.getTypeArgumentIndex();
+                    if (typeArgumentIndex == null) {
+                        Exceptions.raiseIf(actualTypeArguments.length > 1, ValidationException::new,
+                                "Missing required type argument index for %s", host);
+                        typeArgumentIndex = Integer.valueOf(0);
+                    }
+                    return new ContainerElementKey(annotatedType, typeArgumentIndex);
+                }, XmlBuilder.ForContainerElementType::new));
+            }
+            if (!elements.isEmpty()) {
+                Exceptions.raise(ValidationException::new, "Illegally specified %d container element type(s) for %s",
+                        elements.size(), host);
+            }
+            return Collections.emptyMap();
+        }
+
+        @SuppressWarnings("unchecked")
+        SELF withGetValid(Function<D, String> getValid) {
+            Validate.notNull(getValid);
+            this.isCascade = new Lazy<>(() -> getValid.apply(descriptor) != null);
+            return (SELF) this;
+        }
+
+        @SuppressWarnings("unchecked")
+        SELF withGetGroupConversions(Function<D, List<GroupConversionType>> getGroupConversions) {
+            Validate.notNull(getGroupConversions);
+
+            this.getGroupConversions = new Lazy<>(() -> {
+                return getGroupConversions.apply(descriptor).stream().map(gc -> {
+                    final String from = gc.getFrom();
+                    final Class<?> source = from == null ? Default.class : resolveClass(from);
+                    final Class<?> target = resolveClass(gc.getTo());
+                    return GroupConversion.from(source).to(target);
+                }).collect(ToUnmodifiable.set());
+            });
+            return (SELF) this;
+        }
+
+        @SuppressWarnings("unchecked")
+        SELF withGetContainerElementTypes(Function<D, List<ContainerElementTypeType>> getContainerElementTypes) {
+            Validate.notNull(getContainerElementTypes);
+            this.getContainerElementTypes = new Lazy<>(() -> getContainerElementTypes.apply(descriptor));
+            return (SELF) this;
+        }
+    }
+
+    private class ForContainerElementType
+            extends ForContainer<ForContainerElementType, AnnotatedType, ContainerElementTypeType> {
+
+        ForContainerElementType(ContainerElementTypeType descriptor) {
+            super(descriptor);
+            this.withGetConstraintTypes(ContainerElementTypeType::getConstraint)
+                    .withGetValid(ContainerElementTypeType::getValid)
+                    .withGetGroupConversions(ContainerElementTypeType::getConvertGroup)
+                    .withGetContainerElementTypes(ContainerElementTypeType::getContainerElementType);
+        }
+    }
+
+    private class ForField extends XmlBuilder.ForContainer<ForField, Field, FieldType> {
+
+        ForField(FieldType descriptor) {
+            super(descriptor);
+            this.withGetIgnoreAnnotations(FieldType::getIgnoreAnnotations)
+                    .withGetConstraintTypes(FieldType::getConstraint).withGetValid(FieldType::getValid)
+                    .withGetGroupConversions(FieldType::getConvertGroup)
+                    .withGetContainerElementTypes(FieldType::getContainerElementType);
+        }
+    }
+
+    private class ForGetter extends XmlBuilder.ForContainer<ForGetter, Method, GetterType> {
+
+        ForGetter(GetterType descriptor) {
+            super(descriptor);
+            this.withGetIgnoreAnnotations(GetterType::getIgnoreAnnotations)
+                    .withGetConstraintTypes(GetterType::getConstraint).withGetValid(GetterType::getValid)
+                    .withGetGroupConversions(GetterType::getConvertGroup)
+                    .withGetContainerElementTypes(GetterType::getContainerElementType);
+        }
+    }
+
+    private abstract class ForExecutable<SELF extends ForExecutable<SELF, E, D>, E extends Executable, D>
+            extends NonRootLevel<SELF, D> implements MetadataBuilder.ForExecutable<E> {
+
+        Lazy<ReturnValueType> getReturnValue;
+        Lazy<CrossParameterType> getCrossParameter;
+        Lazy<List<ParameterType>> getParameters;
+
+        ForExecutable(D descriptor) {
+            super(descriptor);
+        }
+
+        @Override
+        public MetadataBuilder.ForElement<E> getCrossParameter(Meta<E> meta) {
+            final CrossParameterType cp = lazy(getCrossParameter, "getCrossParameter");
+            if (cp == null) {
+                return EmptyBuilder.instance().<E> forExecutable().getCrossParameter(meta);
+            }
+            return new XmlBuilder.ForCrossParameter<>(cp);
+        }
+
+        @Override
+        public MetadataBuilder.ForContainer<E> getReturnValue(Meta<E> meta) {
+            final ReturnValueType rv = lazy(getReturnValue, "getReturnValue");
+            if (rv == null) {
+                return EmptyBuilder.instance().<E> forExecutable().getReturnValue(meta);
+            }
+            return new XmlBuilder.ForReturnValue<>(rv);
+        }
+
+        @Override
+        public List<MetadataBuilder.ForContainer<Parameter>> getParameters(Meta<E> meta) {
+            return lazy(getParameters, "getParameters").stream().map(XmlBuilder.ForParameter::new)
+                    .collect(Collectors.toList());
+        }
+
+        @SuppressWarnings("unchecked")
+        SELF withGetReturnValue(Function<D, ReturnValueType> getReturnValue) {
+            Validate.notNull(getReturnValue);
+            this.getReturnValue = new Lazy<>(() -> getReturnValue.apply(descriptor));
+            return (SELF) this;
+        }
+
+        @SuppressWarnings("unchecked")
+        SELF withGetCrossParameter(Function<D, CrossParameterType> getCrossParameter) {
+            Validate.notNull(getCrossParameter);
+            this.getCrossParameter = new Lazy<>(() -> getCrossParameter.apply(descriptor));
+            return (SELF) this;
+        }
+
+        @SuppressWarnings("unchecked")
+        SELF withGetParameters(Function<D, List<ParameterType>> getParameters) {
+            Validate.notNull(getParameters);
+            this.getParameters = new Lazy<>(() -> getParameters.apply(descriptor));
+            return (SELF) this;
+        }
+    }
+
+    private class ForConstructor<T> extends ForExecutable<ForConstructor<T>, Constructor<? extends T>, ConstructorType> {
+
+        ForConstructor(ConstructorType descriptor) {
+            super(descriptor);
+            this.withGetIgnoreAnnotations(ConstructorType::getIgnoreAnnotations)
+                    .withGetReturnValue(ConstructorType::getReturnValue)
+                    .withGetCrossParameter(ConstructorType::getCrossParameter)
+                    .withGetParameters(ConstructorType::getParameter);
+        }
+    }
+
+    private class ForMethod extends ForExecutable<ForMethod, Method, MethodType> {
+
+        ForMethod(MethodType descriptor) {
+            super(descriptor);
+            this.withGetIgnoreAnnotations(MethodType::getIgnoreAnnotations)
+                    .withGetReturnValue(MethodType::getReturnValue).withGetCrossParameter(MethodType::getCrossParameter)
+                    .withGetParameters(MethodType::getParameter);
+        }
+    }
+
+    private class ForParameter extends ForContainer<ForParameter, Parameter, ParameterType> {
+
+        ForParameter(ParameterType descriptor) {
+            super(descriptor);
+            this.withGetIgnoreAnnotations(ParameterType::getIgnoreAnnotations)
+                    .withGetConstraintTypes(ParameterType::getConstraint).withGetValid(ParameterType::getValid)
+                    .withGetGroupConversions(ParameterType::getConvertGroup)
+                    .withGetContainerElementTypes(ParameterType::getContainerElementType);
+        }
+    }
+
+    private class ForCrossParameter<E extends Executable>
+            extends ForElement<ForCrossParameter<E>, E, CrossParameterType> {
+
+        ForCrossParameter(CrossParameterType descriptor) {
+            super(descriptor);
+            this.withGetIgnoreAnnotations(CrossParameterType::getIgnoreAnnotations)
+                    .withGetDeclaredConstraints(d -> d.getConstraint().stream()
+                            .map(ct -> createConstraint(ct, ConstraintTarget.PARAMETERS)).toArray(Annotation[]::new));
+        }
+    }
+
+    private class ForReturnValue<E extends Executable> extends ForContainer<ForReturnValue<E>, E, ReturnValueType> {
+
+        ForReturnValue(ReturnValueType descriptor) {
+            super(descriptor);
+            this.withGetDeclaredConstraints(d -> d.getConstraint().stream()
+                    .map(ct -> createConstraint(ct, ConstraintTarget.RETURN_VALUE)).toArray(Annotation[]::new))
+                    .withGetIgnoreAnnotations(ReturnValueType::getIgnoreAnnotations).withGetValid(ReturnValueType::getValid)
+                    .withGetGroupConversions(ReturnValueType::getConvertGroup)
+                    .withGetContainerElementTypes(ReturnValueType::getContainerElementType);
+        }
+    }
+
+    private static final <T> T lazy(Lazy<T> lazy, String name) {
+        Validate.validState(lazy != null, "%s not set", name);
+        return lazy.get();
+    }
+
+    private final ApacheValidatorFactory validatorFactory;
+    private final ConstraintMappingsType constraintMappings;
+    private final Version version;
+
+    public XmlBuilder(ApacheValidatorFactory validatorFactory, ConstraintMappingsType constraintMappings) {
+        super();
+        this.validatorFactory = Validate.notNull(validatorFactory, "validatorFactory");
+        this.constraintMappings = Validate.notNull(constraintMappings, "constraintMappings");
+        this.version = Version.of(constraintMappings);
+        new MappingValidator(constraintMappings, this::resolveClass).validateMappings();
+    }
+
+    public Map<Class<?>, MetadataBuilder.ForBean<?>> forBeans() {
+        return constraintMappings.getBean().stream().map(XmlBuilder.ForBean::new)
+                .collect(ToUnmodifiable.map(XmlBuilder.ForBean::getBeanClass, Function.identity()));
+    }
+
+    public String getDefaultPackage() {
+        return constraintMappings.getDefaultPackage();
+    }
+
+    boolean atLeast(Version v) {
+        return version.compareTo(v) >= 0;
+    }
+
+    <T> Class<T> resolveClass(String className) {
+        return loadClass(toQualifiedClassName(className));
+    }
+
+    private String toQualifiedClassName(String className) {
+        if (isQualifiedClass(className)) {
+            return className;
+        }
+        if (className.startsWith("[L") && className.endsWith(";")) {
+            return "[L" + getDefaultPackage() + "." + className.substring(2);
+        }
+        return getDefaultPackage() + "." + className;
+    }
+
+    private boolean isQualifiedClass(String clazz) {
+        return clazz.indexOf('.') >= 0;
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T> Class<T> loadClass(final String fqn) {
+        ClassLoader loader = Reflection.loaderFromThreadOrClass(XmlBuilder.class);
+        if (loader == null) {
+            loader = getClass().getClassLoader();
+        }
+        try {
+            return (Class<T>) Class.forName(fqn, true, loader);
+        } catch (ClassNotFoundException ex) {
+            throw Exceptions.create(ValidationException::new, ex, "Unable to load class: %d", fqn);
+        }
+    }
+
+    private Class<?>[] loadClasses(Supplier<Stream<String>> classNames) {
+        return streamClasses(classNames).toArray(Class[]::new);
+    }
+
+    private Stream<Class<?>> streamClasses(Supplier<Stream<String>> classNames) {
+        return classNames.get().map(this::loadClass);
+    }
+
+    private <A extends Annotation, T> A createConstraint(final ConstraintType constraint) {
+        return createConstraint(constraint, ConstraintTarget.IMPLICIT);
+    }
+
+    private <A extends Annotation, T> A createConstraint(final ConstraintType constraint, ConstraintTarget target) {
+        final Class<A> annotationClass = this.<A> loadClass(toQualifiedClassName(constraint.getAnnotation()));
+        final AnnotationProxyBuilder<A> annoBuilder =
+                validatorFactory.getAnnotationsManager().buildProxyFor(annotationClass);
+
+        if (constraint.getMessage() != null) {
+            annoBuilder.setMessage(constraint.getMessage());
+        }
+        annoBuilder.setGroups(getGroups(constraint.getGroups()));
+        annoBuilder.setPayload(getPayload(constraint.getPayload()));
+
+        if (ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO.analyze(annotationClass).isValid()) {
+            annoBuilder.setValidationAppliesTo(target);
+        }
+        for (final ElementType elementType : constraint.getElement()) {
+            final String name = elementType.getName();
+            final Class<?> returnType = getAnnotationParameterType(annotationClass, name);
+            final Object elementValue = getElementValue(elementType, returnType);
+            annoBuilder.setValue(name, elementValue);
+        }
+        return annoBuilder.createAnnotation();
+    }
+
+    private <A extends Annotation> Class<?> getAnnotationParameterType(final Class<A> annotationClass,
+                                                                       final String name) {
+        final Method m = Reflection.getPublicMethod(annotationClass, name);
+        Exceptions.raiseIf(m == null, ValidationException::new,
+                "Annotation of type %s does not contain a parameter %s.", annotationClass.getName(), name);
+        return m.getReturnType();
+    }
+
+    private Object getElementValue(ElementType elementType, Class<?> returnType) {
+        removeEmptyContentElements(elementType);
+
+        final List<Serializable> content = elementType.getContent();
+        final int sz = content.size();
+        if (returnType.isArray()) {
+            final Object result = Array.newInstance(returnType.getComponentType(), sz);
+            for (int i = 0; i < sz; i++) {
+                Array.set(result, i, getSingleValue(content.get(i), returnType.getComponentType()));
+            }
+            return result;
+        }
+        Exceptions.raiseIf(sz != 1, ValidationException::new,
+                "Attempt to specify an array where single value is expected.");
+
+        return getSingleValue(content.get(0), returnType);
+    }
+
+    private void removeEmptyContentElements(ElementType elementType) {
+        for (Iterator<Serializable> iter = elementType.getContent().iterator(); iter.hasNext();) {
+            final Serializable content = iter.next();
+            if (content instanceof String && ((String) content).matches("[\\n ].*")) {
+                iter.remove();
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private Object getSingleValue(Serializable serializable, Class<?> returnType) {
+        if (serializable instanceof String) {
+            return convertToResultType(returnType, (String) serializable);
+        }
+        if (serializable instanceof JAXBElement<?>) {
+            final JAXBElement<?> elem = (JAXBElement<?>) serializable;
+            if (String.class.equals(elem.getDeclaredType())) {
+                return convertToResultType(returnType, (String) elem.getValue());
+            }
+            if (AnnotationType.class.equals(elem.getDeclaredType())) {
+                AnnotationType annotationType = (AnnotationType) elem.getValue();
+                try {
+                    return createAnnotation(annotationType, (Class<? extends Annotation>) returnType);
+                } catch (ClassCastException e) {
+                    throw new ValidationException("Unexpected parameter value");
+                }
+            }
+        }
+        throw new ValidationException("Unexpected parameter value");
+    }
+
+    private Object convertToResultType(Class<?> returnType, String value) {
+        /**
+         * Class is represented by the fully qualified class name of the class. spec: Note that if the raw string is
+         * unqualified, default package is taken into account.
+         */
+        if (String.class.equals(returnType)) {
+            return value;
+        }
+        if (Class.class.equals(returnType)) {
+            return resolveClass(value);
+        }
+        if (returnType.isEnum()) {
+            try {
+                @SuppressWarnings({ "rawtypes", "unchecked" })
+                final Enum e = Enum.valueOf(returnType.asSubclass(Enum.class), value);
+                return e;
+            } catch (IllegalArgumentException e) {
+                throw new ConstraintDeclarationException(e);
+            }
+        }
+        try {
+            if (Byte.class.equals(returnType) || byte.class.equals(returnType)) {
+                // spec mandates it:
+                return Byte.parseByte(value);
+            }
+            if (Short.class.equals(returnType) || short.class.equals(returnType)) {
+                return Short.parseShort(value);
+            }
+            if (Integer.class.equals(returnType) || int.class.equals(returnType)) {
+                return Integer.parseInt(value);
+            }
+            if (Long.class.equals(returnType) || long.class.equals(returnType)) {
+                return Long.parseLong(value);
+            }
+            if (Float.class.equals(returnType) || float.class.equals(returnType)) {
+                return Float.parseFloat(value);
+            }
+            if (Double.class.equals(returnType) || double.class.equals(returnType)) {
+                return Double.parseDouble(value);
+            }
+            if (Boolean.class.equals(returnType) || boolean.class.equals(returnType)) {
+                return Boolean.parseBoolean(value);
+            }
+        } catch (Exception e) {
+            Exceptions.raise(ValidationException::new, e, "Unable to coerce value '%s' to %s", value, returnType);
+        }
+        if (Character.class.equals(returnType) || char.class.equals(returnType)) {
+            Exceptions.raiseIf(value.length() > 1, ConstraintDeclarationException::new,
+                    "a char must have a length of 1");
+            return value.charAt(0);
+        }
+        return Exceptions.raise(ValidationException::new, "Unknown annotation value type %s", returnType.getName());
+    }
+
+    private <A extends Annotation> Annotation createAnnotation(AnnotationType annotationType, Class<A> returnType) {
+        final AnnotationProxyBuilder<A> metaAnnotation =
+                validatorFactory.getAnnotationsManager().buildProxyFor(returnType);
+        for (ElementType elementType : annotationType.getElement()) {
+            final String name = elementType.getName();
+            metaAnnotation.setValue(name, getElementValue(elementType, getAnnotationParameterType(returnType, name)));
+        }
+        return metaAnnotation.createAnnotation();
+    }
+
+    private Class<?>[] getGroups(GroupsType groupsType) {
+        if (groupsType == null) {
+            return ObjectUtils.EMPTY_CLASS_ARRAY;
+        }
+        return loadClasses(groupsType.getValue()::stream);
+    }
+
+    @SuppressWarnings("unchecked")
+    private Class<? extends Payload>[] getPayload(PayloadType payloadType) {
+        if (payloadType == null) {
+            return (Class<? extends Payload>[]) ObjectUtils.EMPTY_CLASS_ARRAY;
+        }
+        return streamClasses(payloadType.getValue()::stream).peek(pc -> {
+            Exceptions.raiseUnless(Payload.class.isAssignableFrom(pc), ConstraintDeclarationException::new,
+                    "Specified payload class %s does not implement %s", pc.getName(), Payload.class.getName());
+        }).<Class<? extends Payload>> map(pc -> pc.asSubclass(Payload.class)).toArray(Class[]::new);
+    }
+}
diff --git a/transform/src/patch/java/org/apache/bval/jsr/xml/ValidationMappingParser.java b/transform/src/patch/java/org/apache/bval/jsr/xml/ValidationMappingParser.java
new file mode 100644
index 0000000..33076b4
--- /dev/null
+++ b/transform/src/patch/java/org/apache/bval/jsr/xml/ValidationMappingParser.java
@@ -0,0 +1,167 @@
+/*
+ *  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.bval.jsr.xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import javax.validation.ValidationException;
+import javax.validation.ValidatorFactory;
+import javax.validation.spi.ConfigurationState;
+
+import org.apache.bval.jsr.ApacheValidatorFactory;
+import org.apache.bval.jsr.metadata.MetadataBuilder;
+import org.apache.bval.jsr.metadata.MetadataBuilder.ForBean;
+import org.apache.bval.jsr.metadata.MetadataSource;
+import org.apache.bval.jsr.metadata.ValidatorMappingProvider;
+import org.apache.bval.jsr.metadata.XmlBuilder;
+import org.apache.bval.jsr.metadata.XmlValidationMappingProvider;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+import org.xml.sax.InputSource;
+
+/**
+ * Uses JAXB to parse constraints.xml based on the validation-mapping XML schema.
+ */
+@Privilizing(@CallTo(Reflection.class))
+public class ValidationMappingParser implements MetadataSource {
+    private static final SchemaManager SCHEMA_MANAGER = new SchemaManager.Builder()
+            .add(XmlBuilder.Version.v10.getId(), "http://jboss.org/xml/ns/javax/validation/mapping",
+                    "META-INF/validation-mapping-1.0.xsd")
+            .add(XmlBuilder.Version.v11.getId(), "http://jboss.org/xml/ns/javax/validation/mapping",
+                    "META-INF/validation-mapping-1.1.xsd")
+            .add(XmlBuilder.Version.v20.getId(), "http://xmlns.jcp.org/xml/ns/validation/mapping",
+                    "META-INF/validation-mapping-2.0.xsd")
+            .add(XmlBuilder.Version.v30.getId(), "https://jakarta.ee/xml/ns/validation/mapping",
+                    "META-INF/validation-mapping-2.0.xsd")
+            .build();
+
+    private ApacheValidatorFactory validatorFactory;
+
+    @Override
+    public void initialize(ApacheValidatorFactory validatorFactory) {
+        this.validatorFactory = Validate.notNull(validatorFactory);
+    }
+
+    @Override
+    public void process(ConfigurationState configurationState,
+                        Consumer<ValidatorMappingProvider> addValidatorMappingProvider, BiConsumer<Class<?>, ForBean<?>> addBuilder) {
+        Validate.validState(validatorFactory != null, "validatorFactory unknown");
+
+        if (configurationState.isIgnoreXmlConfiguration()) {
+            return;
+        }
+        final Set<Class<?>> beanTypes = new HashSet<>();
+        for (final InputStream xmlStream : configurationState.getMappingStreams()) {
+            final ConstraintMappingsType mapping = parseXmlMappings(xmlStream);
+
+            Optional.of(mapping).map(this::toMappingProvider).ifPresent(addValidatorMappingProvider);
+
+            final Map<Class<?>, MetadataBuilder.ForBean<?>> builders =
+                    new XmlBuilder(validatorFactory, mapping).forBeans();
+            if (Collections.disjoint(beanTypes, builders.keySet())) {
+                builders.forEach(addBuilder::accept);
+                beanTypes.addAll(builders.keySet());
+            } else {
+                Exceptions.raise(ValidationException::new,
+                        builders.keySet().stream().filter(beanTypes::contains).map(Class::getName).collect(Collectors
+                                .joining("bean classes specified multiple times for XML validation mapping: [", "; ", "]")));
+            }
+        }
+    }
+
+    /**
+     * @param in
+     *            XML stream to parse using the validation-mapping-1.0.xsd
+     */
+    private ConstraintMappingsType parseXmlMappings(final InputStream in) {
+        try {
+            return SCHEMA_MANAGER.unmarshal(new InputSource(in), ConstraintMappingsType.class);
+        } catch (Exception e) {
+            throw new ValidationException("Failed to parse XML deployment descriptor file.", e);
+        } finally {
+            try {
+                in.reset(); // can be read several times + we ensured it was
+                // re-readable in addMapping()
+            } catch (final IOException e) {
+                // no-op
+            }
+        }
+    }
+
+    private ValidatorMappingProvider toMappingProvider(ConstraintMappingsType mapping) {
+        if (mapping.getConstraintDefinition().isEmpty()) {
+            return null;
+        }
+        final Map<Class<? extends Annotation>, ValidatedByType> validatorMappings = new HashMap<>();
+
+        for (ConstraintDefinitionType constraintDefinition : mapping.getConstraintDefinition()) {
+            final String annotationClassName = constraintDefinition.getAnnotation();
+
+            final Class<?> clazz = loadClass(annotationClassName, mapping.getDefaultPackage());
+
+            Exceptions.raiseUnless(clazz.isAnnotation(), ValidationException::new, "%s is not an annotation",
+                    annotationClassName);
+
+            final Class<? extends Annotation> annotationClass = clazz.asSubclass(Annotation.class);
+
+            Exceptions.raiseIf(validatorMappings.containsKey(annotationClass), ValidationException::new,
+                    "XML constraint validator(s) for %s already configured.", annotationClass);
+
+            validatorMappings.put(annotationClass, constraintDefinition.getValidatedBy());
+        }
+        return new XmlValidationMappingProvider(validatorMappings,
+                cn -> toQualifiedClassName(cn, mapping.getDefaultPackage()));
+    }
+
+    private Class<?> loadClass(String className, String defaultPackage) {
+        final String fqn = toQualifiedClassName(className, defaultPackage);
+        try {
+            return Reflection.toClass(fqn, Reflection.loaderFromThreadOrClass(ValidationMappingParser.class));
+        } catch (ClassNotFoundException ex) {
+            throw Exceptions.create(ValidationException::new, ex, "Unable to load class: %s", fqn);
+        }
+    }
+
+    private String toQualifiedClassName(String className, String defaultPackage) {
+        if (!isQualifiedClass(className)) {
+            if (className.startsWith("[L") && className.endsWith(";")) {
+                className = "[L" + defaultPackage + '.' + className.substring(2);
+            } else {
+                className = defaultPackage + '.' + className;
+            }
+        }
+        return className;
+    }
+
+    private boolean isQualifiedClass(String clazz) {
+        return clazz.indexOf('.') >= 0;
+    }
+}
diff --git a/transform/src/patch/java/org/apache/bval/jsr/xml/ValidationParser.java b/transform/src/patch/java/org/apache/bval/jsr/xml/ValidationParser.java
new file mode 100644
index 0000000..f0dfaa4
--- /dev/null
+++ b/transform/src/patch/java/org/apache/bval/jsr/xml/ValidationParser.java
@@ -0,0 +1,154 @@
+/*
+ * 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.bval.jsr.xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import javax.validation.BootstrapConfiguration;
+import javax.validation.ValidationException;
+import javax.validation.executable.ExecutableType;
+
+import org.apache.bval.jsr.BootstrapConfigurationImpl;
+import org.apache.bval.jsr.ConfigurationImpl;
+import org.apache.bval.jsr.metadata.XmlBuilder;
+import org.apache.bval.util.Exceptions;
+import org.apache.bval.util.Validate;
+import org.apache.bval.util.reflection.Reflection;
+import org.apache.commons.weaver.privilizer.Privileged;
+import org.apache.commons.weaver.privilizer.Privilizing;
+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
+import org.xml.sax.InputSource;
+
+/**
+ * Description: uses jaxb to parse validation.xml<br/>
+ */
+@Privilizing(@CallTo(Reflection.class))
+public class ValidationParser {
+
+    private static final String DEFAULT_VALIDATION_XML_FILE = "META-INF/validation.xml";
+    private static final Logger log = Logger.getLogger(ValidationParser.class.getName());
+
+    private static final SchemaManager SCHEMA_MANAGER = new SchemaManager.Builder()
+            .add(XmlBuilder.Version.v10.getId(), "http://jboss.org/xml/ns/javax/validation/configuration",
+                    "META-INF/validation-configuration-1.0.xsd")
+            .add(XmlBuilder.Version.v11.getId(), "http://jboss.org/xml/ns/javax/validation/configuration",
+                    "META-INF/validation-configuration-1.1.xsd")
+            .add(XmlBuilder.Version.v20.getId(), "http://xmlns.jcp.org/xml/ns/validation/configuration",
+                    "META-INF/validation-configuration-2.0.xsd")
+            .add(XmlBuilder.Version.v30.getId(), "https://jakarta.ee/xml/ns/validation/configuration",
+                    "META-INF/validation-configuration-2.0.xsd")
+            .build();
+
+    private static String getValidationXmlFile(String file) {
+        return file == null ? DEFAULT_VALIDATION_XML_FILE : file;
+    }
+
+    private static Map<String, String> toMap(final List<PropertyType> property) {
+        return property == null || property.isEmpty() ? Collections.emptyMap()
+                : property.stream().collect(Collectors.toMap(PropertyType::getName, PropertyType::getValue));
+    }
+
+    private final ClassLoader loader;
+
+    public ValidationParser(ClassLoader loader) {
+        this.loader = Validate.notNull(loader, null);
+    }
+
+    public BootstrapConfiguration processValidationConfig(final String file,
+                                                          final ConfigurationImpl targetConfig) {
+        final ValidationConfigType xmlConfig = parseXmlConfig(file);
+        if (xmlConfig == null) {
+            return null;
+        }
+        final boolean executableValidationEnabled;
+        final Set<ExecutableType> defaultValidatedExecutableTypes;
+
+        if (xmlConfig.getExecutableValidation() == null) {
+            defaultValidatedExecutableTypes = EnumSet.of(ExecutableType.IMPLICIT);
+            executableValidationEnabled = true;
+        } else {
+            final Optional<ExecutableValidationType> executableValidation =
+                    Optional.of(xmlConfig).map(ValidationConfigType::getExecutableValidation);
+            executableValidationEnabled = executableValidation.map(ExecutableValidationType::getEnabled)
+                    .filter(Predicate.isEqual(Boolean.TRUE)).isPresent();
+
+            defaultValidatedExecutableTypes = executableValidation.filter(x -> executableValidationEnabled)
+                    .map(ExecutableValidationType::getDefaultValidatedExecutableTypes)
+                    .map(DefaultValidatedExecutableTypesType::getExecutableType).map(EnumSet::copyOf)
+                    .orElse(EnumSet.noneOf(ExecutableType.class));
+        }
+        return new BootstrapConfigurationImpl(xmlConfig.getDefaultProvider(), xmlConfig.getConstraintValidatorFactory(),
+                xmlConfig.getMessageInterpolator(), xmlConfig.getTraversableResolver(),
+                xmlConfig.getParameterNameProvider(), new HashSet<>(xmlConfig.getConstraintMapping()),
+                executableValidationEnabled, defaultValidatedExecutableTypes, toMap(xmlConfig.getProperty()),
+                xmlConfig.getClockProvider(), new HashSet<>(xmlConfig.getValueExtractor()));
+    }
+
+    public InputStream open(String mappingFileName) {
+        if (mappingFileName.charAt(0) == '/') {
+            // Classloader needs a path without a starting /
+            mappingFileName = mappingFileName.substring(1);
+        }
+        try {
+            final InputStream in = getInputStream(mappingFileName);
+            Exceptions.raiseIf(in == null, ValidationException::new,
+                    "Unable to open input stream for mapping file %s", mappingFileName);
+            return(in);
+        } catch (IOException e) {
+            throw Exceptions.create(ValidationException::new, e, "Unable to open input stream for mapping file %s",
+                    mappingFileName);
+        }
+    }
+
+    InputStream getInputStream(final String path) throws IOException {
+        final List<URL> urls = Collections.list(loader.getResources(path));
+        Exceptions.raiseIf(urls.stream().distinct().count() > 1, ValidationException::new,
+                "More than one %s is found in the classpath", path);
+        return urls.isEmpty() ? null : urls.get(0).openStream();
+    }
+
+    @Privileged
+    private ValidationConfigType parseXmlConfig(final String validationXmlFile) {
+        try (InputStream inputStream = getInputStream(getValidationXmlFile(validationXmlFile))) {
+            if (inputStream == null) {
+                log.log(Level.FINEST,
+                        String.format("No %s found. Using annotation based configuration only.", validationXmlFile));
+                return null;
+            }
+            log.log(Level.FINEST, String.format("%s found.", validationXmlFile));
+
+            return SCHEMA_MANAGER.unmarshal(new InputSource(inputStream), ValidationConfigType.class);
+        } catch (Exception e) {
+            throw Exceptions.create(ValidationException::new, e, "Unable to parse %s", validationXmlFile);
+        }
+    }
+}
diff --git a/transform/src/patch/resources/META-INF/validation-configuration-3.0.xsd b/transform/src/patch/resources/META-INF/validation-configuration-3.0.xsd
new file mode 100644
index 0000000..abce03e
--- /dev/null
+++ b/transform/src/patch/resources/META-INF/validation-configuration-3.0.xsd
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+-->
+<xs:schema attributeFormDefault="unqualified"
+           elementFormDefault="qualified"
+           targetNamespace="https://jakarta.ee/xml/ns/validation/configuration"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           xmlns:config="https://jakarta.ee/xml/ns/validation/configuration"
+           version="3.0">
+
+    <xs:element name="validation-config" type="config:validation-configType"/>
+    <xs:complexType name="validation-configType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="default-provider" minOccurs="0"/>
+            <xs:element type="xs:string" name="message-interpolator" minOccurs="0"/>
+            <xs:element type="xs:string" name="traversable-resolver" minOccurs="0"/>
+            <xs:element type="xs:string" name="constraint-validator-factory" minOccurs="0"/>
+            <xs:element type="xs:string" name="parameter-name-provider" minOccurs="0"/>
+            <xs:element type="xs:string" name="clock-provider" minOccurs="0"/>
+            <xs:element type="xs:string" name="value-extractor" maxOccurs="unbounded"
+                        minOccurs="0"/>
+            <xs:element type="config:executable-validationType" name="executable-validation"
+                        minOccurs="0"/>
+            <xs:element type="xs:string" name="constraint-mapping" maxOccurs="unbounded"
+                        minOccurs="0"/>
+            <xs:element type="config:propertyType" name="property" maxOccurs="unbounded"
+                        minOccurs="0"/>
+        </xs:sequence>
+        <xs:attribute name="version" type="config:versionType" fixed="3.0" use="required"/>
+    </xs:complexType>
+
+    <xs:complexType name="executable-validationType">
+        <xs:sequence>
+            <xs:element type="config:default-validated-executable-typesType"
+                        name="default-validated-executable-types" minOccurs="0"/>
+        </xs:sequence>
+        <xs:attribute name="enabled" use="optional" type="xs:boolean" default="true"/>
+    </xs:complexType>
+    <xs:complexType name="default-validated-executable-typesType">
+        <xs:sequence>
+            <xs:element name="executable-type" maxOccurs="unbounded" minOccurs="1">
+                <xs:simpleType>
+                    <xs:restriction base="xs:string">
+                        <xs:enumeration value="NONE"/>
+                        <xs:enumeration value="CONSTRUCTORS"/>
+                        <xs:enumeration value="NON_GETTER_METHODS"/>
+                        <xs:enumeration value="GETTER_METHODS"/>
+                        <xs:enumeration value="ALL"/>
+                    </xs:restriction>
+                </xs:simpleType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="propertyType">
+        <xs:simpleContent>
+            <xs:extension base="xs:string">
+                <xs:attribute name="name" use="required" type="xs:string"/>
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+    <xs:simpleType name="versionType">
+        <xs:restriction base="xs:token">
+            <xs:pattern value="[0-9]+(\.[0-9]+)*"/>
+        </xs:restriction>
+    </xs:simpleType>
+</xs:schema>
\ No newline at end of file
diff --git a/transform/src/patch/resources/META-INF/validation-mapping-3.0.xsd b/transform/src/patch/resources/META-INF/validation-mapping-3.0.xsd
new file mode 100644
index 0000000..38991ec
--- /dev/null
+++ b/transform/src/patch/resources/META-INF/validation-mapping-3.0.xsd
@@ -0,0 +1,295 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+-->
+<xs:schema attributeFormDefault="unqualified"
+           elementFormDefault="qualified"
+           targetNamespace="https://jakarta.ee/xml/ns/validation/mapping"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           xmlns:map="https://jakarta.ee/xml/ns/validation/mapping"
+           version="3.0">
+
+    <xs:element name="constraint-mappings" type="map:constraint-mappingsType"/>
+
+    <xs:complexType name="payloadType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="groupsType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="groupSequenceType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="groupConversionType">
+        <xs:attribute type="xs:string" name="from" use="optional"/>
+        <xs:attribute type="xs:string" name="to" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="constraint-mappingsType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="default-package" minOccurs="0"/>
+            <xs:element type="map:beanType"
+                        name="bean"
+                        maxOccurs="unbounded"
+                        minOccurs="0"/>
+            <xs:element type="map:constraint-definitionType"
+                        name="constraint-definition"
+                        maxOccurs="unbounded"
+                        minOccurs="0"/>
+        </xs:sequence>
+        <xs:attribute name="version" type="map:versionType" fixed="3.0" use="required"/>
+    </xs:complexType>
+    <xs:simpleType name="versionType">
+        <xs:restriction base="xs:token">
+            <xs:pattern value="[0-9]+(\.[0-9]+)*"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:complexType name="validated-byType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+        </xs:sequence>
+        <xs:attribute type="xs:boolean" name="include-existing-validators" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="constraintType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="message" minOccurs="0"/>
+            <xs:element type="map:groupsType"
+                        name="groups"
+                        minOccurs="0"/>
+            <xs:element type="map:payloadType"
+                        name="payload"
+                        minOccurs="0"/>
+            <xs:element type="map:elementType"
+                        name="element"
+                        maxOccurs="unbounded"
+                        minOccurs="0"/>
+        </xs:sequence>
+        <xs:attribute type="xs:string" name="annotation" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="elementType" mixed="true">
+        <xs:sequence>
+            <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+            <xs:element type="map:annotationType"
+                        name="annotation"
+                        maxOccurs="unbounded"
+                        minOccurs="0"/>
+        </xs:sequence>
+        <xs:attribute type="xs:string" name="name" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="containerElementTypeType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="valid" minOccurs="0" fixed=""/>
+            <xs:element type="map:groupConversionType"
+                        name="convert-group"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+            <xs:element type="map:containerElementTypeType"
+                        name="container-element-type"
+                        maxOccurs="unbounded"
+                        minOccurs="0"/>
+            <xs:element type="map:constraintType"
+                        name="constraint"
+                        maxOccurs="unbounded"
+                        minOccurs="0"/>
+        </xs:sequence>
+        <xs:attribute name="type-argument-index" use="optional">
+            <xs:simpleType>
+                <xs:restriction base="xs:int">
+                    <xs:minInclusive value="0" />
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+    </xs:complexType>
+    <xs:complexType name="classType">
+        <xs:sequence>
+            <xs:element type="map:groupSequenceType"
+                        name="group-sequence"
+                        minOccurs="0"/>
+            <xs:element type="map:constraintType"
+                        name="constraint"
+                        maxOccurs="unbounded"
+                        minOccurs="0"/>
+        </xs:sequence>
+        <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="beanType">
+        <xs:sequence>
+            <xs:element type="map:classType"
+                        name="class"
+                        minOccurs="0">
+            </xs:element>
+            <xs:element type="map:fieldType"
+                        name="field"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+            <xs:element type="map:getterType"
+                        name="getter"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+            <xs:element type="map:constructorType"
+                        name="constructor"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+            <xs:element type="map:methodType"
+                        name="method"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute type="xs:string" name="class" use="required"/>
+        <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"
+                      default="true"/>
+    </xs:complexType>
+    <xs:complexType name="annotationType">
+        <xs:sequence>
+            <xs:element type="map:elementType"
+                        name="element"
+                        maxOccurs="unbounded"
+                        minOccurs="0"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="getterType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="valid" minOccurs="0" fixed=""/>
+            <xs:element type="map:groupConversionType"
+                        name="convert-group"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+            <xs:element type="map:containerElementTypeType"
+                        name="container-element-type"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+            <xs:element type="map:constraintType"
+                        name="constraint"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute type="xs:string" name="name" use="required"/>
+        <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="methodType">
+        <xs:sequence>
+            <xs:element type="map:parameterType"
+                        name="parameter"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+            <xs:element type="map:crossParameterType"
+                        name="cross-parameter"
+                        minOccurs="0"
+                        maxOccurs="1"/>
+            <xs:element type="map:returnValueType"
+                        name="return-value"
+                        minOccurs="0"
+                        maxOccurs="1"/>
+        </xs:sequence>
+        <xs:attribute type="xs:string" name="name" use="required"/>
+        <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="constructorType">
+        <xs:sequence>
+            <xs:element type="map:parameterType"
+                        name="parameter"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+            <xs:element type="map:crossParameterType"
+                        name="cross-parameter"
+                        minOccurs="0"
+                        maxOccurs="1"/>
+            <xs:element type="map:returnValueType"
+                        name="return-value"
+                        minOccurs="0"
+                        maxOccurs="1"/>
+        </xs:sequence>
+        <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="parameterType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="valid" minOccurs="0" fixed=""/>
+            <xs:element type="map:groupConversionType"
+                        name="convert-group"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+            <xs:element type="map:containerElementTypeType"
+                        name="container-element-type"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+            <xs:element type="map:constraintType"
+                        name="constraint"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute type="xs:string" name="type" use="required"/>
+        <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="returnValueType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="valid" minOccurs="0" fixed=""/>
+            <xs:element type="map:groupConversionType"
+                        name="convert-group"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+            <xs:element type="map:containerElementTypeType"
+                        name="container-element-type"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+            <xs:element type="map:constraintType"
+                        name="constraint"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="crossParameterType">
+        <xs:sequence>
+            <xs:element type="map:constraintType"
+                        name="constraint"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="constraint-definitionType">
+        <xs:sequence>
+            <xs:element type="map:validated-byType"
+                        name="validated-by"/>
+        </xs:sequence>
+        <xs:attribute type="xs:string" name="annotation" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="fieldType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="valid" minOccurs="0" fixed=""/>
+            <xs:element type="map:groupConversionType"
+                        name="convert-group"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+            <xs:element type="map:containerElementTypeType"
+                        name="container-element-type"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+            <xs:element type="map:constraintType"
+                        name="constraint"
+                        minOccurs="0"
+                        maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute type="xs:string" name="name" use="required"/>
+        <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+    </xs:complexType>
+</xs:schema>
\ No newline at end of file