diff --git a/modules/core/src/main/java/org/apache/synapse/config/xml/AbstractMediatorFactory.java b/modules/core/src/main/java/org/apache/synapse/config/xml/AbstractMediatorFactory.java index aac6c3112e..2375acee1f 100644 --- a/modules/core/src/main/java/org/apache/synapse/config/xml/AbstractMediatorFactory.java +++ b/modules/core/src/main/java/org/apache/synapse/config/xml/AbstractMediatorFactory.java @@ -31,8 +31,11 @@ import org.apache.synapse.SynapseException; import org.apache.synapse.aspects.AspectConfigurable; import org.apache.synapse.aspects.AspectConfiguration; +import org.apache.synapse.mediators.v2.ext.InputArgument; +import org.jaxen.JaxenException; import javax.xml.namespace.QName; +import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; @@ -76,6 +79,12 @@ public abstract class AbstractMediatorFactory implements MediatorFactory { protected static final QName DESCRIPTION_Q = new QName(XMLConfigConstants.SYNAPSE_NAMESPACE, "description"); + protected static final QName RESULT_TARGET_Q = new QName("result-target"); + protected static final QName ATT_TYPE = new QName("type"); + protected static final QName ATT_ARGUMENT = new QName("argument"); + protected static final QName INPUTS + = new QName(XMLConfigConstants.SYNAPSE_NAMESPACE, "inputs"); + /** * A constructor that makes subclasses pick up the correct logger */ @@ -238,4 +247,37 @@ protected static void addAllCommentChildrenToList(OMElement el, List com } } } + + /** + * This method is used to get the input arguments of the script mediator. + * + * @param inputArgsElement input arguments element + * @return List of input arguments + */ + protected List getInputArguments(OMElement inputArgsElement, String mediator) { + + List inputArgsMap = new ArrayList<>(); + Iterator inputIterator = inputArgsElement.getChildrenWithName(ATT_ARGUMENT); + while (inputIterator.hasNext()) { + OMElement inputElement = (OMElement) inputIterator.next(); + String nameAttribute = inputElement.getAttributeValue(ATT_NAME); + String typeAttribute = inputElement.getAttributeValue(ATT_TYPE); + String valueAttribute = inputElement.getAttributeValue(ATT_VALUE); + String expressionAttribute = inputElement.getAttributeValue(ATT_EXPRN); + InputArgument argument = new InputArgument(nameAttribute); + if (valueAttribute != null) { + argument.setValue(valueAttribute, typeAttribute); + } else if (expressionAttribute != null) { + try { + argument.setExpression(SynapsePathFactory.getSynapsePath(inputElement, + new QName("expression")), typeAttribute); + } catch (JaxenException e) { + handleException("Error setting expression : " + expressionAttribute + " as an input argument to " + + mediator + " mediator. " + e.getMessage(), e); + } + } + inputArgsMap.add(argument); + } + return inputArgsMap; + } } diff --git a/modules/core/src/main/java/org/apache/synapse/config/xml/AbstractMediatorSerializer.java b/modules/core/src/main/java/org/apache/synapse/config/xml/AbstractMediatorSerializer.java index ca484257c3..5f9ebac464 100644 --- a/modules/core/src/main/java/org/apache/synapse/config/xml/AbstractMediatorSerializer.java +++ b/modules/core/src/main/java/org/apache/synapse/config/xml/AbstractMediatorSerializer.java @@ -32,8 +32,10 @@ import org.apache.synapse.SynapseException; import org.apache.synapse.aspects.AspectConfigurable; import org.apache.synapse.aspects.statistics.StatisticsConfigurable; +import org.apache.synapse.mediators.AbstractMediator; import org.apache.synapse.mediators.MediatorProperty; import org.apache.synapse.mediators.builtin.CommentMediator; +import org.apache.synapse.mediators.v2.ext.InputArgument; import javax.xml.namespace.QName; import java.util.Collection; diff --git a/modules/core/src/main/java/org/apache/synapse/config/xml/ClassMediatorFactory.java b/modules/core/src/main/java/org/apache/synapse/config/xml/ClassMediatorFactory.java index 2371ec12cd..bf3fe10e22 100644 --- a/modules/core/src/main/java/org/apache/synapse/config/xml/ClassMediatorFactory.java +++ b/modules/core/src/main/java/org/apache/synapse/config/xml/ClassMediatorFactory.java @@ -21,13 +21,20 @@ import org.apache.axiom.om.OMAttribute; import org.apache.axiom.om.OMElement; +import org.apache.commons.lang3.StringUtils; import org.apache.synapse.Mediator; import org.apache.synapse.SynapseException; import org.apache.synapse.SynapseConstants; import org.apache.synapse.mediators.ext.ClassMediator; +import org.apache.synapse.mediators.v2.ext.AbstractClassMediator; +import org.apache.synapse.mediators.v2.ext.InputArgument; import javax.xml.namespace.QName; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Properties; @@ -114,7 +121,40 @@ public Mediator createSpecificMediator(OMElement elem, Properties properties) { throw new SynapseException(msg, e); } - classMediator.addAllProperties(MediatorPropertyFactory.getMediatorProperties(elem, mediator)); + String targetAtt = elem.getAttributeValue(RESULT_TARGET_Q); + if (StringUtils.isNotBlank(targetAtt)) { + // This a V2 class mediator. Set the result target and input arguments + String methodAtt = elem.getAttributeValue(new QName(XMLConfigConstants.NULL_NAMESPACE, + "method")); + if (StringUtils.isBlank(methodAtt)) { + String msg = "The 'method' attribute is required for the class mediator " + clazz.getName(); + log.error(msg); + throw new SynapseException(msg); + } + classMediator.setResultTarget(targetAtt); + classMediator.setMethodName(methodAtt); + OMElement inputArgsElement = elem.getFirstChildWithName(INPUTS); + if (inputArgsElement != null) { + List inputArgsMap = getInputArguments(inputArgsElement, clazz.getName() + " class"); + classMediator.setInputArguments(inputArgsMap); + } + Method[] methods = clazz.getMethods(); + for (Method method : methods) { + if (method.getName().equals(methodAtt)) { + List arguments = new ArrayList<>(); + for (Parameter parameter : method.getParameters()) { + if (parameter.isAnnotationPresent(AbstractClassMediator.Arg.class)) { + AbstractClassMediator.Arg arg = parameter.getAnnotation(AbstractClassMediator.Arg.class); + arguments.add(arg); + } + } + classMediator.setArguments(arguments); + break; + } + } + } else { + classMediator.addAllProperties(MediatorPropertyFactory.getMediatorProperties(elem, mediator)); + } // after successfully creating the mediator // set its common attributes such as tracing etc diff --git a/modules/core/src/main/java/org/apache/synapse/config/xml/ClassMediatorSerializer.java b/modules/core/src/main/java/org/apache/synapse/config/xml/ClassMediatorSerializer.java index 9b4be8230c..bc946f528d 100644 --- a/modules/core/src/main/java/org/apache/synapse/config/xml/ClassMediatorSerializer.java +++ b/modules/core/src/main/java/org/apache/synapse/config/xml/ClassMediatorSerializer.java @@ -21,6 +21,7 @@ import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMNode; +import org.apache.commons.lang3.StringUtils; import org.apache.synapse.Mediator; import org.apache.synapse.mediators.ext.ClassMediator; @@ -51,7 +52,14 @@ public OMElement serializeSpecificMediator(Mediator m) { handleException("Invalid class mediator. The class name is required"); } - super.serializeProperties(clazz, mediator.getProperties()); + if (StringUtils.isNotBlank(mediator.getResultTarget())) { + // If result target is set, this is V2 class mediator + clazz.addAttribute(fac.createOMAttribute("result-target", nullNS, mediator.getResultTarget())); + clazz.addAttribute(fac.createOMAttribute("method", nullNS, mediator.getMethodName())); + clazz.addChild(InputArgumentSerializer.serializeInputArguments(mediator.getInputArguments())); + } else { + super.serializeProperties(clazz, mediator.getProperties()); + } serializeComments(clazz, mediator.getCommentsList()); diff --git a/modules/core/src/main/java/org/apache/synapse/config/xml/InputArgumentSerializer.java b/modules/core/src/main/java/org/apache/synapse/config/xml/InputArgumentSerializer.java new file mode 100644 index 0000000000..6b00026eb2 --- /dev/null +++ b/modules/core/src/main/java/org/apache/synapse/config/xml/InputArgumentSerializer.java @@ -0,0 +1,61 @@ +/* + * 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.apache.synapse.config.xml; + +import org.apache.axiom.om.OMAbstractFactory; +import org.apache.axiom.om.OMElement; +import org.apache.axiom.om.OMFactory; +import org.apache.axiom.om.OMNamespace; +import org.apache.synapse.SynapseConstants; +import org.apache.synapse.mediators.v2.ext.InputArgument; + +import java.util.List; + +/** + * Serializer for input arguments + */ +public class InputArgumentSerializer { + + private static final OMFactory fac = OMAbstractFactory.getOMFactory(); + private static final OMNamespace nullNS = fac.createOMNamespace(XMLConfigConstants.NULL_NAMESPACE, ""); + private static final String INPUTS = "inputs"; + private static final String ARGUMENT = "argument"; + private static final String NAME = "name"; + private static final String TYPE = "type"; + private static final String EXPRESSION = "expression"; + private static final String VALUE = "value"; + + public static OMElement serializeInputArguments(List inputArguments) { + + OMElement inputElement = fac.createOMElement(INPUTS, SynapseConstants.SYNAPSE_OMNAMESPACE); + for (InputArgument inputArgument : inputArguments) { + OMElement inputArgElement = fac.createOMElement(ARGUMENT, SynapseConstants.SYNAPSE_OMNAMESPACE); + inputArgElement.addAttribute(fac.createOMAttribute(NAME, nullNS, inputArgument.getName())); + inputArgElement.addAttribute(fac.createOMAttribute(TYPE, nullNS, inputArgument.getType())); + if (inputArgument.getExpression() != null) { + SynapsePathSerializer.serializePath(inputArgument.getExpression(), + inputArgument.getExpression().getExpression(), inputArgElement, EXPRESSION); + } else { + inputArgElement.addAttribute(fac.createOMAttribute(VALUE, nullNS, inputArgument.getValue().toString())); + } + inputElement.addChild(inputArgElement); + } + return inputElement; + } +} diff --git a/modules/core/src/main/java/org/apache/synapse/config/xml/ScatterGatherMediatorFactory.java b/modules/core/src/main/java/org/apache/synapse/config/xml/ScatterGatherMediatorFactory.java index f6bbb54dc5..a7d4a9d07d 100644 --- a/modules/core/src/main/java/org/apache/synapse/config/xml/ScatterGatherMediatorFactory.java +++ b/modules/core/src/main/java/org/apache/synapse/config/xml/ScatterGatherMediatorFactory.java @@ -61,7 +61,6 @@ public class ScatterGatherMediatorFactory extends AbstractMediatorFactory { private static final QName ATT_MAX_MESSAGES = new QName("max-messages"); private static final QName SEQUENCE_Q = new QName(XMLConfigConstants.SYNAPSE_NAMESPACE, "sequence"); private static final QName PARALLEL_EXEC_Q = new QName("parallel-execution"); - private static final QName RESULT_TARGET_Q = new QName("result-target"); private static final QName ROOT_ELEMENT_Q = new QName("root-element"); private static final QName CONTENT_TYPE_Q = new QName("content-type"); diff --git a/modules/core/src/main/java/org/apache/synapse/config/xml/SynapsePathSerializer.java b/modules/core/src/main/java/org/apache/synapse/config/xml/SynapsePathSerializer.java index 71078aa340..3fd9a64310 100644 --- a/modules/core/src/main/java/org/apache/synapse/config/xml/SynapsePathSerializer.java +++ b/modules/core/src/main/java/org/apache/synapse/config/xml/SynapsePathSerializer.java @@ -80,7 +80,7 @@ public static OMElement serializePath(SynapsePath path, String expression, attribName, nullNS, "{${" + expression.substring(1, expression.length() - 1) + "}}")); } else { elem.addAttribute(elem.getOMFactory().createOMAttribute( - attribName, nullNS, "{${" + expression + "}")); + attribName, nullNS, "${" + expression + "}")); } } diff --git a/modules/core/src/main/java/org/apache/synapse/mediators/MediatorWorker.java b/modules/core/src/main/java/org/apache/synapse/mediators/MediatorWorker.java index 8f68925cbe..8ef19e1ce0 100644 --- a/modules/core/src/main/java/org/apache/synapse/mediators/MediatorWorker.java +++ b/modules/core/src/main/java/org/apache/synapse/mediators/MediatorWorker.java @@ -33,7 +33,7 @@ import org.apache.synapse.continuation.SeqContinuationState; import org.apache.synapse.debug.SynapseDebugManager; import org.apache.synapse.mediators.base.SequenceMediator; -import org.apache.synapse.mediators.v2.ScatterGatherUtils; +import org.apache.synapse.mediators.v2.Utils; import org.apache.synapse.util.logging.LoggingUtils; /** @@ -96,7 +96,7 @@ public void run() { boolean result = seq.mediate(synCtx); // If this is a scatter message, then we need to use the continuation state and continue the mediation - if (ScatterGatherUtils.isScatterMessage(synCtx) && result) { + if (Utils.isScatterMessage(synCtx) && result) { SeqContinuationState seqContinuationState = (SeqContinuationState) ContinuationStackManager.peakContinuationStateStack(synCtx); if (seqContinuationState == null) { return; @@ -143,7 +143,7 @@ public void run() { debugManager.advertiseMediationFlowTerminatePoint(synCtx); debugManager.releaseMediationFlowLock(); } - if (RuntimeStatisticCollector.isStatisticsEnabled() && !ScatterGatherUtils.isScatterMessage(synCtx)) { + if (RuntimeStatisticCollector.isStatisticsEnabled() && !Utils.isScatterMessage(synCtx)) { this.statisticsCloseEventListener.invokeCloseEventEntry(synCtx); } } diff --git a/modules/core/src/main/java/org/apache/synapse/mediators/base/SequenceMediator.java b/modules/core/src/main/java/org/apache/synapse/mediators/base/SequenceMediator.java index c357b31d49..ef52d68d2f 100644 --- a/modules/core/src/main/java/org/apache/synapse/mediators/base/SequenceMediator.java +++ b/modules/core/src/main/java/org/apache/synapse/mediators/base/SequenceMediator.java @@ -32,7 +32,7 @@ import org.apache.synapse.aspects.flow.statistics.collectors.OpenEventCollector; import org.apache.synapse.aspects.flow.statistics.collectors.RuntimeStatisticCollector; import org.apache.synapse.aspects.flow.statistics.data.artifact.ArtifactHolder; -import org.apache.synapse.mediators.v2.ScatterGatherUtils; +import org.apache.synapse.mediators.v2.Utils; import org.apache.synapse.transport.customlogsetter.CustomLogSetter; import org.apache.synapse.aspects.ComponentType; import org.apache.synapse.continuation.ContinuationStackManager; @@ -158,7 +158,7 @@ public boolean mediate(MessageContext synCtx) { boolean result = super.mediate(synCtx); - if (result && !skipAddition && !ScatterGatherUtils.isScatterMessage(synCtx)) { + if (result && !skipAddition && !Utils.isScatterMessage(synCtx)) { // if flow completed remove the previously added SeqContinuationState ContinuationStackManager.removeSeqContinuationState(synCtx, sequenceType); } diff --git a/modules/core/src/main/java/org/apache/synapse/mediators/ext/ClassMediator.java b/modules/core/src/main/java/org/apache/synapse/mediators/ext/ClassMediator.java index c7b79aad65..14fb13bfd1 100644 --- a/modules/core/src/main/java/org/apache/synapse/mediators/ext/ClassMediator.java +++ b/modules/core/src/main/java/org/apache/synapse/mediators/ext/ClassMediator.java @@ -19,6 +19,7 @@ package org.apache.synapse.mediators.ext; +import org.apache.axis2.AxisFault; import org.apache.synapse.ManagedLifecycle; import org.apache.synapse.Mediator; import org.apache.synapse.MessageContext; @@ -29,8 +30,14 @@ import org.apache.synapse.core.SynapseEnvironment; import org.apache.synapse.mediators.AbstractMediator; import org.apache.synapse.mediators.MediatorProperty; +import org.apache.synapse.mediators.v2.Utils; +import org.apache.synapse.mediators.v2.ext.AbstractClassMediator; +import org.apache.synapse.mediators.v2.ext.InputArgument; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; /** @@ -54,6 +61,11 @@ public class ClassMediator extends AbstractMediator implements ManagedLifecycle /** Contains Dynamic Expressions/not */ private boolean hasDynamicProperties = false; + private List arguments = new ArrayList<>(); + private HashMap inputArguments = new HashMap<>(); + private String methodName; + private String resultTarget; + /** * Don't use a new instance... do one instance of the object per instance of * this mediator @@ -87,12 +99,16 @@ public boolean mediate(MessageContext synCtx) { boolean result; try { - if (hasDynamicProperties) { - synchronized (mediator) { + if (mediator instanceof AbstractClassMediator) { + result = invokeClassMediatorV2(synCtx); + } else { + if (hasDynamicProperties) { + synchronized (mediator) { + result = updateInstancePropertiesAndMediate(synCtx); + } + } else { result = updateInstancePropertiesAndMediate(synCtx); } - } else { - result = updateInstancePropertiesAndMediate(synCtx); } } catch (Exception e) { // throw Synapse Exception for any exception in class meditor @@ -105,6 +121,47 @@ public boolean mediate(MessageContext synCtx) { return result; } + private boolean invokeClassMediatorV2(MessageContext synCtx) { + + List methodArgs = new ArrayList<>(); + methodArgs.add(synCtx); + arguments.forEach(arg -> methodArgs.add(getArgument(synCtx, arg.name()))); + Method[] methods = mediator.getClass().getMethods(); + Method targetMethod = null; + for (Method method : methods) { + if (method.getName().equals(methodName) && method.getParameterCount() == methodArgs.size()) { + targetMethod = method; + break; + } + } + if (targetMethod == null) { + handleException("No suitable method found for name: " + methodName + " with " + methodArgs.size() + + " arguments for class " + mediator.getClass().getSimpleName(), synCtx); + } + try { + Object result = targetMethod.invoke(mediator, methodArgs.toArray()); + return Utils.setResultTarget(synCtx, resultTarget, result); + } catch (IllegalAccessException | InvocationTargetException e) { + handleException("Error while invoking method: " + methodName + " in class " + + mediator.getClass().getSimpleName(), e, synCtx); + } catch (AxisFault e) { + handleException("Error while setting Class mediator '" + mediator.getClass().getName() + + "' result to body", e, synCtx); + } catch (SynapseException e) { + handleException("Error while setting Class mediator '" + mediator.getClass().getName() + + "' result", e, synCtx); + } + return false; + } + + private Object getArgument(MessageContext context, String argName) { + + if (inputArguments.containsKey(argName)) { + return inputArguments.get(argName).getResolvedArgument(context); + } + return null; + } + public void destroy() { if (log.isDebugEnabled()) { log.debug("Destroying class mediator instance for : " + mediator.getClass()); @@ -181,4 +238,40 @@ private boolean updateInstancePropertiesAndMediate(MessageContext synCtx) { } return mediator.mediate(synCtx); } + + + public void setInputArguments(List inputArguments) { + + inputArguments.forEach(arg -> this.inputArguments.put(arg.getName(), arg)); + } + + public List getInputArguments() { + + return new ArrayList<>(inputArguments.values()); + } + + public void setArguments(List arguments) { + + this.arguments.addAll(arguments); + } + + public void setResultTarget(String resultTarget) { + + this.resultTarget = resultTarget; + } + + public String getResultTarget() { + + return resultTarget; + } + + public void setMethodName(String methodName) { + + this.methodName = methodName; + } + + public String getMethodName() { + + return methodName; + } } diff --git a/modules/core/src/main/java/org/apache/synapse/mediators/v2/ForEachMediatorV2.java b/modules/core/src/main/java/org/apache/synapse/mediators/v2/ForEachMediatorV2.java index ef4afa8ef5..7a1b35be50 100644 --- a/modules/core/src/main/java/org/apache/synapse/mediators/v2/ForEachMediatorV2.java +++ b/modules/core/src/main/java/org/apache/synapse/mediators/v2/ForEachMediatorV2.java @@ -256,7 +256,7 @@ public boolean mediate(MessageContext synCtx, ContinuationState continuationStat // If there are no children and the continuation was triggered from a mediator worker start aggregation // otherwise mediate through the sub branch sequence if (!continuationState.hasChild()) { - if (ScatterGatherUtils.isContinuationTriggeredFromMediatorWorker(synCtx)) { + if (Utils.isContinuationTriggeredFromMediatorWorker(synCtx)) { synLog.traceOrDebug("Continuation is triggered from a mediator worker"); result = true; } else { diff --git a/modules/core/src/main/java/org/apache/synapse/mediators/v2/ScatterGather.java b/modules/core/src/main/java/org/apache/synapse/mediators/v2/ScatterGather.java index 21c0bf9be7..3375f8ad1d 100644 --- a/modules/core/src/main/java/org/apache/synapse/mediators/v2/ScatterGather.java +++ b/modules/core/src/main/java/org/apache/synapse/mediators/v2/ScatterGather.java @@ -24,9 +24,7 @@ import org.apache.axiom.om.OMAbstractFactory; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.util.AXIOMUtil; -import org.apache.axiom.soap.SOAP11Constants; import org.apache.axiom.soap.SOAPEnvelope; -import org.apache.axiom.soap.SOAPFactory; import org.apache.axis2.AxisFault; import org.apache.axis2.Constants; import org.apache.axis2.context.OperationContext; @@ -268,7 +266,7 @@ public boolean mediate(MessageContext synCtx, ContinuationState continuationStat // If there are no children and the continuation was triggered from a mediator worker start aggregation // otherwise mediate through the sub branch sequence if (!continuationState.hasChild()) { - if (ScatterGatherUtils.isContinuationTriggeredFromMediatorWorker(synCtx)) { + if (Utils.isContinuationTriggeredFromMediatorWorker(synCtx)) { synLog.traceOrDebug("Continuation is triggered from a mediator worker"); result = true; } else { @@ -615,7 +613,7 @@ private MessageContext getAggregatedMessage(Aggregate aggregate) { // setting the new JSON payload to the messageContext try { newCtx = MessageHelper.cloneMessageContext(aggregate.getLastMessage(), false, false, true); - SOAPEnvelope newEnvelope = createNewSoapEnvelope(aggregate.getLastMessage().getEnvelope()); + SOAPEnvelope newEnvelope = Utils.createNewSoapEnvelope(aggregate.getLastMessage().getEnvelope()); newCtx.setEnvelope(newEnvelope); JsonUtil.getNewJsonPayload(((Axis2MessageContext) newCtx).getAxis2MessageContext(), new ByteArrayInputStream(jsonArray.toString().getBytes()), true, true); @@ -629,7 +627,7 @@ private MessageContext getAggregatedMessage(Aggregate aggregate) { setXMLResultToRootOMElement(rootElement, aggregate); try { newCtx = MessageHelper.cloneMessageContext(aggregate.getLastMessage(), false, false, true); - SOAPEnvelope newEnvelope = createNewSoapEnvelope(aggregate.getLastMessage().getEnvelope()); + SOAPEnvelope newEnvelope = Utils.createNewSoapEnvelope(aggregate.getLastMessage().getEnvelope()); newEnvelope.getBody().addChild(rootElement); newCtx.setEnvelope(newEnvelope); } catch (AxisFault axisFault) { @@ -647,17 +645,6 @@ private MessageContext getAggregatedMessage(Aggregate aggregate) { return newCtx; } - private SOAPEnvelope createNewSoapEnvelope(SOAPEnvelope envelope) { - - SOAPFactory fac; - if (SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(envelope.getBody().getNamespace().getNamespaceURI())) { - fac = OMAbstractFactory.getSOAP11Factory(); - } else { - fac = OMAbstractFactory.getSOAP12Factory(); - } - return fac.getDefaultEnvelope(); - } - public SynapsePath getCorrelateExpression() { return correlateExpression; diff --git a/modules/core/src/main/java/org/apache/synapse/mediators/v2/ScatterGatherUtils.java b/modules/core/src/main/java/org/apache/synapse/mediators/v2/ScatterGatherUtils.java deleted file mode 100644 index 8849923b63..0000000000 --- a/modules/core/src/main/java/org/apache/synapse/mediators/v2/ScatterGatherUtils.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) 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.apache.synapse.mediators.v2; - -import org.apache.synapse.MessageContext; -import org.apache.synapse.SynapseConstants; - -public class ScatterGatherUtils { - - /** - * Check whether the message is a scatter message or not - * - * @param synCtx MessageContext - * @return true if the message is a scatter message - */ - public static boolean isScatterMessage(MessageContext synCtx) { - - Boolean isScatterMessage = (Boolean) synCtx.getProperty(SynapseConstants.SCATTER_MESSAGES); - return isScatterMessage != null && isScatterMessage; - } - - /** - * Check whether the message is a foreach message or not - * - * @param synCtx MessageContext - * @return true if the message is a foreach message - */ - public static boolean isContinuationTriggeredFromMediatorWorker(MessageContext synCtx) { - - Boolean isContinuationTriggeredMediatorWorker = - (Boolean) synCtx.getProperty(SynapseConstants.CONTINUE_FLOW_TRIGGERED_FROM_MEDIATOR_WORKER); - return isContinuationTriggeredMediatorWorker != null && isContinuationTriggeredMediatorWorker; - } -} diff --git a/modules/core/src/main/java/org/apache/synapse/mediators/v2/Utils.java b/modules/core/src/main/java/org/apache/synapse/mediators/v2/Utils.java new file mode 100644 index 0000000000..6e27cd7e21 --- /dev/null +++ b/modules/core/src/main/java/org/apache/synapse/mediators/v2/Utils.java @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com) 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.apache.synapse.mediators.v2; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSyntaxException; +import org.apache.axiom.om.OMAbstractFactory; +import org.apache.axiom.om.OMElement; +import org.apache.axiom.soap.SOAP11Constants; +import org.apache.axiom.soap.SOAPEnvelope; +import org.apache.axiom.soap.SOAPFactory; +import org.apache.axis2.AxisFault; +import org.apache.axis2.util.JavaUtils; +import org.apache.commons.logging.Log; +import org.apache.synapse.MessageContext; +import org.apache.synapse.SynapseConstants; +import org.apache.synapse.SynapseException; +import org.apache.synapse.commons.json.JsonUtil; +import org.apache.synapse.config.SynapseConfigUtils; +import org.apache.synapse.config.xml.SynapsePath; +import org.apache.synapse.config.xml.XMLConfigConstants; +import org.apache.synapse.core.axis2.Axis2MessageContext; + +import java.io.ByteArrayInputStream; +import javax.xml.namespace.QName; + +public class Utils { + + /** + * Check whether the message is a scatter message or not + * + * @param synCtx MessageContext + * @return true if the message is a scatter message + */ + public static boolean isScatterMessage(MessageContext synCtx) { + + Boolean isScatterMessage = (Boolean) synCtx.getProperty(SynapseConstants.SCATTER_MESSAGES); + return isScatterMessage != null && isScatterMessage; + } + + /** + * Check whether the message is a foreach message or not + * + * @param synCtx MessageContext + * @return true if the message is a foreach message + */ + public static boolean isContinuationTriggeredFromMediatorWorker(MessageContext synCtx) { + + Boolean isContinuationTriggeredMediatorWorker = + (Boolean) synCtx.getProperty(SynapseConstants.CONTINUE_FLOW_TRIGGERED_FROM_MEDIATOR_WORKER); + return isContinuationTriggeredMediatorWorker != null && isContinuationTriggeredMediatorWorker; + } + + public static Object convertValue(String value, String type, Log log) { + + if (type == null) { + return value; + } + try { + XMLConfigConstants.VARIABLE_DATA_TYPES dataType = XMLConfigConstants.VARIABLE_DATA_TYPES.valueOf(type); + switch (dataType) { + case BOOLEAN: + return JavaUtils.isTrueExplicitly(value); + case DOUBLE: + return Double.parseDouble(value); + case INTEGER: + return Integer.parseInt(value); + case LONG: + return Long.parseLong(value); + case XML: + return buildOMElement(value); + case JSON: + return buildJSONElement(value, log); + default: + return value; + } + } catch (IllegalArgumentException e) { + String msg = "Unknown type : " + type + " for the variable mediator or the " + + "variable value cannot be converted into the specified type."; + log.error(msg, e); + throw new SynapseException(msg, e); + } + } + + public static OMElement buildOMElement(String xml) { + + if (xml == null) { + return null; + } + OMElement result = SynapseConfigUtils.stringToOM(xml); + result.buildWithAttachments(); + return result; + } + + private static JsonElement buildJSONElement(String jsonPayload, Log log) { + + JsonParser jsonParser = new JsonParser(); + try { + return jsonParser.parse(jsonPayload); + } catch (JsonSyntaxException ex) { + // Enclosing using quotes due to the following issue + // https://github.com/google/gson/issues/1286 + String enclosed = "\"" + jsonPayload + "\""; + try { + return jsonParser.parse(enclosed); + } catch (JsonSyntaxException e) { + // log the original exception and discard the new exception + log.error("Malformed JSON payload : " + jsonPayload, ex); + return null; + } + } + } + + private static boolean isXMLType(String type) { + + return type != null && XMLConfigConstants.VARIABLE_DATA_TYPES.XML.equals(XMLConfigConstants.VARIABLE_DATA_TYPES.valueOf(type)); + } + + private static boolean isStringType(String type) { + + return type != null && XMLConfigConstants.VARIABLE_DATA_TYPES.STRING.equals(XMLConfigConstants.VARIABLE_DATA_TYPES.valueOf(type)); + } + + public static Object getResolvedValue(MessageContext synCtx, SynapsePath expression, Object value, String type, Log log) { + + if (value != null) { + return value; + } else { + if (expression != null) { + if (isXMLType(type)) { + return Utils.buildOMElement(expression.stringValueOf(synCtx)); + } else if (isStringType(type)) { + return expression.stringValueOf(synCtx); + } + return convertExpressionResult(expression.objectValueOf(synCtx), type, expression.getExpression(), log); + } + } + return null; + } + + /** + * Convert the evaluated value to the expected data type. + * + * @param evaluatedValue Evaluated value to be converted + * @param type Expected data type + * @return Converted value + */ + private static Object convertExpressionResult(Object evaluatedValue, String type, String expression, Log log) { + + if (type == null) { + return evaluatedValue; + } + + if (evaluatedValue instanceof JsonPrimitive) { + return convertJsonPrimitive((JsonPrimitive) evaluatedValue, type, expression, log); + } + + XMLConfigConstants.VARIABLE_DATA_TYPES dataType = XMLConfigConstants.VARIABLE_DATA_TYPES.valueOf(type); + switch (dataType) { + case BOOLEAN: + if (!(evaluatedValue instanceof Boolean)) { + handleDataTypeException("BOOLEAN", expression, log); + } + break; + case DOUBLE: + if (!(evaluatedValue instanceof Double)) { + handleDataTypeException("DOUBLE", expression, log); + } + break; + case INTEGER: + if (!(evaluatedValue instanceof Integer)) { + handleDataTypeException("INTEGER", expression, log); + } + break; + case LONG: + if (!(evaluatedValue instanceof Long)) { + handleDataTypeException("LONG", expression, log); + } + break; + case XML: + if (!(evaluatedValue instanceof OMElement)) { + handleDataTypeException("XML", expression, log); + } + break; + case JSON: + if (!(evaluatedValue instanceof JsonElement)) { + handleDataTypeException("JSON", expression, log); + } + break; + default: + } + return evaluatedValue; + } + + /** + * Convert the JSON primitive to the expected data type. + * + * @param jsonPrimitive JSON primitive to be converted + * @param type Expected data type + * @return Converted JSON primitive + */ + private static Object convertJsonPrimitive(JsonPrimitive jsonPrimitive, String type, String expression, Log log) { + + XMLConfigConstants.VARIABLE_DATA_TYPES dataType = XMLConfigConstants.VARIABLE_DATA_TYPES.valueOf(type); + switch (dataType) { + case BOOLEAN: + if (jsonPrimitive.isBoolean()) { + return jsonPrimitive.getAsBoolean(); + } else { + handleDataTypeException("BOOLEAN", expression, log); + } + case DOUBLE: + if (jsonPrimitive.isNumber()) { + return jsonPrimitive.getAsDouble(); + } else { + handleDataTypeException("DOUBLE", expression, log); + } + case INTEGER: + if (jsonPrimitive.isNumber()) { + return jsonPrimitive.getAsInt(); + } else { + handleDataTypeException("INTEGER", expression, log); + } + case LONG: + if (jsonPrimitive.isNumber()) { + return jsonPrimitive.getAsLong(); + } else { + handleDataTypeException("LONG", expression, log); + } + default: + return jsonPrimitive.getAsString(); + } + } + + /** + * This method will throw a SynapseException with a message indicating that the expression result does not match + * the expected data type. + * + * @param dataType Expected data type + */ + private static void handleDataTypeException(String dataType, String expression, Log log) { + + String msg = "Expression '${" + expression + "}' result does not match the expected data type '" + dataType + "'"; + log.error(msg); + throw new SynapseException(msg); + } + + public static SOAPEnvelope createNewSoapEnvelope(SOAPEnvelope envelope) { + + SOAPFactory fac; + if (SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(envelope.getBody().getNamespace().getNamespaceURI())) { + fac = OMAbstractFactory.getSOAP11Factory(); + } else { + fac = OMAbstractFactory.getSOAP12Factory(); + } + return fac.getDefaultEnvelope(); + } + + /** + * This method is used to check whether the return object is a valid variable type + * + * @param returnObject return object + * @return true if the return object is a valid type + */ + public static boolean isValidReturnObjectType(Object returnObject) { + + return returnObject instanceof String || + returnObject instanceof Boolean || + returnObject instanceof Integer || + returnObject instanceof Long || + returnObject instanceof Double || + returnObject instanceof OMElement || + returnObject instanceof JsonElement; + } + + public static boolean isTargetBody(String resultTarget) { + + return "body".equalsIgnoreCase(resultTarget); + } + + public static boolean isTargetNone(String resultTarget) { + + return "none".equalsIgnoreCase(resultTarget); + } + + /** + * This method is used to set the result to the target + * + * @param synCtx MessageContext + * @param resultTarget Target to set the result + * @param result Result object to set + * @return true if the result is set successfully + * @throws AxisFault + * @throws SynapseException + */ + public static boolean setResultTarget(MessageContext synCtx, String resultTarget, Object result) throws AxisFault, + SynapseException { + + if (Utils.isTargetNone(resultTarget)) { + return true; + } + if (result != null) { + if (Utils.isTargetBody(resultTarget)) { + // set result to body + if (result instanceof JsonElement) { + JsonUtil.getNewJsonPayload(((Axis2MessageContext) synCtx).getAxis2MessageContext(), new + ByteArrayInputStream(result.toString().getBytes()), true, true); + } else { + OMElement rootElement = OMAbstractFactory.getOMFactory().createOMElement(new QName( + "result")); + rootElement.setText(result.toString()); + SOAPEnvelope newEnvelope = Utils.createNewSoapEnvelope(synCtx.getEnvelope()); + newEnvelope.getBody().addChild(rootElement); + synCtx.setEnvelope(newEnvelope); + } + } else { + // set result to variable + if (Utils.isValidReturnObjectType(result)) { + synCtx.setVariable(resultTarget, result); + } else { + throw new SynapseException("Return object type is not supported. Supported types are " + + "String, Boolean, Integer, Long, Double, JsonElement, OMElement"); + } + } + return true; + } else { + throw new SynapseException("Return object is null. Cannot set null object to the target : " + + resultTarget); + } + } +} diff --git a/modules/core/src/main/java/org/apache/synapse/mediators/v2/VariableMediator.java b/modules/core/src/main/java/org/apache/synapse/mediators/v2/VariableMediator.java index 149c50e568..b2fd333d3e 100644 --- a/modules/core/src/main/java/org/apache/synapse/mediators/v2/VariableMediator.java +++ b/modules/core/src/main/java/org/apache/synapse/mediators/v2/VariableMediator.java @@ -18,20 +18,12 @@ package org.apache.synapse.mediators.v2; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSyntaxException; import org.apache.axiom.om.OMElement; -import org.apache.axis2.util.JavaUtils; import org.apache.synapse.MessageContext; -import org.apache.synapse.SynapseException; import org.apache.synapse.SynapseLog; import org.apache.synapse.aspects.ComponentType; import org.apache.synapse.aspects.flow.statistics.collectors.CloseEventCollector; -import org.apache.synapse.config.SynapseConfigUtils; import org.apache.synapse.config.xml.SynapsePath; -import org.apache.synapse.config.xml.XMLConfigConstants; import org.apache.synapse.mediators.AbstractMediator; import java.util.Set; @@ -127,7 +119,7 @@ public void setValue(String value) { public void setValue(String value, String type) { this.type = type; - this.value = convertValue(value, type); + this.value = Utils.convertValue(value, type, log); } public String getType() { @@ -165,168 +157,7 @@ public void setExpression(SynapsePath expression, String type) { private Object getResultValue(MessageContext synCtx) { - if (value != null) { - return value; - } else { - if (expression != null) { - if (isXMLType(type)) { - return buildOMElement(expression.stringValueOf(synCtx)); - } else if (isStringType(type)) { - return expression.stringValueOf(synCtx); - } - return convertExpressionResult(expression.objectValueOf(synCtx), type); - } - } - return null; - } - - private boolean isXMLType(String type) { - - return type != null && XMLConfigConstants.VARIABLE_DATA_TYPES.XML.equals(XMLConfigConstants.VARIABLE_DATA_TYPES.valueOf(type)); - } - - private boolean isStringType(String type) { - - return type != null && XMLConfigConstants.VARIABLE_DATA_TYPES.STRING.equals(XMLConfigConstants.VARIABLE_DATA_TYPES.valueOf(type)); - } - - private Object convertValue(String value, String type) { - - if (type == null) { - return value; - } - - try { - XMLConfigConstants.VARIABLE_DATA_TYPES dataType = XMLConfigConstants.VARIABLE_DATA_TYPES.valueOf(type); - switch (dataType) { - case BOOLEAN: - return JavaUtils.isTrueExplicitly(value); - case DOUBLE: - return Double.parseDouble(value); - case INTEGER: - return Integer.parseInt(value); - case LONG: - return Long.parseLong(value); - case XML: - return buildOMElement(value); - case JSON: - return buildJSONElement(value); - default: - return value; - } - } catch (IllegalArgumentException e) { - String msg = "Unknown type : " + type + " for the variable mediator or the " + - "variable value cannot be converted into the specified type."; - log.error(msg, e); - throw new SynapseException(msg, e); - } - } - - /** - * Convert the evaluated value to the expected data type. - * - * @param evaluatedValue Evaluated value to be converted - * @param type Expected data type - * @return Converted value - */ - private Object convertExpressionResult(Object evaluatedValue, String type) { - - if (type == null) { - return evaluatedValue; - } - - if (evaluatedValue instanceof JsonPrimitive) { - return convertJsonPrimitive((JsonPrimitive) evaluatedValue, type); - } - - XMLConfigConstants.VARIABLE_DATA_TYPES dataType = XMLConfigConstants.VARIABLE_DATA_TYPES.valueOf(type); - switch (dataType) { - case BOOLEAN: - if (!(evaluatedValue instanceof Boolean)) { - handleDataTypeException("BOOLEAN"); - } - break; - case DOUBLE: - if (!(evaluatedValue instanceof Double)) { - handleDataTypeException("DOUBLE"); - } - break; - case INTEGER: - if (!(evaluatedValue instanceof Integer)) { - handleDataTypeException("INTEGER"); - } - break; - case LONG: - if (!(evaluatedValue instanceof Long)) { - handleDataTypeException("LONG"); - } - break; - case XML: - if (!(evaluatedValue instanceof OMElement)) { - handleDataTypeException("XML"); - } - break; - case JSON: - if (!(evaluatedValue instanceof JsonElement)) { - handleDataTypeException("JSON"); - } - break; - default: - } - return evaluatedValue; - } - - /** - * Convert the JSON primitive to the expected data type. - * - * @param jsonPrimitive JSON primitive to be converted - * @param type Expected data type - * @return Converted JSON primitive - */ - public Object convertJsonPrimitive(JsonPrimitive jsonPrimitive, String type) { - - XMLConfigConstants.VARIABLE_DATA_TYPES dataType = XMLConfigConstants.VARIABLE_DATA_TYPES.valueOf(type); - switch (dataType) { - case BOOLEAN: - if (jsonPrimitive.isBoolean()) { - return jsonPrimitive.getAsBoolean(); - } else { - handleDataTypeException("BOOLEAN"); - } - case DOUBLE: - if (jsonPrimitive.isNumber()) { - return jsonPrimitive.getAsDouble(); - } else { - handleDataTypeException("DOUBLE"); - } - case INTEGER: - if (jsonPrimitive.isNumber()) { - return jsonPrimitive.getAsInt(); - } else { - handleDataTypeException("INTEGER"); - } - case LONG: - if (jsonPrimitive.isNumber()) { - return jsonPrimitive.getAsLong(); - } else { - handleDataTypeException("LONG"); - } - default: - return jsonPrimitive.getAsString(); - } - } - - /** - * This method will throw a SynapseException with a message indicating that the expression result does not match - * the expected data type. - * - * @param dataType Expected data type - */ - private void handleDataTypeException(String dataType) { - - String msg = "Expression '${" + expression + "}' result does not match the expected data type '" + dataType + "'"; - log.error(msg); - throw new SynapseException(msg); + return Utils.getResolvedValue(synCtx, expression, value, type, log); } @Override @@ -339,35 +170,6 @@ public boolean isContentAware() { return contentAware; } - private OMElement buildOMElement(String xml) { - - if (xml == null) { - return null; - } - OMElement result = SynapseConfigUtils.stringToOM(xml); - result.buildWithAttachments(); - return result; - } - - private JsonElement buildJSONElement(String jsonPayload) { - - JsonParser jsonParser = new JsonParser(); - try { - return jsonParser.parse(jsonPayload); - } catch (JsonSyntaxException ex) { - // Enclosing using quotes due to the following issue - // https://github.com/google/gson/issues/1286 - String enclosed = "\"" + jsonPayload + "\""; - try { - return jsonParser.parse(enclosed); - } catch (JsonSyntaxException e) { - // log the original exception and discard the new exception - log.error("Malformed JSON payload : " + jsonPayload, ex); - return null; - } - } - } - @Override public String getMediatorName() { diff --git a/modules/core/src/main/java/org/apache/synapse/mediators/v2/ext/AbstractClassMediator.java b/modules/core/src/main/java/org/apache/synapse/mediators/v2/ext/AbstractClassMediator.java new file mode 100644 index 0000000000..fd63e2dd51 --- /dev/null +++ b/modules/core/src/main/java/org/apache/synapse/mediators/v2/ext/AbstractClassMediator.java @@ -0,0 +1,62 @@ +package org.apache.synapse.mediators.v2.ext; + +import org.apache.synapse.MessageContext; +import org.apache.synapse.mediators.AbstractMediator; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.PARAMETER; + +/** + * This is the super class for custom class mediators. The user can extend this class and implement a custom method with + * the allowed argument types. The method parameters should be annotated with the @Arg annotation to specify the + * argument name and the type. The method may return a value if needed. The return type should be one of the allowed. + */ +public class AbstractClassMediator extends AbstractMediator { + + @Override + public boolean mediate(MessageContext synCtx) { + + // User implemented method will be invoked from the Class mediator + return true; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({PARAMETER}) + public @interface Arg { + + String name(); + + ArgumentType type(); + } + + public enum ArgumentType { + STRING("String"), + BOOLEAN("Boolean"), + INTEGER("Integer"), + LONG("Long"), + DOUBLE("Double"), + JSON("Json"), + XML("Xml"); + + private final String typeName; + + ArgumentType(String typeName) { + + this.typeName = typeName; + } + + public String getTypeName() { + + return typeName; + } + + @Override + public String toString() { + + return typeName; + } + } +} diff --git a/modules/core/src/main/java/org/apache/synapse/mediators/v2/ext/InputArgument.java b/modules/core/src/main/java/org/apache/synapse/mediators/v2/ext/InputArgument.java new file mode 100644 index 0000000000..7d1b429f4a --- /dev/null +++ b/modules/core/src/main/java/org/apache/synapse/mediators/v2/ext/InputArgument.java @@ -0,0 +1,76 @@ +/* + * 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.apache.synapse.mediators.v2.ext; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.synapse.MessageContext; +import org.apache.synapse.config.xml.SynapsePath; +import org.apache.synapse.mediators.v2.Utils; + +public class InputArgument { + + private static final Log log = LogFactory.getLog(InputArgument.class); + private String name; + private SynapsePath expression; + private Object value; + private String type; + + public InputArgument(String name) { + + this.name = name; + } + + public Object getResolvedArgument(MessageContext synCtx) { + + return Utils.getResolvedValue(synCtx, expression, value, type, log); + } + + public void setExpression(SynapsePath expression, String type) { + + this.type = type; + this.expression = expression; + } + + public void setValue(String value, String type) { + + this.type = type; + this.value = Utils.convertValue(value, type, log); + } + + public String getName() { + + return name; + } + + public SynapsePath getExpression() { + + return expression; + } + + public Object getValue() { + + return value; + } + + public String getType() { + + return type; + } +} diff --git a/modules/core/src/test/java/org/apache/synapse/config/xml/ClassMediatorSerializationTest.java b/modules/core/src/test/java/org/apache/synapse/config/xml/ClassMediatorSerializationTest.java index 619795b014..82d77046c7 100644 --- a/modules/core/src/test/java/org/apache/synapse/config/xml/ClassMediatorSerializationTest.java +++ b/modules/core/src/test/java/org/apache/synapse/config/xml/ClassMediatorSerializationTest.java @@ -72,4 +72,17 @@ public void testClassMediatorLoadException() throws Exception { assertTrue(failed); } + + public void testV2ClassMediator() throws Exception { + + String inputXml = "" + + "" + + "" + + "" + + "" + + ""; + assertTrue(serialization(inputXml, classMediatorFactory, classMediatorSerializer)); + assertTrue(serialization(inputXml, classMediatorSerializer)); + } } diff --git a/modules/extensions/src/main/java/org/apache/synapse/mediators/bsf/ScriptMediator.java b/modules/extensions/src/main/java/org/apache/synapse/mediators/bsf/ScriptMediator.java index 30d1ee615e..4410a10393 100644 --- a/modules/extensions/src/main/java/org/apache/synapse/mediators/bsf/ScriptMediator.java +++ b/modules/extensions/src/main/java/org/apache/synapse/mediators/bsf/ScriptMediator.java @@ -27,6 +27,7 @@ import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMText; import org.apache.bsf.xml.XMLHelper; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.synapse.MessageContext; @@ -43,6 +44,8 @@ import org.apache.synapse.mediators.bsf.access.control.config.AccessControlConfig; import org.apache.synapse.mediators.bsf.access.control.config.AccessControlListType; import org.apache.synapse.mediators.eip.EIPUtils; +import org.apache.synapse.mediators.v2.Utils; +import org.apache.synapse.mediators.v2.ext.InputArgument; import org.graalvm.polyglot.Context; import org.mozilla.javascript.ClassShutter; import org.mozilla.javascript.ContextFactory; @@ -52,6 +55,7 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; @@ -178,6 +182,8 @@ public class ScriptMediator extends AbstractMediator { * Store java method access config */ private AccessControlConfig nativeObjectAccessControlConfig; + private final List inputArgumentList = new ArrayList<>(); + private String resultTarget; /** * Create a script mediator for the given language and given script source. @@ -293,11 +299,17 @@ private boolean invokeScript(MessageContext synCtx) { Object returnObject; if (key != null) { returnObject = mediateWithExternalScript(synCtx, context); + // If result target is set, this is V2 script mediator + // Set the result to the target and returnValue to true + if (StringUtils.isNotBlank(resultTarget)) { + returnValue = Utils.setResultTarget(synCtx, resultTarget, returnObject); + } else { + returnValue = !(returnObject != null && returnObject instanceof Boolean) || (Boolean) returnObject; + } } else { returnObject = mediateForInlineScript(synCtx, context); + returnValue = !(returnObject != null && returnObject instanceof Boolean) || (Boolean) returnObject; } - returnValue = !(returnObject != null && returnObject instanceof Boolean) || (Boolean) returnObject; - } catch (ScriptException e) { handleException("The script engine returned an error executing the " + (key == null ? "inlined " : "external ") + language + " script" + @@ -355,7 +367,13 @@ private Object mediateWithExternalScript(MessageContext synCtx, Context context) processJSONPayload(synCtx, scriptMC); Invocable invocableScript = (Invocable) sew.getEngine(); - obj = invocableScript.invokeFunction(function, new Object[]{scriptMC}); + List scriptArgs = new ArrayList<>(); + // First argument is always the ScriptMessageContext + scriptArgs.add(scriptMC); + for (InputArgument inputArgument : inputArgumentList) { + scriptArgs.add(inputArgument.getResolvedArgument(synCtx)); + } + obj = invocableScript.invokeFunction(function, scriptArgs.toArray()); } finally { if(sew != null){ // return engine to front of queue or drop if queue is full (i.e. if getNewScriptEngine() spawns a new engine) @@ -822,4 +840,23 @@ private void readAccessControlConfigurations(Properties properties) { } } + public void setInputArgumentMap(List inputArgumentList) { + + this.inputArgumentList.addAll(inputArgumentList); + } + + public List getInputArgumentList() { + + return inputArgumentList; + } + + public void setResultTarget(String resultTarget) { + + this.resultTarget = resultTarget; + } + + public String getResultTarget() { + + return resultTarget; + } } diff --git a/modules/extensions/src/main/java/org/apache/synapse/mediators/bsf/ScriptMediatorFactory.java b/modules/extensions/src/main/java/org/apache/synapse/mediators/bsf/ScriptMediatorFactory.java index de581c357f..6925db6f4c 100644 --- a/modules/extensions/src/main/java/org/apache/synapse/mediators/bsf/ScriptMediatorFactory.java +++ b/modules/extensions/src/main/java/org/apache/synapse/mediators/bsf/ScriptMediatorFactory.java @@ -21,6 +21,7 @@ import org.apache.axiom.om.OMAttribute; import org.apache.axiom.om.OMElement; +import org.apache.commons.lang3.StringUtils; import org.apache.synapse.Mediator; import org.apache.synapse.SynapseConstants; import org.apache.synapse.SynapseException; @@ -29,7 +30,7 @@ import org.apache.synapse.config.xml.XMLConfigConstants; import org.apache.synapse.mediators.Value; import org.apache.synapse.config.xml.ValueFactory; -import org.mozilla.javascript.Context; +import org.apache.synapse.mediators.v2.ext.InputArgument; import javax.xml.namespace.QName; import java.util.Iterator; @@ -111,6 +112,15 @@ public Mediator createSpecificMediator(OMElement elem, Properties properties) { String functionName = (functionAtt == null ? null : functionAtt.getAttributeValue()); mediator = new ScriptMediator(langAtt.getAttributeValue(), includeKeysMap, generatedKey, functionName,classLoader); + String targetAtt = elem.getAttributeValue(RESULT_TARGET_Q); + if (StringUtils.isNotBlank(targetAtt)) { + mediator.setResultTarget(targetAtt); + OMElement inputArgsElement = elem.getFirstChildWithName(INPUTS); + if (inputArgsElement != null) { + List inputArgsMap = getInputArguments(inputArgsElement, "script"); + mediator.setInputArgumentMap(inputArgsMap); + } + } } else { String language = langAtt.getAttributeValue(); if (language.equals(JAVA_SCRIPT) && diff --git a/modules/extensions/src/main/java/org/apache/synapse/mediators/bsf/ScriptMediatorSerializer.java b/modules/extensions/src/main/java/org/apache/synapse/mediators/bsf/ScriptMediatorSerializer.java index 1aebdeeabd..243cb97de6 100644 --- a/modules/extensions/src/main/java/org/apache/synapse/mediators/bsf/ScriptMediatorSerializer.java +++ b/modules/extensions/src/main/java/org/apache/synapse/mediators/bsf/ScriptMediatorSerializer.java @@ -20,8 +20,10 @@ import org.apache.axiom.om.OMElement; import org.apache.axiom.om.impl.llom.OMTextImpl; +import org.apache.commons.lang3.StringUtils; import org.apache.synapse.Mediator; import org.apache.synapse.config.xml.AbstractMediatorSerializer; +import org.apache.synapse.config.xml.InputArgumentSerializer; import org.apache.synapse.config.xml.ValueSerializer; import org.apache.synapse.config.xml.XMLConfigConstants; import org.apache.synapse.mediators.Value; @@ -59,6 +61,11 @@ public OMElement serializeSpecificMediator(Mediator m) { if (!function.equals("mediate")) { script.addAttribute(fac.createOMAttribute("function", nullNS, function)); } + if (StringUtils.isNotBlank(scriptMediator.getResultTarget())) { + // If result target is set, this is V2 script mediator + script.addAttribute(fac.createOMAttribute("result-target", nullNS, scriptMediator.getResultTarget())); + script.addChild(InputArgumentSerializer.serializeInputArguments(scriptMediator.getInputArgumentList())); + } } else { script.addAttribute(fac.createOMAttribute("language", nullNS, language)); OMTextImpl textData = (OMTextImpl) fac.createOMText( diff --git a/modules/extensions/src/test/java/org/apache/synapse/mediators/bsf/ScriptMediatorSerializationTest.java b/modules/extensions/src/test/java/org/apache/synapse/mediators/bsf/ScriptMediatorSerializationTest.java index 10f4b7fc2f..5c23632c48 100644 --- a/modules/extensions/src/test/java/org/apache/synapse/mediators/bsf/ScriptMediatorSerializationTest.java +++ b/modules/extensions/src/test/java/org/apache/synapse/mediators/bsf/ScriptMediatorSerializationTest.java @@ -63,4 +63,15 @@ public void testInlineScriptMediatorSerializationScenarioTwo() throws XMLCompari assertTrue(serialization(inputXml, mediatorFactory, scriptMediatorSerializer)); assertTrue(serialization(inputXml, scriptMediatorSerializer)); } + + public void testV2ScriptMediator() throws XMLComparisonException { + String inputXml = ""; + assertTrue(serialization(inputXml, mediatorFactory, scriptMediatorSerializer)); + assertTrue(serialization(inputXml, scriptMediatorSerializer)); + } }