configuredClockProvider = Arc.container().instance(ClockProvider.class);
+ if (configuredClockProvider.isAvailable()) {
+ configuration.clockProvider(configuredClockProvider.get());
+ }
+
+ // Automatically add all the values extractors declared as beans
+ for (ValueExtractor> valueExtractor : Arc.container().beanManager().createInstance()
+ .select(ValueExtractor.class)) {
+ configuration.addValueExtractor(valueExtractor);
+ }
+
+ ValidatorFactory validatorFactory = configuration.buildValidatorFactory();
+ ValidatorHolder.initialize(validatorFactory);
+
+ // Close the ValidatorFactory on shutdown
+ shutdownContext.addShutdownTask(validatorFactory::close);
+ };
+ }
+
+}
diff --git a/formalist/runtime/src/main/java/org/pagan/formalist/TraverseAllTraversableResolver.java b/formalist/runtime/src/main/java/org/pagan/formalist/TraverseAllTraversableResolver.java
new file mode 100644
index 0000000..a716dbf
--- /dev/null
+++ b/formalist/runtime/src/main/java/org/pagan/formalist/TraverseAllTraversableResolver.java
@@ -0,0 +1,25 @@
+package org.pagan.formalist;
+
+import java.lang.annotation.ElementType;
+
+import javax.validation.Path;
+import javax.validation.Path.Node;
+import javax.validation.TraversableResolver;
+
+class TraverseAllTraversableResolver implements TraversableResolver {
+
+ TraverseAllTraversableResolver() {
+ }
+
+ @Override
+ public boolean isReachable(Object traversableObject, Node traversableProperty, Class> rootBeanType,
+ Path pathToTraversableObject, ElementType elementType) {
+ return true;
+ }
+
+ @Override
+ public boolean isCascadable(Object traversableObject, Node traversableProperty, Class> rootBeanType,
+ Path pathToTraversableObject, ElementType elementType) {
+ return true;
+ }
+}
diff --git a/formalist/runtime/src/main/java/org/pagan/formalist/ValidatorHolder.java b/formalist/runtime/src/main/java/org/pagan/formalist/ValidatorHolder.java
new file mode 100644
index 0000000..795588c
--- /dev/null
+++ b/formalist/runtime/src/main/java/org/pagan/formalist/ValidatorHolder.java
@@ -0,0 +1,24 @@
+package org.pagan.formalist;
+
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+
+public class ValidatorHolder {
+
+ private static ValidatorFactory validatorFactory;
+
+ private static Validator validator;
+
+ static void initialize(ValidatorFactory validatorFactory) {
+ ValidatorHolder.validatorFactory = validatorFactory;
+ ValidatorHolder.validator = validatorFactory.getValidator();
+ }
+
+ static ValidatorFactory getValidatorFactory() {
+ return validatorFactory;
+ }
+
+ static Validator getValidator() {
+ return validator;
+ }
+}
diff --git a/formalist/runtime/src/main/java/org/pagan/formalist/ValidatorProvider.java b/formalist/runtime/src/main/java/org/pagan/formalist/ValidatorProvider.java
new file mode 100644
index 0000000..e6c5cf7
--- /dev/null
+++ b/formalist/runtime/src/main/java/org/pagan/formalist/ValidatorProvider.java
@@ -0,0 +1,22 @@
+package org.pagan.formalist;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.Produces;
+import javax.inject.Named;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+
+@ApplicationScoped
+public class ValidatorProvider {
+
+ @Produces
+ @Named("quarkus-formalist-validator-factory")
+ public ValidatorFactory factory() {
+ return ValidatorHolder.getValidatorFactory();
+ }
+
+ @Produces
+ public Validator validator() {
+ return ValidatorHolder.getValidator();
+ }
+}
diff --git a/formalist/runtime/src/main/java/org/pagan/formalist/annotataions/JaxrsEndPointValidated.java b/formalist/runtime/src/main/java/org/pagan/formalist/annotataions/JaxrsEndPointValidated.java
new file mode 100644
index 0000000..7e81d2b
--- /dev/null
+++ b/formalist/runtime/src/main/java/org/pagan/formalist/annotataions/JaxrsEndPointValidated.java
@@ -0,0 +1,20 @@
+package org.pagan.formalist.annotataions;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.interceptor.InterceptorBinding;
+
+/**
+ * Marker class to indicate a JAX-RS end point should be validated.
+ */
+@Inherited
+@InterceptorBinding
+@Retention(value = RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.METHOD })
+public @interface JaxrsEndPointValidated {
+
+}
diff --git a/formalist/runtime/src/main/java/org/pagan/formalist/annotataions/MethodValidated.java b/formalist/runtime/src/main/java/org/pagan/formalist/annotataions/MethodValidated.java
new file mode 100644
index 0000000..3366fac
--- /dev/null
+++ b/formalist/runtime/src/main/java/org/pagan/formalist/annotataions/MethodValidated.java
@@ -0,0 +1,20 @@
+package org.pagan.formalist.annotataions;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.interceptor.InterceptorBinding;
+
+/**
+ * Marker class to indicate a method should be validated.
+ */
+@Inherited
+@InterceptorBinding
+@Retention(value = RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.METHOD })
+public @interface MethodValidated {
+
+}
diff --git a/formalist/runtime/src/main/java/org/pagan/formalist/interceptor/AbstractMethodValidationInterceptor.java b/formalist/runtime/src/main/java/org/pagan/formalist/interceptor/AbstractMethodValidationInterceptor.java
new file mode 100644
index 0000000..ba844c4
--- /dev/null
+++ b/formalist/runtime/src/main/java/org/pagan/formalist/interceptor/AbstractMethodValidationInterceptor.java
@@ -0,0 +1,159 @@
+package org.pagan.formalist.interceptor;
+
+import java.io.Serializable;
+import java.lang.reflect.Member;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.interceptor.InvocationContext;
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.ElementKind;
+import javax.validation.Path;
+import javax.validation.Validator;
+import javax.validation.executable.ExecutableValidator;
+
+/**
+ * NOTE: this is a copy of the interceptor present in //. For now, I prefer not
+ * depending on this artifact but this might change in the future.
+ *
+ * An interceptor which performs a validation of the Bean Validation constraints
+ * specified at the parameters and/or return values of intercepted methods using
+ * the method validation functionality provided by Hibernate Validator.
+ *
+ * @author Gunnar Morling
+ * @author Hardy Ferentschik
+ */
+public abstract class AbstractMethodValidationInterceptor implements Serializable {
+
+ /**
+ * The validator to be used for method validation.
+ *
+ * Although the concrete validator is not necessarily serializable (and HV's
+ * implementation indeed isn't) it is still alright to have it as non-transient
+ * field here. Upon passivation not the validator itself will be serialized, but
+ * the proxy injected here, which in turn is serializable.
+ *
+ */
+ @Inject
+ Validator validator;
+
+ /**
+ * Validates the Bean Validation constraints specified at the parameters and/or
+ * return value of the intercepted method.
+ *
+ * @param ctx The context of the intercepted method invocation.
+ * @param customValidator Custom Validator
+ *
+ * @return The result of the method invocation.
+ *
+ * @throws Exception Any exception caused by the intercepted method invocation.
+ * A {@link ConstraintViolationException} in case at least one
+ * constraint violation occurred either during parameter or
+ * return value validation.
+ */
+ protected Object validateMethodInvocation(InvocationContext ctx) throws Exception {
+
+ ExecutableValidator executableValidator = validator.forExecutables();
+ Set> violations = executableValidator.validateParameters(ctx.getTarget(),
+ ctx.getMethod(), ctx.getParameters());
+
+ if (!violations.isEmpty()) {
+ throw new ConstraintViolationException(getMessage(ctx.getMethod(), ctx.getParameters(), violations),
+ violations);
+ }
+
+ Object result = ctx.proceed();
+
+ violations = executableValidator.validateReturnValue(ctx.getTarget(), ctx.getMethod(), result);
+
+ if (!violations.isEmpty()) {
+ throw new ConstraintViolationException(getMessage(ctx.getMethod(), ctx.getParameters(), violations),
+ violations);
+ }
+
+ return result;
+ }
+
+ /**
+ * Validates the Bean Validation constraints specified at the parameters and/or
+ * return value of the intercepted constructor.
+ *
+ * @param ctx The context of the intercepted constructor invocation.
+ *
+ * @throws Exception Any exception caused by the intercepted constructor
+ * invocation. A {@link ConstraintViolationException} in case
+ * at least one constraint violation occurred either during
+ * parameter or return value validation.
+ */
+ protected void validateConstructorInvocation(InvocationContext ctx) throws Exception {
+ ExecutableValidator executableValidator = validator.forExecutables();
+ Set extends ConstraintViolation>> violations = executableValidator
+ .validateConstructorParameters(ctx.getConstructor(), ctx.getParameters());
+
+ if (!violations.isEmpty()) {
+ throw new ConstraintViolationException(getMessage(ctx.getConstructor(), ctx.getParameters(), violations),
+ violations);
+ }
+
+ ctx.proceed();
+ Object createdObject = ctx.getTarget();
+
+ violations = validator.forExecutables().validateConstructorReturnValue(ctx.getConstructor(), createdObject);
+
+ if (!violations.isEmpty()) {
+ throw new ConstraintViolationException(getMessage(ctx.getConstructor(), ctx.getParameters(), violations),
+ violations);
+ }
+ }
+
+ private String getMessage(Member member, Object[] args, Set extends ConstraintViolation>> violations) {
+
+ StringBuilder message = new StringBuilder();
+ message.append(violations.size());
+ message.append(" constraint violation(s) occurred during method validation.");
+ message.append("\nConstructor or Method: ");
+ message.append(member);
+ message.append("\nArgument values: ");
+ message.append(Arrays.toString(args));
+ message.append("\nConstraint violations: ");
+
+ int i = 1;
+ for (ConstraintViolation> constraintViolation : violations) {
+ Path.Node leafNode = getLeafNode(constraintViolation);
+
+ message.append("\n (");
+ message.append(i);
+ message.append(")");
+ message.append(" Kind: ");
+ message.append(leafNode.getKind());
+ if (leafNode.getKind() == ElementKind.PARAMETER) {
+ message.append("\n parameter index: ");
+ message.append(leafNode.as(Path.ParameterNode.class).getParameterIndex());
+ }
+ message.append("\n message: ");
+ message.append(constraintViolation.getMessage());
+ message.append("\n root bean: ");
+ message.append(constraintViolation.getRootBean());
+ message.append("\n property path: ");
+ message.append(constraintViolation.getPropertyPath());
+ message.append("\n constraint: ");
+ message.append(constraintViolation.getConstraintDescriptor().getAnnotation());
+
+ i++;
+ }
+
+ return message.toString();
+ }
+
+ private Path.Node getLeafNode(ConstraintViolation> constraintViolation) {
+ Iterator nodes = constraintViolation.getPropertyPath().iterator();
+ Path.Node leafNode = null;
+ while (nodes.hasNext()) {
+ leafNode = nodes.next();
+ }
+ return leafNode;
+ }
+}
diff --git a/formalist/runtime/src/main/java/org/pagan/formalist/interceptor/MethodValidationInterceptor.java b/formalist/runtime/src/main/java/org/pagan/formalist/interceptor/MethodValidationInterceptor.java
new file mode 100644
index 0000000..cc9355c
--- /dev/null
+++ b/formalist/runtime/src/main/java/org/pagan/formalist/interceptor/MethodValidationInterceptor.java
@@ -0,0 +1,26 @@
+package org.pagan.formalist.interceptor;
+
+import org.pagan.formalist.annotataions.MethodValidated;
+import javax.annotation.Priority;
+import javax.interceptor.AroundConstruct;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+
+@MethodValidated
+@Interceptor
+@Priority(Interceptor.Priority.PLATFORM_AFTER + 800)
+public class MethodValidationInterceptor extends AbstractMethodValidationInterceptor {
+
+ @AroundInvoke
+ @Override
+ public Object validateMethodInvocation(InvocationContext ctx) throws Exception {
+ return super.validateMethodInvocation(ctx);
+ }
+
+ @AroundConstruct
+ @Override
+ public void validateConstructorInvocation(InvocationContext ctx) throws Exception {
+ super.validateConstructorInvocation(ctx);
+ }
+}
diff --git a/formalist/runtime/src/main/java/org/pagan/formalist/jaxrs/ConstraintTypeUtil20.java b/formalist/runtime/src/main/java/org/pagan/formalist/jaxrs/ConstraintTypeUtil20.java
new file mode 100644
index 0000000..ac28970
--- /dev/null
+++ b/formalist/runtime/src/main/java/org/pagan/formalist/jaxrs/ConstraintTypeUtil20.java
@@ -0,0 +1,50 @@
+package org.pagan.formalist.jaxrs;
+
+import java.util.Iterator;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ElementKind;
+import javax.validation.Path.Node;
+
+import org.jboss.resteasy.api.validation.ConstraintType;
+import org.jboss.resteasy.resteasy_jaxrs.i18n.Messages;
+import org.jboss.resteasy.spi.validation.ConstraintTypeUtil;
+
+public class ConstraintTypeUtil20 implements ConstraintTypeUtil {
+
+ @Override
+ public ConstraintType.Type getConstraintType(Object o) {
+ if (!(o instanceof ConstraintViolation)) {
+ throw new RuntimeException(Messages.MESSAGES.unknownObjectPassedAsConstraintViolation(o));
+ }
+ ConstraintViolation> v = ConstraintViolation.class.cast(o);
+
+ Iterator nodes = v.getPropertyPath().iterator();
+ Node firstNode = nodes.next();
+
+ switch (firstNode.getKind()) {
+ case BEAN:
+ return ConstraintType.Type.CLASS;
+ case CONSTRUCTOR:
+ case METHOD:
+ Node secondNode = nodes.next();
+
+ if (secondNode.getKind() == ElementKind.PARAMETER
+ || secondNode.getKind() == ElementKind.CROSS_PARAMETER) {
+ return ConstraintType.Type.PARAMETER;
+ } else if (secondNode.getKind() == ElementKind.RETURN_VALUE) {
+ return ConstraintType.Type.RETURN_VALUE;
+ } else {
+ throw new RuntimeException(Messages.MESSAGES.unexpectedPathNodeViolation(secondNode.getKind()));
+ }
+ case PROPERTY:
+ return ConstraintType.Type.PROPERTY;
+ case CROSS_PARAMETER:
+ case PARAMETER:
+ case RETURN_VALUE:
+ case CONTAINER_ELEMENT: // we shouldn't encounter these element types at the root
+ default:
+ throw new RuntimeException(Messages.MESSAGES.unexpectedPathNode(firstNode.getKind()));
+ }
+ }
+}
diff --git a/formalist/runtime/src/main/java/org/pagan/formalist/jaxrs/JaxrsEndPointValidationInterceptor.java b/formalist/runtime/src/main/java/org/pagan/formalist/jaxrs/JaxrsEndPointValidationInterceptor.java
new file mode 100644
index 0000000..eb8607d
--- /dev/null
+++ b/formalist/runtime/src/main/java/org/pagan/formalist/jaxrs/JaxrsEndPointValidationInterceptor.java
@@ -0,0 +1,51 @@
+package org.pagan.formalist.jaxrs;
+
+import org.pagan.formalist.annotataions.JaxrsEndPointValidated;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.Priority;
+import javax.interceptor.AroundConstruct;
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+import javax.validation.ConstraintViolationException;
+import javax.ws.rs.core.MediaType;
+
+import org.jboss.resteasy.util.MediaTypeHelper;
+
+import org.pagan.formalist.interceptor.AbstractMethodValidationInterceptor;
+
+@JaxrsEndPointValidated
+@Interceptor
+@Priority(Interceptor.Priority.PLATFORM_AFTER + 800)
+public class JaxrsEndPointValidationInterceptor extends AbstractMethodValidationInterceptor {
+
+ @AroundInvoke
+ @Override
+ public Object validateMethodInvocation(InvocationContext ctx) throws Exception {
+ try {
+ return super.validateMethodInvocation(ctx);
+ } catch (ConstraintViolationException e) {
+ throw new ResteasyViolationExceptionImpl(e.getConstraintViolations(), getAccept(ctx.getMethod()));
+ }
+ }
+
+ @AroundConstruct
+ @Override
+ public void validateConstructorInvocation(InvocationContext ctx) throws Exception {
+ super.validateConstructorInvocation(ctx);
+ }
+
+ private List getAccept(Method method) {
+ MediaType[] producedMediaTypes = MediaTypeHelper.getProduces(method.getDeclaringClass(), method);
+
+ if (producedMediaTypes == null) {
+ return Collections.emptyList();
+ }
+
+ return Arrays.asList(producedMediaTypes);
+ }
+}
diff --git a/formalist/runtime/src/main/java/org/pagan/formalist/jaxrs/ResteasyViolationExceptionImpl.java b/formalist/runtime/src/main/java/org/pagan/formalist/jaxrs/ResteasyViolationExceptionImpl.java
new file mode 100644
index 0000000..4c1d838
--- /dev/null
+++ b/formalist/runtime/src/main/java/org/pagan/formalist/jaxrs/ResteasyViolationExceptionImpl.java
@@ -0,0 +1,31 @@
+package org.pagan.formalist.jaxrs;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.ws.rs.core.MediaType;
+
+import org.jboss.resteasy.api.validation.ResteasyViolationException;
+import org.jboss.resteasy.core.ResteasyContext;
+import org.jboss.resteasy.spi.ResteasyConfiguration;
+import org.jboss.resteasy.spi.validation.ConstraintTypeUtil;
+
+public class ResteasyViolationExceptionImpl extends ResteasyViolationException {
+ private static final long serialVersionUID = 657697354453281559L;
+
+ public ResteasyViolationExceptionImpl(final Set extends ConstraintViolation>> constraintViolations,
+ final List accept) {
+ super(constraintViolations, accept);
+ }
+
+ @Override
+ public ConstraintTypeUtil getConstraintTypeUtil() {
+ return new ConstraintTypeUtil20();
+ }
+
+ @Override
+ protected ResteasyConfiguration getResteasyConfiguration() {
+ return ResteasyContext.getContextData(ResteasyConfiguration.class);
+ }
+}
diff --git a/formalist/runtime/src/main/java/org/pagan/formalist/jaxrs/ResteasyViolationExceptionMapper.java b/formalist/runtime/src/main/java/org/pagan/formalist/jaxrs/ResteasyViolationExceptionMapper.java
new file mode 100644
index 0000000..c6c8b74
--- /dev/null
+++ b/formalist/runtime/src/main/java/org/pagan/formalist/jaxrs/ResteasyViolationExceptionMapper.java
@@ -0,0 +1,108 @@
+package org.pagan.formalist.jaxrs;
+
+import java.util.Iterator;
+import java.util.List;
+
+import javax.validation.ConstraintDeclarationException;
+import javax.validation.ConstraintDefinitionException;
+import javax.validation.GroupDefinitionException;
+import javax.validation.ValidationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.jboss.resteasy.api.validation.ResteasyViolationException;
+import org.jboss.resteasy.api.validation.Validation;
+import org.jboss.resteasy.api.validation.ViolationReport;
+
+@Provider
+public class ResteasyViolationExceptionMapper implements ExceptionMapper {
+
+ @Override
+ public Response toResponse(ValidationException exception) {
+
+ if (exception instanceof ConstraintDefinitionException) {
+ return buildResponse(unwrapException(exception), MediaType.TEXT_PLAIN, Status.INTERNAL_SERVER_ERROR);
+ }
+ if (exception instanceof ConstraintDeclarationException) {
+ return buildResponse(unwrapException(exception), MediaType.TEXT_PLAIN, Status.INTERNAL_SERVER_ERROR);
+ }
+ if (exception instanceof GroupDefinitionException) {
+ return buildResponse(unwrapException(exception), MediaType.TEXT_PLAIN, Status.INTERNAL_SERVER_ERROR);
+ }
+ if (exception instanceof ResteasyViolationException) {
+ ResteasyViolationException resteasyViolationException = ResteasyViolationException.class.cast(exception);
+ Exception e = resteasyViolationException.getException();
+ if (e != null) {
+ return buildResponse(unwrapException(e), MediaType.TEXT_PLAIN, Status.INTERNAL_SERVER_ERROR);
+ } else if (resteasyViolationException.getReturnValueViolations().isEmpty()) {
+ return buildViolationReportResponse(resteasyViolationException, Status.BAD_REQUEST);
+ } else {
+ return buildViolationReportResponse(resteasyViolationException, Status.INTERNAL_SERVER_ERROR);
+ }
+ }
+ return buildResponse(unwrapException(exception), MediaType.TEXT_PLAIN, Status.INTERNAL_SERVER_ERROR);
+ }
+
+ protected Response buildResponse(Object entity, String mediaType, Status status) {
+ ResponseBuilder builder = Response.status(status).entity(entity);
+ builder.type(MediaType.TEXT_PLAIN);
+ builder.header(Validation.VALIDATION_HEADER, "true");
+ return builder.build();
+ }
+
+ protected Response buildViolationReportResponse(ResteasyViolationException exception, Status status) {
+ ResponseBuilder builder = Response.status(status);
+ builder.header(Validation.VALIDATION_HEADER, "true");
+
+ // Check standard media types.
+ MediaType mediaType = getAcceptMediaType(exception.getAccept());
+ if (mediaType != null) {
+ builder.type(mediaType);
+ builder.entity(new ViolationReport(exception));
+ return builder.build();
+ }
+
+ // Default media type.
+ builder.type(MediaType.TEXT_PLAIN);
+ builder.entity(exception.toString());
+ return builder.build();
+ }
+
+ protected String unwrapException(Throwable t) {
+ StringBuffer sb = new StringBuffer();
+ doUnwrapException(sb, t);
+ return sb.toString();
+ }
+
+ private void doUnwrapException(StringBuffer sb, Throwable t) {
+ if (t == null) {
+ return;
+ }
+ sb.append(t.toString());
+ if (t.getCause() != null && t != t.getCause()) {
+ sb.append('[');
+ doUnwrapException(sb, t.getCause());
+ sb.append(']');
+ }
+ }
+
+ private MediaType getAcceptMediaType(List accept) {
+ Iterator it = accept.iterator();
+ while (it.hasNext()) {
+ MediaType mt = it.next();
+ if (MediaType.APPLICATION_XML_TYPE.getType().equals(mt.getType())
+ && MediaType.APPLICATION_XML_TYPE.getSubtype().equals(mt.getSubtype())) {
+ return MediaType.APPLICATION_XML_TYPE;
+ }
+ if (MediaType.APPLICATION_JSON_TYPE.getType().equals(mt.getType())
+ && MediaType.APPLICATION_JSON_TYPE.getSubtype().equals(mt.getSubtype())) {
+ return MediaType.APPLICATION_JSON_TYPE;
+ }
+ }
+ return null;
+ }
+}
diff --git a/formalist/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/formalist/runtime/src/main/resources/META-INF/quarkus-extension.yaml
new file mode 100644
index 0000000..62c4a2e
--- /dev/null
+++ b/formalist/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -0,0 +1,12 @@
+---
+name: "Formalist (Apache Bval) Validator"
+metadata:
+ short-name: "bean validation"
+ keywords:
+ - "formalist-validator"
+ - "bean-validation"
+ - "validation"
+ categories:
+ - "web"
+ - "data"
+ status: "stable"
diff --git a/formalist/runtime/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers b/formalist/runtime/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers
new file mode 100644
index 0000000..5a3e9a3
--- /dev/null
+++ b/formalist/runtime/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers
@@ -0,0 +1 @@
+org.pagan.formalist.jaxrs.ResteasyViolationExceptionMapper
diff --git a/janitor/runtime/pom.xml b/janitor/runtime/pom.xml
index 4b2c7d9..88e53b6 100644
--- a/janitor/runtime/pom.xml
+++ b/janitor/runtime/pom.xml
@@ -32,7 +32,6 @@
quarkus-security
${quarkus.platform.version}
-
demo