Skip to content

Commit

Permalink
Merge pull request #1748 from arunans23/dmnew
Browse files Browse the repository at this point in the history
Avoid schema validation and implement minimal datamapper
  • Loading branch information
arunans23 authored Jan 22, 2025
2 parents 171030f + a9defbd commit 08d410f
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 LLC. 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.wso2.carbon.mediator.datamapper.engine.core.executors;

import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;

public class ScriptRunner {

private final Context context;
private final Value mapFunction;

public ScriptRunner(String jsCode) {

// Build the context with experimental options enabled
this.context = Context.newBuilder("js")
.allowExperimentalOptions(true) // Allow experimental options
.option("js.nashorn-compat", "true") // Enable Nashorn compatibility
.build();
// we need to evaluate the script only once
this.mapFunction = context.eval("js", jsCode);
}

public String runScript(String inputJson, String variablesJson) {

// provide input/payload and variables as JSON objects
Value input = context.eval("js", "(" + inputJson + ")");
Value variables = context.eval("js", "(" + variablesJson + ")");
Value result = mapFunction.execute(input, variables);
return result.toString();
}

public void close() {
context.close();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package org.wso2.carbon.mediator.datamapper;

import com.google.gson.Gson;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axiom.om.impl.llom.OMTextImpl;
Expand All @@ -24,6 +25,7 @@
import org.apache.axiom.soap.SOAP12Constants;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axis2.AxisFault;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
Expand All @@ -40,12 +42,15 @@
import org.apache.synapse.mediators.AbstractMediator;
import org.apache.synapse.mediators.Value;
import org.apache.synapse.mediators.template.TemplateContext;
import org.apache.synapse.mediators.v2.VariableMediator;
import org.apache.synapse.util.AXIOMUtils;
import org.wso2.carbon.mediator.datamapper.config.xml.DataMapperMediatorConstants;
import org.wso2.carbon.mediator.datamapper.engine.core.exceptions.JSException;
import org.wso2.carbon.mediator.datamapper.engine.core.exceptions.ReaderException;
import org.wso2.carbon.mediator.datamapper.engine.core.exceptions.SchemaException;
import org.wso2.carbon.mediator.datamapper.engine.core.exceptions.WriterException;
import org.wso2.carbon.mediator.datamapper.engine.core.executors.ScriptExecutor;
import org.wso2.carbon.mediator.datamapper.engine.core.executors.ScriptRunner;
import org.wso2.carbon.mediator.datamapper.engine.core.mapper.JSFunction;
import org.wso2.carbon.mediator.datamapper.engine.core.mapper.MappingHandler;
import org.wso2.carbon.mediator.datamapper.engine.core.mapper.MappingResource;
Expand Down Expand Up @@ -115,6 +120,9 @@ public class DataMapperMediator extends AbstractMediator implements ManagedLifec
private XSLTMappingResource xsltMappingResource = null;
private XSLTMappingHandler xsltMappingHandler = null;
private final Object xsltHandlerLock = new Object();
private String targetVariableName = null;
private String target = null;
private static ScriptRunner scriptRunner = null;

/**
* Returns registry resources as input streams to create the MappingResourceLoader object
Expand Down Expand Up @@ -250,6 +258,42 @@ public void setOutputType(String type) {
this.outputType = type;
}

/**
* Get the target variable name
*
* @return the target variable name
*/
public String getTargetVariableName() {
return targetVariableName;
}

/**
* Set the target variable name
*
* @param targetVariableName the target variable name
*/
public void setTargetVariableName(String targetVariableName) {
this.targetVariableName = targetVariableName;
}

/**
* Get the target
*
* @return the target
*/
public String getTarget() {
return target;
}

/**
* Set the target
*
* @param target the target
*/
public void setTarget(String target) {
this.target = target;
}

/**
* Set a pre-built mapping resource
* This method is used by data-mapper test feature in EI-Tooling
Expand Down Expand Up @@ -304,24 +348,45 @@ public boolean mediate(MessageContext synCtx) {
}
}

// Does message conversion and gives the final result
transform(synCtx);
if (target != null || targetVariableName != null) {
// new datamapping behaviour without schema validation is decided based on above attributes
try {
String input = getInput(synCtx, inputType);
String output = transform(synCtx, mappingConfigurationKey.evaluateValue(synCtx), input);

if (DataMapperMediatorConstants.TARGET_BODY.equalsIgnoreCase(target)) {
setOutput(synCtx, outputType, output);
} else if (DataMapperMediatorConstants.TARGET_VARIABLE.equalsIgnoreCase(target) &&
targetVariableName != null) {
setOutputVariable(synCtx, outputType, output, targetVariableName);
} else {
handleException("Invalid target attribute found in DataMapper mediator", synCtx);
}
} catch (IOException e) {
handleException("Mapping failed in DataMapper mediator", e, synCtx);
}
} else {
// preserve backward compatibility
// Does message conversion and gives the final result
transformWithSchemaValidation(synCtx);
}

//setting output type in the axis2 message context
switch (outputType) {
case "JSON":
((Axis2MessageContext) synCtx).getAxis2MessageContext().setProperty("messageType", "application/json");
((Axis2MessageContext) synCtx).getAxis2MessageContext().setProperty("ContentType", "application/json");
break;
case "XML":
((Axis2MessageContext) synCtx).getAxis2MessageContext().setProperty("messageType", "application/xml");
((Axis2MessageContext) synCtx).getAxis2MessageContext().setProperty("ContentType", "application/xml");
break;
case "CSV":
((Axis2MessageContext) synCtx).getAxis2MessageContext().setProperty("messageType", "text/xml");
((Axis2MessageContext) synCtx).getAxis2MessageContext().setProperty("ContentType", "text/xml");
break;
default:
throw new SynapseException("Unsupported output data type found : " + outputType);
case "JSON":
((Axis2MessageContext) synCtx).getAxis2MessageContext().setProperty("messageType", "application/json");
((Axis2MessageContext) synCtx).getAxis2MessageContext().setProperty("ContentType", "application/json");
break;
case "XML":
((Axis2MessageContext) synCtx).getAxis2MessageContext().setProperty("messageType", "application/xml");
((Axis2MessageContext) synCtx).getAxis2MessageContext().setProperty("ContentType", "application/xml");
break;
case "CSV":
((Axis2MessageContext) synCtx).getAxis2MessageContext().setProperty("messageType", "text/xml");
((Axis2MessageContext) synCtx).getAxis2MessageContext().setProperty("ContentType", "text/xml");
break;
default:
throw new SynapseException("Unsupported output data type found : " + outputType);
}

if (synLog.isTraceOrDebugEnabled()) {
Expand Down Expand Up @@ -361,12 +426,107 @@ private void checkForXSLTTransformation(MessageContext synCtx) {
}
}

public String transform(MessageContext synCtx, String configKey, String inputJson) throws IOException {

if (scriptRunner == null) {
String jsCode = getScriptResource(synCtx, configKey);
scriptRunner = new ScriptRunner(jsCode);
}
return scriptRunner.runScript(inputJson, convertVariablesMapToJSON(synCtx));
}

/**
* Convert the variables map to a JSON String.
*
* @param synCtx Message context
* @return JSON String
*/
private String convertVariablesMapToJSON(MessageContext synCtx) {
return new Gson().toJson(((Axis2MessageContext) synCtx).getVariables());
}

private String getInput(MessageContext context, String inputType) {

String inputString = null;
try {
switch (InputOutputDataType.fromString(inputType)) {
case XML:
case CSV:
inputString = JsonUtil.toJsonString(context.getEnvelope()).toString();
break;
case JSON:
org.apache.axis2.context.MessageContext a2mc =
((Axis2MessageContext) context).getAxis2MessageContext();
if (JsonUtil.hasAJsonPayload(a2mc)) {
try {
inputString = IOUtils.toString(JsonUtil.getJsonPayload(a2mc));
} catch (IOException e) {
handleException("Unable to read input message in Data Mapper mediator. " +
e.getMessage(), e, context);
}
}
break;
default:
inputString = context.getEnvelope().toString();
}
} catch (OMException e) {
handleException("Unable to read input message in Data Mapper mediator. " + e.getMessage(), e,
context);
} catch (AxisFault e) {
handleException("Unable to convert input message from XML to JSON in Data Mapper mediator. " +
e.getMessage(), e, context);
}
return inputString;
}

private void setOutput(MessageContext msgCtx, String outputType, String output) {

org.apache.axis2.context.MessageContext a2mc = ((Axis2MessageContext) msgCtx).getAxis2MessageContext();
switch (InputOutputDataType.fromString(outputType)) {
case JSON:
try {
JsonUtil.getNewJsonPayload(a2mc, output, true, true);
} catch (AxisFault e) {
handleException("Unable to add output JSON to Message Context in Data Mapper mediator. " +
e.getMessage(), e, msgCtx);
}
break;
case XML:
case CSV:
default:
try {
JsonUtil.removeJsonPayload(a2mc);
OMElement omXML = JsonUtil.toXml(IOUtils.toInputStream(output), false);
a2mc.getEnvelope().getBody().addChild(omXML.getFirstElement());
} catch (AxisFault e) {
handleException("Unable to convert output JSON to XML in Data Mapper mediator. " +
e.getMessage(), e, msgCtx);
}
}
}

/**
* Set the output message to the message context as a variable
*
* @param msgCtx message context
* @param outputType output type
* @param output output message
* @param targetVariableName
*/
private void setOutputVariable(MessageContext msgCtx, String outputType, String output, String targetVariableName) {

VariableMediator variableMediator = new VariableMediator();
variableMediator.setName(targetVariableName);
variableMediator.setValue(output, outputType);
msgCtx.setVariable(targetVariableName, variableMediator.getValue());
}

/**
* Does message conversion and gives the output message as the final result
*
* @param synCtx the message synCtx
*/
private void transform(MessageContext synCtx) {
private void transformWithSchemaValidation(MessageContext synCtx) {
try {
String outputResult;
if (usingXSLTMapping) {
Expand Down Expand Up @@ -583,6 +743,15 @@ private MappingResource getMappingResource(MessageContext synCtx, String configK
return null;
}

private String getScriptResource(MessageContext synCtx, String configKey) throws IOException {

InputStream configFileInputStream = getRegistryResource(synCtx, configKey);
if (configFileInputStream == null) {
handleException("DataMapper mediator : mapping configuration is null", synCtx);
}
return IOUtils.toString(configFileInputStream, String.valueOf(StandardCharsets.UTF_8));
}

/**
* When Data mapper mediator has been invoked initially, this creates a new xslt mapping
* resource
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public class DataMapperMediatorConstants {
public static final String INPUT_TYPE = "inputType";
public static final String OUTPUT_TYPE = "outputType";
public static final String XSLT_STYLE_SHEET = "xsltStyleSheet";
public static final String TARGET_VARIABLE = "variable";
public static final String TARGET = "target";
public static final String TARGET_BODY = "body";

/* Names of the different contexts used in the ESB */
public static final String DEFAULT_CONTEXT = "DEFAULT";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ public class DataMapperMediatorFactory extends AbstractMediatorFactory {
OMAttribute outputTypeAttribute = element.getAttribute(new QName(DataMapperMediatorConstants.OUTPUT_TYPE));
OMAttribute xsltStyleSheetKeyAttribute = element.getAttribute(new QName
(DataMapperMediatorConstants.XSLT_STYLE_SHEET));

OMAttribute targetVariableAttribute = element.getAttribute(new QName(DataMapperMediatorConstants.TARGET_VARIABLE));
OMAttribute targetAttribute = element.getAttribute(new QName(DataMapperMediatorConstants.TARGET));

/*
* ValueFactory for creating dynamic or static Value and provide methods
Expand Down Expand Up @@ -120,6 +121,14 @@ public class DataMapperMediatorFactory extends AbstractMediatorFactory {
datamapperMediator.setXsltStyleSheetKey(xsltStyleSheetKeyValue);
}

if (targetAttribute != null) {
datamapperMediator.setTarget(targetAttribute.getAttributeValue());
}

if (targetVariableAttribute != null) {
datamapperMediator.setTargetVariableName(targetVariableAttribute.getAttributeValue());
}

processAuditStatus(datamapperMediator, element);
addAllCommentChildrenToList(element, datamapperMediator.getCommentsList());
return datamapperMediator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,17 @@ public class DataMapperMediatorSerializer extends AbstractMediatorSerializer {
outputType));
}

String targetVariableName = dataMapperMediator.getTargetVariableName();
if (targetVariableName != null) {
dataMapperElement.addAttribute(fac.createOMAttribute(DataMapperMediatorConstants.TARGET_VARIABLE, nullNS,
targetVariableName));
}

String target = dataMapperMediator.getTarget();
if (target != null) {
dataMapperElement.addAttribute(fac.createOMAttribute(DataMapperMediatorConstants.TARGET, nullNS, target));
}

saveTracingState(dataMapperElement, dataMapperMediator);
serializeComments(dataMapperElement, dataMapperMediator.getCommentsList());
return dataMapperElement;
Expand Down

0 comments on commit 08d410f

Please sign in to comment.