view flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/FunctionResolver.java @ 5779:ebec12def170

Datacage: Add a pool of builders to make it multi threadable. XML DOM is not thread safe. Therefore the old implementation only allowed one thread to use the builder at a time. As the complexity of the configuration has increased over time this has become a bottleneck of the whole application because it took quiet some time to build a result. Furthermore the builder code path is visited very frequent. So many concurrent requests were piled up resulting in long waits for the users. To mitigate this problem a round robin pool of builders is used now. Each of the pooled builders has an independent copy of the XML template and can be run in parallel. The number of builders is determined by the system property 'flys.datacage.pool.size'. It defaults to 4.
author Sascha L. Teichmann <teichmann@intevation.de>
date Sun, 21 Apr 2013 12:48:09 +0200
parents 619da3fa7a8b
children efbbfe32e9fe
line wrap: on
line source
package de.intevation.flys.artifacts.datacage.templating;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;

import javax.xml.xpath.XPathFunction;
import javax.xml.xpath.XPathFunctionException;
import javax.xml.xpath.XPathFunctionResolver;

import org.apache.log4j.Logger;


/** Resolves functions (e.g. dc:contains) in Datacage/Meta-Data system. */
public class FunctionResolver
implements   XPathFunctionResolver
{
    /** Home logger. */
    private static Logger log = Logger.getLogger(FunctionResolver.class);

    public static final String FUNCTION_NAMESPACE_URI = "dc";

    public static final double FAR_AWAY = 99999d;

    public static final class Entry {

        String        name;
        XPathFunction function;
        int           arity;

        public Entry() {
        }

        public Entry(String name, XPathFunction function, int arity) {
            this.name     = name;
            this.function = function;
            this.arity    = arity;
        }
    } // class Entry

    /** List of functions. */
    protected List<Entry> functions;

    protected Builder.BuildHelper buildHelper;


    public FunctionResolver() {
        this(null);
    }

    public FunctionResolver(Builder.BuildHelper buildHelper) {
        this.buildHelper = buildHelper;

        functions = new ArrayList<Entry>();

        addFunction("contains", 2, new XPathFunction() {
            @Override
            public Object evaluate(List args) throws XPathFunctionException {
                return contains(args);
            }
        });

        addFunction("fromValue", 3, new XPathFunction() {
            @Override
            public Object evaluate(List args) throws XPathFunctionException {
                return fromValue(args);
            }
        });

        addFunction("toValue", 3, new XPathFunction() {
            @Override
            public Object evaluate(List args) throws XPathFunctionException {
                return toValue(args);
            }
        });

        addFunction("replace", 3, new XPathFunction() {
            @Override
            public Object evaluate(List args) throws XPathFunctionException {
                return replace(args);
            }
        });

        addFunction("has-result", 0, new XPathFunction() {
            @Override
            public Object evaluate(List args) throws XPathFunctionException {
                return FunctionResolver.this.buildHelper.hasResult();
            }
        });

        addFunction("date-format", 2, new XPathFunction() {
            @Override
            public Object evaluate(List args) throws XPathFunctionException {
                return dateFormat(args);
            }
        });
    }

    /**
     * Create a new function.
     * @param name  Name of the function.
     * @param arity Number of arguments for function.
     * @param function the function itself.
     */
    public void addFunction(String name, int arity, XPathFunction function) {
        functions.add(new Entry(name, function, arity));
    }

    @Override
    public XPathFunction resolveFunction(QName functionName, int arity) {

        if (!functionName.getNamespaceURI().equals(FUNCTION_NAMESPACE_URI)) {
            return null;
        }

        String name = functionName.getLocalPart();
        for (Entry entry: functions) {
            if (entry.arity == arity && entry.name.equals(name)) {
                return entry.function;
            }
        }

        return null;
    }

    /** Implementation of case-ignoring dc:contains. */
    public Object contains(List args) throws XPathFunctionException {
        Object haystack = args.get(0);
        Object needle   = args.get(1);

        if (needle instanceof String && !(haystack instanceof String)) {
            needle = ((String)needle).toUpperCase();
        }

        try {
            if (haystack instanceof Collection) {
                return Boolean.valueOf(
                    ((Collection)haystack).contains(needle));
            }

            if (haystack instanceof Map) {
                return Boolean.valueOf(
                    ((Map)haystack).containsKey(needle));
            }

            if (haystack instanceof Object []) {
                for (Object straw: (Object [])haystack) {
                    if (straw.equals(needle)) {
                        return Boolean.TRUE;
                    }
                }
            }

            if (haystack instanceof String && needle instanceof String) {
                String h = (String)haystack;
                String n = (String)needle;
                return h.contains(n);
            }

            return Boolean.FALSE;
        }
        catch (Exception e) {
            log.error(e);
            throw new XPathFunctionException(e);
        }
    }

    /** Implementation for getting the minimum value of location or distance
     *  dc:fromValue.
     */
    public Object fromValue(List args) throws XPathFunctionException {
        Object mode      = args.get(0);
        Object locations = args.get(1);
        Object from      = args.get(2);

        if (!(mode instanceof String)){
            return -FAR_AWAY;
        }

        if (mode.equals("locations")) {
            if (!(locations instanceof String)) {
                return -FAR_AWAY;
            }
            String loc = ((String)locations).replace(" ", "");
            String[] split = loc.split(",");
            if (split.length < 1) {
                return -FAR_AWAY;
            }
            try {
                double min = Double.parseDouble(split[0]);
                for (int i = 1; i < split.length; ++i) {
                    double v = Double.parseDouble(split[i]);
                    if (v < min) {
                        min = v;
                    }
                }
                return min;
            }
            catch (NumberFormatException nfe) {
                return -FAR_AWAY;
            }
        }
        else if (mode.equals("distance")) {
            if (!(from instanceof String)) {
                return -FAR_AWAY;
            }
            String f = (String)from;
            try {
                return Double.parseDouble(f);
            }
            catch(NumberFormatException nfe) {
                return -FAR_AWAY;
            }
        }
        else {
            return -FAR_AWAY;
        }
    }

    /** Implementation for getting the maximum value of location or distance
     *  dc:toValue.
     */
    public Object toValue(List args) throws XPathFunctionException {
        Object mode      = args.get(0);
        Object locations = args.get(1);
        Object to        = args.get(2);

        if (!(mode instanceof String)){
            return FAR_AWAY;
        }

        if (mode.equals("locations")) {
            if (!(locations instanceof String)) {
                return FAR_AWAY;
            }
            try {
                String loc = ((String)locations).replace(" ", "");
                String[] split = loc.split(",");
                if (split.length < 1) {
                    return FAR_AWAY;
                }
                double max = Double.parseDouble(split[0]);
                for (int i = 1; i < split.length; ++i) {
                    double v = Double.parseDouble(split[i]);
                    if (v > max) {
                        max = v;
                    }
                }
                return max;
            }
            catch (NumberFormatException nfe) {
                return FAR_AWAY;
            }
        }
        else if (mode.equals("distance")) {
            if (!(to instanceof String)) {
                return FAR_AWAY;
            }
            else {
                String t = (String)to;
                try {
                    return Double.parseDouble(t);
                }
                catch(NumberFormatException nfe) {
                    return FAR_AWAY;
                }
            }
        }
        else {
            return FAR_AWAY;
        }
    }

    /** Implementation for doing a string replace
     *  dc:replace
     */
    public Object replace(List args) throws XPathFunctionException {
        Object haystack    = args.get(0);
        Object needle      = args.get(1);
        Object replacement = args.get(2);

        if (needle instanceof String &&
            haystack instanceof String &&
            replacement instanceof String) {
            return ((String)haystack).replace(
                    (String)needle, (String)replacement);
        } else {
            return haystack;
        }
    }

    public Object dateFormat(List args) throws XPathFunctionException {
        Object pattern = args.get(0);
        Object date    = args.get(1);
        if (pattern instanceof String && date instanceof Date) {
            try {
                // TODO: Take locale into account.
                return new SimpleDateFormat((String)pattern).format((Date)date);
            }
            catch (IllegalArgumentException iae) {
                throw new XPathFunctionException(iae);
            }
        }
        return "";
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org