Make JAX-B API optional (#4634)

* Make JAX-B API optional

Signed-off-by: Jan Supol <jan.supol@oracle.com>
diff --git a/ext/bean-validation/pom.xml b/ext/bean-validation/pom.xml
index b33e994..30da28d 100644
--- a/ext/bean-validation/pom.xml
+++ b/ext/bean-validation/pom.xml
@@ -98,6 +98,11 @@
             </exclusions>
         </dependency>
         <dependency>
+            <groupId>jakarta.xml.bind</groupId>
+            <artifactId>jakarta.xml.bind-api</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
             <groupId>javax.enterprise</groupId>
             <artifactId>cdi-api</artifactId>
             <optional>true</optional>
diff --git a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/ValidationError.java b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/ValidationError.java
index cf2f1c4..a737399 100644
--- a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/ValidationError.java
+++ b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/ValidationError.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -25,15 +25,7 @@
  */
 @XmlRootElement
 @SuppressWarnings("UnusedDeclaration")
-public final class ValidationError {
-
-    private String message;
-
-    private String messageTemplate;
-
-    private String path;
-
-    private String invalidValue;
+public final class ValidationError extends ValidationErrorData {
 
     /**
      * Create a {@code ValidationError} instance. Constructor for JAXB providers.
@@ -50,81 +42,6 @@
      * @param invalidValue value that failed to pass constraints.
      */
     public ValidationError(final String message, final String messageTemplate, final String path, final String invalidValue) {
-        this.message = message;
-        this.messageTemplate = messageTemplate;
-        this.path = path;
-        this.invalidValue = invalidValue;
-    }
-
-    /**
-     * Return the interpolated error message for this validation error.
-     *
-     * @return the interpolated error message for this validation error.
-     */
-    public String getMessage() {
-        return message;
-    }
-
-    /**
-     * Return the interpolated error message for this validation error.
-     *
-     * @param message the interpolated error message for this validation error.
-     */
-    public void setMessage(final String message) {
-        this.message = message;
-    }
-
-    /**
-     * Return the string representation of the property path to the value.
-     *
-     * @return the string representation of the property path to the value.
-     */
-    public String getPath() {
-        return path;
-    }
-
-    /**
-     * Set the string representation of the property path to the value.
-     *
-     * @param path the string representation of the property path to the value.
-     */
-    public void setPath(final String path) {
-        this.path = path;
-    }
-
-    /**
-     * Returns the string representation of the value failing to pass the constraint.
-     *
-     * @return the value failing to pass the constraint.
-     */
-    public String getInvalidValue() {
-        return invalidValue;
-    }
-
-    /**
-     * Set the value failing to pass the constraint.
-     *
-     * @param invalidValue the value failing to pass the constraint.
-     */
-    public void setInvalidValue(final String invalidValue) {
-        this.invalidValue = invalidValue;
-    }
-
-    /**
-     * Return the non-interpolated error message for this validation error.
-     *
-     * @return the non-interpolated error message for this validation error.
-     */
-    public String getMessageTemplate() {
-        return messageTemplate;
-    }
-
-    /**
-     * Set the non-interpolated error message for this validation error.
-     *
-     * @param messageTemplate the non-interpolated error message for this validation error.
-     */
-    public void setMessageTemplate(final String messageTemplate) {
-        this.messageTemplate = messageTemplate;
+        super(message, messageTemplate, path, invalidValue);
     }
 }
diff --git a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/ValidationErrorData.java b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/ValidationErrorData.java
new file mode 100644
index 0000000..5c2abe6
--- /dev/null
+++ b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/ValidationErrorData.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.server.validation;
+
+
+import org.glassfish.jersey.internal.util.ReflectionHelper;
+
+/**
+ * Validation error entity to be included in {@code Response} if JAX-B API is not available
+ */
+@SuppressWarnings("UnusedDeclaration")
+public class ValidationErrorData {
+
+    protected String message;
+
+    protected String messageTemplate;
+
+    protected String path;
+
+    protected String invalidValue;
+
+    /* package */ ValidationErrorData() {
+    }
+
+    /**
+     * Create a {@code ValidationError} instance.
+     *
+     * @param message interpolated error message.
+     * @param messageTemplate non-interpolated error message.
+     * @param path property path.
+     * @param invalidValue value that failed to pass constraints.
+     */
+    /* package */
+    ValidationErrorData(final String message, final String messageTemplate, final String path, final String invalidValue) {
+        this.message = message;
+        this.messageTemplate = messageTemplate;
+        this.path = path;
+        this.invalidValue = invalidValue;
+    }
+
+    /**
+     * Return the interpolated error message for this validation error.
+     *
+     * @return the interpolated error message for this validation error.
+     */
+    public String getMessage() {
+        return message;
+    }
+
+    /**
+     * Return the interpolated error message for this validation error.
+     *
+     * @param message the interpolated error message for this validation error.
+     */
+    public void setMessage(final String message) {
+        this.message = message;
+    }
+
+    /**
+     * Return the string representation of the property path to the value.
+     *
+     * @return the string representation of the property path to the value.
+     */
+    public String getPath() {
+        return path;
+    }
+
+    /**
+     * Set the string representation of the property path to the value.
+     *
+     * @param path the string representation of the property path to the value.
+     */
+    public void setPath(final String path) {
+        this.path = path;
+    }
+
+    /**
+     * Returns the string representation of the value failing to pass the constraint.
+     *
+     * @return the value failing to pass the constraint.
+     */
+    public String getInvalidValue() {
+        return invalidValue;
+    }
+
+    /**
+     * Set the value failing to pass the constraint.
+     *
+     * @param invalidValue the value failing to pass the constraint.
+     */
+    public void setInvalidValue(final String invalidValue) {
+        this.invalidValue = invalidValue;
+    }
+
+    /**
+     * Return the non-interpolated error message for this validation error.
+     *
+     * @return the non-interpolated error message for this validation error.
+     */
+    public String getMessageTemplate() {
+        return messageTemplate;
+    }
+
+    /**
+     * Set the non-interpolated error message for this validation error.
+     *
+     * @param messageTemplate the non-interpolated error message for this validation error.
+     */
+    public void setMessageTemplate(final String messageTemplate) {
+        this.messageTemplate = messageTemplate;
+    }
+
+    /*
+     * Cache the information
+     */
+    private static Boolean isJaxbAvailable = null;
+
+    private static boolean isJaxbAvailable() {
+        if (isJaxbAvailable == null) {
+            isJaxbAvailable = ReflectionHelper.isJaxbAvailable();
+        }
+        return isJaxbAvailable;
+    }
+
+    /**
+     * A factory method that creates either JAX-B annotated data if JAX-B is available or POJO data otherwise.
+     * @param message interpolated error message.
+     * @param messageTemplate non-interpolated error message.
+     * @param path property path.
+     * @param invalidValue value that failed to pass constraints.
+     * @return ValidationErrorData subclass or itself
+     */
+    public static ValidationErrorData createValidationError(
+            final String message, final String messageTemplate, final String path, final String invalidValue) {
+        if (isJaxbAvailable()) {
+            return new ValidationError(message, messageTemplate, path, invalidValue);
+        } else {
+            return new ValidationErrorData(message, messageTemplate, path, invalidValue);
+        }
+    }
+}
diff --git a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationErrorMessageBodyWriter.java b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationErrorMessageBodyWriter.java
index 84e2a00..4c91fc5 100644
--- a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationErrorMessageBodyWriter.java
+++ b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationErrorMessageBodyWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -30,10 +30,10 @@
 import javax.ws.rs.ext.MessageBodyWriter;
 
 import org.glassfish.jersey.message.MessageUtils;
-import org.glassfish.jersey.server.validation.ValidationError;
+import org.glassfish.jersey.server.validation.ValidationErrorData;
 
 /**
- * {@link MessageBodyWriter} providing support for (collections of) {@link ValidationError}
+ * {@link MessageBodyWriter} providing support for (collections of) {@link ValidationErrorData}
  * that is able to output instances to {@code text/plain}/{@code text/html}.
  *
  * @author Michal Gajdos
@@ -49,10 +49,10 @@
     }
 
     private static boolean isSupportedType(final Class<?> type, final Type genericType) {
-        if (ValidationError.class.isAssignableFrom(type)) {
+        if (ValidationErrorData.class.isAssignableFrom(type)) {
             return true;
         } else if (Collection.class.isAssignableFrom(type) && (genericType instanceof ParameterizedType)) {
-            return ValidationError.class
+            return ValidationErrorData.class
                     .isAssignableFrom((Class) ((ParameterizedType) genericType).getActualTypeArguments()[0]);
         }
         return false;
@@ -79,13 +79,13 @@
                         final MediaType mediaType,
                         final MultivaluedMap<String, Object> httpHeaders,
                         final OutputStream entityStream) throws IOException, WebApplicationException {
-        final Collection<ValidationError> errors;
+        final Collection<ValidationErrorData> errors;
 
-        if (entity instanceof ValidationError) {
-            errors = Collections.singleton((ValidationError) entity);
+        if (entity instanceof ValidationErrorData) {
+            errors = Collections.singleton((ValidationErrorData) entity);
         } else {
             //noinspection unchecked
-            errors = (Collection<ValidationError>) entity;
+            errors = (Collection<ValidationErrorData>) entity;
         }
 
         final boolean isPlain = MediaType.TEXT_PLAIN_TYPE.getSubtype().equals(mediaType.getSubtype());
@@ -97,7 +97,7 @@
             builder.append("<div class=\"validation-errors\">");
         }
 
-        for (final ValidationError error : errors) {
+        for (final ValidationErrorData error : errors) {
             if (!isPlain) {
                 builder.append("<div class=\"validation-error\">");
             }
diff --git a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationExceptionMapper.java b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationExceptionMapper.java
index b569607..5e44507 100644
--- a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationExceptionMapper.java
+++ b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationExceptionMapper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -35,12 +35,12 @@
 import javax.validation.ValidationException;
 
 import org.glassfish.jersey.server.ServerProperties;
-import org.glassfish.jersey.server.validation.ValidationError;
+import org.glassfish.jersey.server.validation.ValidationErrorData;
 
 /**
  * {@link ExceptionMapper} for {@link ValidationException}.
  * <p/>
- * If {@value ServerProperties#BV_SEND_ERROR_IN_RESPONSE} property is enabled then a list of {@link ValidationError}
+ * If {@value ServerProperties#BV_SEND_ERROR_IN_RESPONSE} property is enabled then a list of {@link ValidationErrorData}
  * instances is sent in {@link Response} as well (in addition to HTTP 400/500 status code). Supported media types are:
  * {@code application/json}/{@code application/xml} (in appropriate provider is registered on server) or
  * {@code text/html}/{@code text/plain} (via custom {@link ValidationErrorMessageBodyWriter}).
@@ -85,7 +85,7 @@
                 response.entity(
                         new GenericEntity<>(
                                 ValidationHelper.constraintViolationToValidationErrors(cve),
-                                new GenericType<List<ValidationError>>() {}.getType()
+                                new GenericType<List<ValidationErrorData>>() {}.getType()
                         )
                 );
             }
diff --git a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationHelper.java b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationHelper.java
index 5e92de9..0722dd4 100644
--- a/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationHelper.java
+++ b/ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -27,7 +27,7 @@
 import javax.validation.Path;
 import javax.ws.rs.core.Response;
 
-import org.glassfish.jersey.server.validation.ValidationError;
+import org.glassfish.jersey.server.validation.ValidationErrorData;
 
 /**
  * Utility methods for Bean Validation processing.
@@ -39,13 +39,13 @@
 
     /**
      * Extract {@link ConstraintViolation constraint violations} from given exception and transform them into a list of
-     * {@link ValidationError validation errors}.
+     * {@link ValidationErrorData validation errors}.
      *
      * @param violation exception containing constraint violations.
      * @return list of validation errors (not {@code null}).
      */
-    public static List<ValidationError> constraintViolationToValidationErrors(final ConstraintViolationException violation) {
-        return violation.getConstraintViolations().stream().map(violation1 -> new ValidationError(
+    public static List<ValidationErrorData> constraintViolationToValidationErrors(final ConstraintViolationException violation) {
+        return violation.getConstraintViolations().stream().map(violation1 -> ValidationErrorData.createValidationError(
                 violation1.getMessage(),
                 violation1.getMessageTemplate(),
                 getViolationPath(violation1),