/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.bsf;
import org.apache.axiom.om.OMAttribute;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axiom.om.OMNode;
import org.apache.axiom.soap.SOAPBody;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPHeader;
import org.apache.axiom.soap.SOAPHeaderBlock;
import org.apache.axis2.AxisFault;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.addressing.RelatesTo;
import org.apache.axis2.context.OperationContext;
import org.apache.bsf.xml.XMLHelper;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.ContinuationState;
import org.apache.synapse.FaultHandler;
import org.apache.synapse.Mediator;
import org.apache.synapse.MessageContext;
import org.apache.synapse.SynapseConstants;
import org.apache.synapse.commons.json.JsonUtil;
import org.apache.synapse.config.SynapseConfiguration;
import org.apache.synapse.core.SynapseEnvironment;
import org.apache.synapse.core.axis2.Axis2MessageContext;
import org.apache.synapse.endpoints.Endpoint;
import org.mozilla.javascript.ConsString;
import org.mozilla.javascript.NativeArray;
import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.Wrapper;
import org.mozilla.javascript.xml.XMLObject;
import org.apache.synapse.config.xml.XMLConfigConstants;
import org.apache.http.protocol.HTTP;
import java.util.*;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
/**
* ScriptMessageContext decorates the Synapse MessageContext adding methods to use the
* message payload XML in a way natural to the scripting languageS
*/
@SuppressWarnings({"UnusedDeclaration"})
public class ScriptMessageContext implements MessageContext {
private static final Log logger = LogFactory.getLog(ScriptMessageContext.class.getName());
private static final String JSON_OBJECT = "JSON_OBJECT";
private static final String JSON_TEXT = "JSON_TEXT";
/** The actual Synapse message context reference */
private final MessageContext mc;
/** The OMElement to scripting language object converter for the selected language */
private final XMLHelper xmlHelper;
private ScriptEngine scriptEngine;
public ScriptMessageContext(MessageContext mc, XMLHelper xmlHelper) {
this.mc = mc;
this.xmlHelper = xmlHelper;
}
/**
* Get the XML representation of SOAP Body payload.
* The payload is the first element inside the SOAP <Body> tags
*
* @return the XML SOAP Body
* @throws ScriptException in-case of an error in getting
* the XML representation of SOAP Body payload
*/
public Object getPayloadXML() throws ScriptException {
return xmlHelper.toScriptXML(mc.getEnvelope().getBody().getFirstElement());
}
/**
* Set the SOAP body payload from XML
*
* @param payload Message payload
* @throws ScriptException For errors in converting xml To OM
* @throws OMException For errors in OM manipulation
*/
public void setPayloadXML(Object payload) throws OMException, ScriptException {
SOAPBody body = mc.getEnvelope().getBody();
OMElement firstChild = body.getFirstElement();
OMElement omElement = xmlHelper.toOMElement(payload);
if (firstChild == null) {
body.addChild(omElement);
} else {
firstChild.insertSiblingAfter(omElement);
firstChild.detach();
}
}
/**
* Get the JSON object representation of the JSON message body of the request.
*
* @return JSON object of the message body
*/
public Object getPayloadJSON() {
return jsonObject(mc);
}
public Object getJsonText() {
if (mc == null) {
return "";
}
Object text = mc.getProperty(JSON_TEXT);
return text == null ? "{}" : text;
}
/**
* Get the Message Payload as a text
*
* @return Payload as text
*/
public String getPayloadText() {
if (JsonUtil.hasAJsonPayload(((Axis2MessageContext) mc).getAxis2MessageContext())) {
return JsonUtil.jsonPayloadToString(((Axis2MessageContext) mc).getAxis2MessageContext());
} else {
return mc.getEnvelope().toString();
}
}
/**
/**
* Saves the payload of this message context as a JSON payload.
*
* @param jsonPayload Javascript native object to be set as the message body
* @throws ScriptException in case of creating a JSON object out of
* the javascript native object.
*/
public void setPayloadJSON0(Object jsonPayload) throws ScriptException {
org.apache.axis2.context.MessageContext messageContext;
messageContext = ((Axis2MessageContext) mc).getAxis2MessageContext();
String jsonString;
if (jsonPayload instanceof String) {
jsonString = (String) jsonPayload;
} else {
jsonString = serializeJSON(jsonPayload);
}
try {
JsonUtil.getNewJsonPayload(messageContext, jsonString, true, true);
} catch (AxisFault axisFault) {
throw new ScriptException(axisFault);
}
//JsonUtil.setContentType(messageContext);
Object jsonObject = scriptEngine.eval('(' + jsonString + ')');
setJsonObject(mc, jsonObject);
}
/**
* Saves the JavaScript Object to the message context.
* @param messageContext
* @param jsonObject
* @return
*/
public boolean setJsonObject(MessageContext messageContext, Object jsonObject) {
if (jsonObject == null) {
logger.error("Setting null JSON object.");
}
messageContext.setProperty(JSON_OBJECT, jsonObject);
return true;
}
/**
* Saves the JSON String to the message context.
* @param messageContext
* @param jsonObject
* @return
*/
public boolean setJsonText(MessageContext messageContext, Object jsonObject) {
if (messageContext == null) {
return false;
}
if (jsonObject == null) {
logger.error("Setting null JSON text.");
}
messageContext.setProperty(JSON_TEXT, jsonObject);
return true;
}
/**
* Returns the JavaScript Object saved in this message context.
* @param messageContext
* @return
*/
public Object jsonObject(MessageContext messageContext) {
if (messageContext == null) {
return null;
}
Object o = messageContext.getProperty(JSON_OBJECT);
if (o == null) {
logger.error("JSON object is null.");
if (this.scriptEngine == null) {
logger.error("Cannot create empty JSON object. ScriptEngine instance not available.");
return null;
}
try {
return this.scriptEngine.eval("({})");
} catch (ScriptException e) {
logger.error("Could not return an empty JSON object. Error>>> " + e.getLocalizedMessage());
}
}
return o;
}
/**
* Set a script engine
*
* @param scriptEngine a ScriptEngine instance
*/
public void setScriptEngine(ScriptEngine scriptEngine) {
this.scriptEngine = scriptEngine;
if (this.scriptEngine == null) {
logger.error("Script engine is invalid.");
}
}
/**
* Add a new SOAP header to the message.
*
* @param mustUnderstand the value for the <code>soapenv:mustUnderstand</code> attribute
* @param content the XML for the new header
* @throws ScriptException if an error occurs when converting the XML to OM
*/
public void addHeader(boolean mustUnderstand, Object content) throws ScriptException {
SOAPEnvelope envelope = mc.getEnvelope();
SOAPFactory factory = (SOAPFactory)envelope.getOMFactory();
SOAPHeader header = envelope.getHeader();
if (header == null) {
header = factory.createSOAPHeader(envelope);
}
OMElement element = xmlHelper.toOMElement(content);
// We can't add the element directly to the SOAPHeader. Instead, we need to copy the
// information over to a SOAPHeaderBlock.
SOAPHeaderBlock headerBlock = header.addHeaderBlock(element.getLocalName(),
element.getNamespace());
for (Iterator it = element.getAllAttributes(); it.hasNext(); ) {
headerBlock.addAttribute((OMAttribute)it.next());
}
headerBlock.setMustUnderstand(mustUnderstand);
OMNode child = element.getFirstOMChild();
while (child != null) {
// Get the next child before addChild will detach the node from its original place.
OMNode next = child.getNextOMSibling();
headerBlock.addChild(child);
child = next;
}
}
/**
* Get the XML representation of the complete SOAP envelope
* @return return an object that represents the payload in the current scripting language
* @throws ScriptException in-case of an error in getting
* the XML representation of SOAP envelope
*/
public Object getEnvelopeXML() throws ScriptException {
return xmlHelper.toScriptXML(mc.getEnvelope());
}
// helpers to set EPRs from a script string
public void setTo(String reference) {
mc.setTo(new EndpointReference(reference));
}
public void setFaultTo(String reference) {
mc.setFaultTo(new EndpointReference(reference));
}
public void setFrom(String reference) {
mc.setFrom(new EndpointReference(reference));
}
public void setReplyTo(String reference) {
mc.setReplyTo(new EndpointReference(reference));
}
// -- all the remainder just use the underlying MessageContext
public SynapseConfiguration getConfiguration() {
return mc.getConfiguration();
}
public void setConfiguration(SynapseConfiguration cfg) {
mc.setConfiguration(cfg);
}
public SynapseEnvironment getEnvironment() {
return mc.getEnvironment();
}
public void setEnvironment(SynapseEnvironment se) {
mc.setEnvironment(se);
}
public Map<String, Object> getContextEntries() {
return mc.getContextEntries();
}
public void setContextEntries(Map<String, Object> entries) {
mc.setContextEntries(entries);
}
public Object getProperty(String key) {
return mc.getProperty(key);
}
public Object getEntry(String key) {
return mc.getEntry(key);
}
public Object getLocalEntry(String key) {
return mc.getLocalEntry(key);
}
public void setProperty(String key, Object value) {
if (value instanceof XMLObject) {
OMElement omElement = null;
try {
omElement = xmlHelper.toOMElement(value);
} catch (ScriptException e) {
mc.setProperty(key, value);
}
if (omElement != null) {
mc.setProperty(key, omElement);
}
} else {
mc.setProperty(key, value);
}
}
public void setProperty(String key, Object value, String scope) {
if (scope == null || XMLConfigConstants.SCOPE_DEFAULT.equals(scope)) {
setProperty(key, value);
} else if (XMLConfigConstants.SCOPE_AXIS2.equals(scope)) {
//Setting property into the Axis2 Message Context
Axis2MessageContext axis2smc = (Axis2MessageContext) mc;
org.apache.axis2.context.MessageContext axis2MessageCtx = axis2smc.getAxis2MessageContext();
axis2MessageCtx.setProperty(key, value);
handleSpecialProperties(key, value, axis2MessageCtx);
} else if (XMLConfigConstants.SCOPE_TRANSPORT.equals(scope)) {
//Setting Transport Headers
Axis2MessageContext axis2smc = (Axis2MessageContext) mc;
org.apache.axis2.context.MessageContext axis2MessageCtx = axis2smc.getAxis2MessageContext();
Object headers = axis2MessageCtx.getProperty(org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS);
if (headers != null && headers instanceof Map) {
Map headersMap = (Map) headers;
headersMap.put(key, value);
}
if (headers == null) {
Map headersMap = new HashMap();
headersMap.put(key, value);
axis2MessageCtx.setProperty(org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS, headersMap);
}
} else if (XMLConfigConstants.SCOPE_OPERATION.equals(scope)) {
Axis2MessageContext axis2smc = (Axis2MessageContext) mc;
org.apache.axis2.context.MessageContext axis2MessageCtx = axis2smc.getAxis2MessageContext();
axis2smc.getAxis2MessageContext().getOperationContext().setProperty(key, value);
}
}
public void removeProperty(String key, String scope) {
if (scope == null || XMLConfigConstants.SCOPE_DEFAULT.equals(scope)) {
Set pros = mc.getPropertyKeySet();
if (pros != null) {
pros.remove(key);
}
} else if (XMLConfigConstants.SCOPE_AXIS2.equals(scope)) {
//Removing property from the Axis2 Message Context
Axis2MessageContext axis2smc = (Axis2MessageContext) mc;
org.apache.axis2.context.MessageContext axis2MessageCtx = axis2smc.getAxis2MessageContext();
axis2MessageCtx.removeProperty(key);
} else if (XMLConfigConstants.SCOPE_TRANSPORT.equals(scope)) {
// Removing transport headers
Axis2MessageContext axis2smc = (Axis2MessageContext) mc;
org.apache.axis2.context.MessageContext axis2MessageCtx = axis2smc.getAxis2MessageContext();
Object headers = axis2MessageCtx.getProperty(org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS);
if (headers != null && headers instanceof Map) {
Map headersMap = (Map) headers;
headersMap.remove(key);
}
} else if (XMLConfigConstants.SCOPE_OPERATION.equals(scope)) {
// Removing operation scope headers
Axis2MessageContext axis2smc = (Axis2MessageContext) mc;
org.apache.axis2.context.MessageContext axis2MessageCtx = axis2smc.getAxis2MessageContext();
OperationContext axis2oc = axis2MessageCtx.getOperationContext();
axis2oc.removeProperty(key);
}
}
private void handleSpecialProperties(String key, Object value,
org.apache.axis2.context.MessageContext messageContext) {
if (org.apache.axis2.Constants.Configuration.MESSAGE_TYPE.equals(key)) {
messageContext.setProperty(org.apache.axis2.Constants.Configuration.CONTENT_TYPE, value);
Object o = messageContext.getProperty(org.apache.axis2.context.MessageContext.TRANSPORT_HEADERS);
Map _headers = (Map) o;
if (_headers != null) {
_headers.remove(HTTP.CONTENT_TYPE);
_headers.put(HTTP.CONTENT_TYPE, value);
}
}
}
public Set getPropertyKeySet() {
return mc.getPropertyKeySet();
}
public Mediator getMainSequence() {
return mc.getMainSequence();
}
public Mediator getFaultSequence() {
return mc.getFaultSequence();
}
public Mediator getSequence(String key) {
return mc.getSequence(key);
}
public OMElement getFormat(String s) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
public Endpoint getEndpoint(String key) {
return mc.getEndpoint(key);
}
public SOAPEnvelope getEnvelope() {
return mc.getEnvelope();
}
public void setEnvelope(SOAPEnvelope envelope) throws AxisFault {
mc.setEnvelope(envelope);
}
public EndpointReference getFaultTo() {
return mc.getFaultTo();
}
public void setFaultTo(EndpointReference reference) {
mc.setFaultTo(reference);
}
public EndpointReference getFrom() {
return mc.getFrom();
}
public void setFrom(EndpointReference reference) {
mc.setFrom(reference);
}
public String getMessageID() {
return mc.getMessageID();
}
public void setMessageID(String string) {
mc.setMessageID(string);
}
public RelatesTo getRelatesTo() {
return mc.getRelatesTo();
}
public void setRelatesTo(RelatesTo[] reference) {
mc.setRelatesTo(reference);
}
public EndpointReference getReplyTo() {
return mc.getReplyTo();
}
public void setReplyTo(EndpointReference reference) {
mc.setReplyTo(reference);
}
public EndpointReference getTo() {
return mc.getTo();
}
public void setTo(EndpointReference reference) {
mc.setTo(reference);
}
public void setWSAAction(String actionURI) {
mc.setWSAAction(actionURI);
}
public String getWSAAction() {
return mc.getWSAAction();
}
public String getSoapAction() {
return mc.getSoapAction();
}
public void setSoapAction(String string) {
mc.setSoapAction(string);
}
public void setWSAMessageID(String messageID) {
mc.setWSAMessageID(messageID);
}
public String getWSAMessageID() {
return mc.getWSAMessageID();
}
public boolean isDoingMTOM() {
return mc.isDoingMTOM();
}
public boolean isDoingSWA() {
return mc.isDoingSWA();
}
public void setDoingMTOM(boolean b) {
mc.setDoingMTOM(b);
}
public void setDoingSWA(boolean b) {
mc.setDoingSWA(b);
}
public boolean isDoingPOX() {
return mc.isDoingPOX();
}
public void setDoingPOX(boolean b) {
mc.setDoingPOX(b);
}
public boolean isDoingGET() {
return mc.isDoingGET();
}
public void setDoingGET(boolean b) {
mc.setDoingGET(b);
}
public boolean isSOAP11() {
return mc.isSOAP11();
}
public void setResponse(boolean b) {
mc.setResponse(b);
}
public boolean isResponse() {
return mc.isResponse();
}
public void setFaultResponse(boolean b) {
mc.setFaultResponse(b);
}
public boolean isFaultResponse() {
return mc.isFaultResponse();
}
public int getTracingState() {
return mc.getTracingState();
}
public void setTracingState(int tracingState) {
mc.setTracingState(tracingState);
}
public Stack<FaultHandler> getFaultStack() {
return mc.getFaultStack();
}
public void pushFaultHandler(FaultHandler fault) {
mc.pushFaultHandler(fault);
}
public void pushContinuationState(ContinuationState continuationState) {
}
public Stack<ContinuationState> getContinuationStateStack() {
return null;
}
public boolean isContinuationEnabled() {
return false;
}
public void setContinuationEnabled(boolean contStateStackEnabled) {
}
public Log getServiceLog() {
return LogFactory.getLog(ScriptMessageContext.class);
}
public Mediator getSequenceTemplate(String key) {
return mc.getSequenceTemplate(key);
}
private String serializeJSON(Object obj) {
StringWriter json = new StringWriter();
if(obj instanceof Wrapper) {
obj = ((Wrapper) obj).unwrap();
}
if (obj instanceof NativeObject) {
json.append("{");
NativeObject o = (NativeObject) obj;
Object[] ids = o.getIds();
boolean first = true;
for (Object id : ids) {
String key = (String) id;
Object value = o.get((String) id, o);
if (!first) {
json.append(", ");
} else {
first = false;
}
json.append("\"").append(key).append("\" : ").append(serializeJSON(value));
}
json.append("}");
} else if (obj instanceof NativeArray) {
json.append("[");
NativeArray o = (NativeArray) obj;
Object[] ids = o.getIds();
boolean first = true;
for (Object id : ids) {
Object value = o.get((Integer) id, o);
if (!first) {
json.append(", ");
} else {
first = false;
}
json.append(serializeJSON(value));
}
json.append("]");
} else if (obj instanceof Object[]) {
json.append("[");
boolean first = true;
for (Object value : (Object[]) obj) {
if (!first) {
json.append(", ");
} else {
first = false;
}
json.append(serializeJSON(value));
}
json.append("]");
} else if (obj instanceof String) {
json.append("\"").append(obj.toString()).append("\"");
} else if (obj instanceof Integer ||
obj instanceof Long ||
obj instanceof Float ||
obj instanceof Double ||
obj instanceof Short ||
obj instanceof BigInteger ||
obj instanceof BigDecimal ||
obj instanceof Boolean) {
json.append(obj.toString());
} else {
json.append("{}");
}
return json.toString();
}
public void setPayloadJSON(Object jsonPayload) throws ScriptException {
org.apache.axis2.context.MessageContext messageContext;
messageContext = ((Axis2MessageContext) mc).getAxis2MessageContext();
byte[] json = {'{', '}'};
if (jsonPayload instanceof String) {
json = jsonPayload.toString().getBytes();
} else if (jsonPayload != null) {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
serializeJSON_(jsonPayload, out);
json = out.toByteArray();
} catch (IOException e) {
logger.error("#setPayloadJSON. Could not retrieve bytes from JSON object. Error>>> "
+ e.getLocalizedMessage());
}
}
// save this JSON object as the new payload.
try {
JsonUtil.getNewJsonPayload(messageContext, json, 0, json.length, true, true);
} catch (AxisFault axisFault) {
throw new ScriptException(axisFault);
}
//JsonUtil.setContentType(messageContext);
Object jsonObject = scriptEngine.eval(JsonUtil.newJavaScriptSourceReader(messageContext));
setJsonObject(mc, jsonObject);
}
private void serializeJSON_(Object obj, OutputStream out) throws IOException {
if (out == null) {
logger.warn("#serializeJSON_. Did not serialize JSON object. Object: " + obj + " Stream: " + out);
return;
}
if(obj instanceof Wrapper) {
obj = ((Wrapper) obj).unwrap();
}
if(obj == null){
out.write("null".getBytes());
}
else if (obj instanceof NativeObject) {
out.write('{');
NativeObject o = (NativeObject) obj;
Object[] ids = o.getIds();
boolean first = true;
for (Object id : ids) {
String key = (String) id;
Object value = o.get((String) id, o);
if (!first) {
out.write(',');
out.write(' ');
} else {
first = false;
}
out.write('"');
out.write(key.getBytes());
out.write('"');
out.write(':');
serializeJSON_(value, out);
}
out.write('}');
} else if (obj instanceof NativeArray) {
out.write('[');
NativeArray o = (NativeArray) obj;
Object[] ids = o.getIds();
boolean first = true;
for (Object id : ids) {
Object value = o.get((Integer) id, o);
if (!first) {
out.write(',');
out.write(' ');
} else {
first = false;
}
serializeJSON_(value, out);
}
out.write(']');
} else if (obj instanceof Object[]) {
out.write('[');
boolean first = true;
for (Object value : (Object[]) obj) {
if (!first) {
out.write(',');
out.write(' ');
} else {
first = false;
}
serializeJSON_(value, out);
}
out.write(']');
} else if (obj instanceof String) {
out.write('"');
out.write(((String) obj).getBytes());
out.write('"');
} else if (obj instanceof ConsString) {
//This class represents a string composed of two components using the "+" operator
//in java script with rhino7 upward. ex:var str = "val1" + "val2";
out.write('"');
out.write((((ConsString) obj).toString()).getBytes());
out.write('"');
} else if (obj instanceof Integer ||
obj instanceof Long ||
obj instanceof Float ||
obj instanceof Double ||
obj instanceof Short ||
obj instanceof BigInteger ||
obj instanceof BigDecimal ||
obj instanceof Boolean) {
out.write(obj.toString().getBytes());
} else {
out.write('{');
out.write('}');
}
}
public Mediator getDefaultConfiguration(String arg0) {
// TODO Auto-generated method stub
return null;
}
public void addComponentToMessageFlow(String mediatorId){
}
public String getMessageString(){
return null;
}
public void setMessageFlowTracingState(int state){
}
public int getMessageFlowTracingState(){
return SynapseConstants.TRACING_OFF;
}
}