diff --git a/modules/qrest/src/main/java/org/jpos/qrest/ValidateParams.java b/modules/qrest/src/main/java/org/jpos/qrest/ValidateParams.java
new file mode 100644
index 0000000000..e588b61921
--- /dev/null
+++ b/modules/qrest/src/main/java/org/jpos/qrest/ValidateParams.java
@@ -0,0 +1,259 @@
+/*
+ * jPOS Project [http://jpos.org]
+ * Copyright (C) 2000-2018 jPOS Software SRL
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.jpos.qrest;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.github.fge.jackson.JsonLoader;
+import com.github.fge.jsonschema.core.exceptions.ProcessingException;
+import com.github.fge.jsonschema.core.report.ProcessingReport;
+import com.github.fge.jsonschema.main.JsonSchema;
+import com.github.fge.jsonschema.main.JsonSchemaFactory;
+import org.jdom2.Element;
+import org.jpos.core.ConfigurationException;
+import org.jpos.core.XmlConfigurable;
+import org.jpos.transaction.Context;
+import org.jpos.transaction.TransactionParticipant;
+import org.jpos.util.Caller;
+import org.jpos.util.Log;
+import org.jpos.util.LogSource;
+import org.jpos.util.Logger;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Validate mandatory and optional parameters. Support JsonSchema.
+ *
+ * @author jr@jpos.org
+ *
+ * Sample usage:
+ *
+ * <participant class="org.jpos.rest.ValidateParams" logger="Q2" realm="validate-params">
+ * <mandatory>
+ * <param name="version">"^1\.0$"</param>
+ * <param name="consumer">"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$</param>
+ * </mandatory>
+ * </participant>
+ *
+ *
+ * <participant class="org.jpos.rest.ValidateParams" logger="Q2" realm="validate-params">
+ * <mandatory>
+ * <param name="JSON_REQUEST" type="json-schema">
+ * {
+ * "$schema": "http://json-schema.org/draft-04/schema#",
+ * "title": "SysConfig",
+ * "description": "A SysConfig to post",
+ * "type": "object",
+ * "properties": {
+ * "id": {
+ * "type": "string"
+ * },
+ * "value": {
+ * "type": "string"
+ * },
+ * "readPerm": {
+ * "type" : "string"
+ * },
+ * "writePerm": {
+ * "type": "string"
+ * }
+ * },
+ * "additionalProperties" : false,
+ * "required": [ "id","value","readPerm","writePerm"]
+ * }
+ * </param>
+ * </mandatory>
+ * </participant>
+ *
+ */
+
+@SuppressWarnings("unused")
+public class ValidateParams implements TransactionParticipant, XmlConfigurable, LogSource {
+ private Map mandatory;
+ private Map optional;
+ private Map mandatoryJson;
+ private Map optionalJson;
+
+ private Log log;
+
+ @Override
+ public int prepare(long id, Serializable context) {
+ Context ctx = (Context) context;
+
+ checkMandatory(ctx);
+ checkOptional(ctx);
+ checkMandatoryJson(ctx);
+ checkOptionalJson(ctx);
+ return (ctx.getResult().hasFailures() ? ABORTED : PREPARED) | READONLY | NO_JOIN;
+ }
+
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void setConfiguration(Element cfg) throws ConfigurationException{
+ mandatory = new HashMap<>();
+ mandatoryJson = new HashMap<>();
+ Element m = cfg.getChild("mandatory");
+ if (m != null) {
+ for (Element e : m.getChildren("param")) {
+ if ("json-schema".equals(e.getAttributeValue("type"))) {
+ try {
+ String json = e.getValue();
+ JsonNode nodeSchema = JsonLoader.fromString(json);
+ JsonSchema schema = JsonSchemaFactory.byDefault().getJsonSchema(nodeSchema);
+ mandatoryJson.put(e.getAttributeValue("name"), schema);
+ } catch (Exception ex) {
+ throw new ConfigurationException (ex);
+ }
+ } else {
+ mandatory.put(e.getAttributeValue("name"), Pattern.compile(e.getValue()));
+ }
+ }
+ }
+ optional = new HashMap<>();
+ optionalJson = new HashMap<>();
+ Element o = cfg.getChild("optional");
+ if (o != null) {
+ for (Element e : o.getChildren("param")) {
+ if ("json-schema".equals(e.getAttributeValue("type"))) {
+ String json = e.getValue();
+ JsonNode nodeSchema;
+ try {
+ nodeSchema = JsonLoader.fromString(json);
+ JsonSchema schema = JsonSchemaFactory.byDefault().getJsonSchema(nodeSchema);
+ optionalJson.put(e.getAttributeValue("name"), schema);
+ } catch (IOException | ProcessingException ex) {
+ throw new ConfigurationException(ex);
+ }
+ } else {
+ optional.put(e.getAttributeValue("name"), Pattern.compile(e.getValue()));
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setLogger(Logger logger, String realm) {
+ this.log = new Log(logger, realm);
+ }
+
+ @Override
+ public String getRealm() {
+ return log.getRealm();
+ }
+
+ @Override
+ public Logger getLogger() {
+ return log.getLogger();
+ }
+
+ private void checkMandatory (Context ctx) {
+ for (Map.Entry entry : mandatory.entrySet()) {
+ Object v = ctx.get(entry.getKey());
+ String value = v != null ? v.toString() : null;
+ if (value == null) {
+ ctx.getResult().fail(
+ ResultCode.MANDATORY_PARAM_NOT_PRESENT, Caller.info(), "Mandatory param '%s' not present", entry.getKey()
+ );
+ return;
+ }
+ Pattern p = entry.getValue();
+ Matcher m = p.matcher(value);
+ if (!m.matches()) {
+ ctx.getResult().fail(
+ ResultCode.INVALID_PARAM, Caller.info(), "Invalid param '%s'", entry.getKey()
+ );
+ return;
+ }
+ }
+ }
+
+ private void checkOptional (Context ctx) {
+ for (Map.Entry entry : optional.entrySet()) {
+ String value = ctx.getString(entry.getKey());
+ if (value != null) {
+ Pattern p = entry.getValue();
+ Matcher m = p.matcher(value);
+ if (!m.matches()) {
+ ctx.getResult().fail(
+ ResultCode.INVALID_OPTIONAL_PARAM, Caller.info(), "Invalid optional param '%s'", entry.getKey()
+ );
+ return;
+ }
+ }
+ }
+ }
+
+ private void checkMandatoryJson (Context ctx) {
+ int errors = 0;
+ for (Map.Entry entry : mandatoryJson.entrySet()) {
+ String value = ctx.getString(entry.getKey());
+ ProcessingReport report;
+ if (value != null) {
+ try {
+ JsonSchema schema = entry.getValue();
+ JsonNode node = JsonLoader.fromString(value);
+ report = schema.validate(node);
+ if (!report.isSuccess()) {
+ ctx.getResult().fail(
+ ResultCode.INVALID_MANDATORY_JSON, Caller.info(), "Invalid mandatory JSON '%s'", entry.getKey()
+ );
+ ctx.log(report);
+ }
+ } catch(Exception ex) {
+ ctx.getResult().fail(
+ ResultCode.INVALID_MANDATORY_JSON, Caller.info(), "Invalid mandatory JSON param '%s' - %s", entry.getKey(), ex.getMessage()
+ );
+ }
+ } else {
+ ctx.getResult().fail(
+ ResultCode.MANDATORY_JSON_NOT_PRESENT, Caller.info(), "Mandatory JSON '%s' not present", entry.getKey()
+ );
+
+ }
+ }
+ }
+
+ private void checkOptionalJson (Context ctx) {
+ for (Map.Entry entry : optionalJson.entrySet()) {
+ String value = ctx.getString(entry.getKey());
+ ProcessingReport report;
+ if (value != null) {
+ try {
+ JsonSchema schema = entry.getValue();
+ JsonNode node = JsonLoader.fromString(value);
+ report = schema.validate(node);
+ if (!report.isSuccess()) {
+ ctx.getResult().fail(
+ ResultCode.INVALID_OPTIONAL_JSON, Caller.info(), "Invalid mandatory JSON '%s'", entry.getKey()
+ );
+ }
+ } catch(Exception ex) {
+ ctx.getResult().fail(
+ ResultCode.INVALID_OPTIONAL_JSON, Caller.info(), "Mandatory JSON '%s' not present", entry.getKey()
+ );
+ }
+ }
+ }
+ }
+}