# MediatorCustomVariable.java

    /*
     *  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.xquery;

    import net.sf.saxon.s9api.XdmNodeKind;
    import org.apache.axiom.om.OMElement;
    import org.apache.axiom.om.OMNode;
    import org.apache.axiom.om.OMText;
    import org.apache.axiom.soap.SOAP11Constants;
    import org.apache.axiom.soap.SOAP12Constants;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.apache.synapse.MessageContext;
    import org.apache.synapse.SynapseException;
    import org.apache.synapse.config.Entry;
    import org.apache.synapse.util.xpath.SynapseXPath;
    import org.apache.synapse.util.xpath.SourceXPathSupport;
    import org.jaxen.JaxenException;

    import javax.xml.namespace.QName;
    import java.util.List;

    /**
     * The value of the custom variable will be evaluated dynamically.
     * The value is computed by extracting the data from the XML document which will lookup  through the
     * key or  the current SOAP message.
     */

    public class MediatorCustomVariable extends MediatorVariable {

        private static final Log log = LogFactory.getLog(MediatorCustomVariable.class);

        /* The key to lookup the xml document from registry */
        private String regKey;
        /* The XPath expression*/
        private SynapseXPath expression;
        /*Lock used to ensure thread-safe lookup of the object from the registry */
        private final Object resourceLock = new Object();

        public MediatorCustomVariable(QName name) {
            super(name);
            // create the default XPath
            try {
                this.expression = new SynapseXPath(SourceXPathSupport.DEFAULT_XPATH);
                this.expression.addNamespace("s11", SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI);
                this.expression.addNamespace("s12", SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI);
            } catch (JaxenException e) {
                handleException("Error creating source XPath expression", e);
            }

        }

        /**
         * To assign a value to variable dynamically
         *
         * @param synCtx The current message context
         */
        public boolean evaluateValue(MessageContext synCtx) {

            if (this.regKey == null) {   // get the node from the current message payload
                this.value = evaluate(synCtx);
                return true;
            } else {
                //Load the XML document from the registry
                boolean reLoad = false;
                boolean hasValueChanged = false;
                Entry dp = synCtx.getConfiguration().getEntryDefinition(this.regKey);
                // if the key refers to a dynamic resource
                if (dp != null && dp.isDynamic()) {
                    if (!dp.isCached() || dp.isExpired()) {
                        reLoad = true;
                    }
                }
                synchronized (resourceLock) {
                    if (reLoad || this.value == null) {
                        hasValueChanged = true;
                        Object o = synCtx.getEntry(this.regKey);
                        if (o != null) {
                            if (!SourceXPathSupport.DEFAULT_XPATH.equals(expression.toString())) {
                                this.value = evaluate(o);
                            } else {
                                this.value = o;
                            }
                        }
                    }
                }
                return hasValueChanged;
            }
        }

        /**
         * Return the object to be used for the variable value
         *
         * @param source The source on which will be evaluated the XPath expression
         * @return Return the OMNode to be used for the variable value
         */
        private Object evaluate(Object source) {
            try {
                Object result = expression.evaluate(source);
                if (result instanceof List && !((List) result).isEmpty()) {
                    result = ((List) result).get(0);  // Always fetches *only* the first
                }
                if (result instanceof OMNode) {
                    //if the type is not document-node(), then get the text value of the node
                    if (this.getNodeKind() != XdmNodeKind.DOCUMENT
                            && this.getNodeKind() != XdmNodeKind.ELEMENT) {

                        int nodeType = ((OMNode) result).getType();
                        if (nodeType == OMNode.TEXT_NODE) {
                            return ((OMText) result).getText();
                        } else if (nodeType == OMNode.ELEMENT_NODE) {
                            return ((OMElement) result).getText();
                        }
                    }
                    return result;
                } else {
                    return result;
                }
            } catch (JaxenException e) {
                handleException("Error evaluating XPath " + expression + " on message" + source);
            }
            return null;
        }

        private void handleException(String msg, Exception e) {
            log.error(msg, e);
            throw new SynapseException(msg, e);
        }

        private void handleException(String msg) {
            log.error(msg);
            throw new SynapseException(msg);
        }

        public void setExpression(SynapseXPath expression) {
            this.expression = expression;
        }

        public void setRegKey(String regKey) {
            this.regKey = regKey;
        }

        public String getRegKey() {
            return regKey;
        }

        public SynapseXPath getExpression() {
            return expression;
        }
    }