com.zimbra.common.soap.SoapTestHarness.java Source code

Java tutorial

Introduction

Here is the source code for com.zimbra.common.soap.SoapTestHarness.java

Source

/*
 * ***** BEGIN LICENSE BLOCK *****
 * Zimbra Collaboration Suite Server
 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013, 2014, 2016 Synacor, Inc.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software Foundation,
 * version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along with this program.
 * If not, see <https://www.gnu.org/licenses/>.
 * ***** END LICENSE BLOCK *****
 */

/*
 * Created on May 26, 2004
 */
package com.zimbra.common.soap;

import com.zimbra.common.auth.ZAuthToken;
import com.zimbra.common.service.ServiceException;
import com.zimbra.common.util.ByteUtil;
import com.zimbra.common.util.CliUtil;

import org.apache.commons.cli.*;
import org.dom4j.Namespace;
import org.dom4j.QName;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SoapTestHarness {

    public static final String NAMESPACE_STR = "urn:zimbraTestHarness";
    public static final Namespace NAMESPACE = Namespace.get("test", NAMESPACE_STR);
    public static final QName E_TESTS = QName.get("tests", NAMESPACE);
    public static final QName E_TEST = QName.get("test", NAMESPACE);
    public static final QName E_REQUEST = QName.get("request", NAMESPACE);
    public static final QName E_RESPONSE = QName.get("response", NAMESPACE);
    public static final QName E_PROPERTY = QName.get("property", NAMESPACE);
    public static final QName E_NAMESPACE = QName.get("namespace", NAMESPACE);
    public static final QName E_SELECT = QName.get("select", NAMESPACE);
    public static final QName E_ECHO = QName.get("echo", NAMESPACE);

    //
    public static final String A_ATTR = "attr";
    public static final String A_NAME = "name";
    public static final String A_PREFIX = "prefix";
    public static final String A_URI = "uri";
    public static final String A_PATH = "path";
    public static final String A_VALUE = "value";
    public static final String A_SET = "set";
    public static final String A_REQUIRED = "required";
    public static final String A_DUMP = "dump";
    public static final String A_MATCH = "match";

    private static Pattern mPropPattern = Pattern.compile("(\\$\\{([^}]+)\\})");

    private SoapProtocol mSoapProto;
    private SoapProtocol mResponseProto;
    private SoapHttpTransport mTransport;
    private String mTargetUser;
    private String mAuthToken;
    private String mSessionId;
    private long mCounter = 0;

    /** any props we have set */
    private HashMap<String, String> mProps;
    private boolean mDebug;

    /** <uri> */
    private String mUri;

    private int mTestNum;

    private Test mCurrent;
    private List<Test> mTests;

    public static class Test {
        /** the <test> element */
        public Element mTest;

        /** doc in the soap body */
        public Element mDocRequest;
        /** doc in the soap body */
        public Element mDocResponse;

        /** the soap request envelope */
        public Element mSoapRequest;

        /** the soap response envelope */
        public Element mSoapResponse;

        /** test number */
        public int mTestNum;

        /** total number of checks made on this test */
        public int mNumChecks;

        /** the number that failed */
        public int mNumCheckFails;

        /** request time in msecs */
        public long mTime;

        public void check(boolean ok, String message) {
            mNumChecks++;
            if (!ok)
                mNumCheckFails++;
        }

        public boolean isRequired() {
            return "true".equals(mTest.getAttribute(A_REQUIRED, null));
        }

        public boolean testFailed() {
            return mNumCheckFails > 0;
        }

        public String getDocReqName() {
            return mDocRequest.getName();
        }

        public String getDocRespName() {
            return mDocResponse.getName();
        }

        public String getStatus() {
            StringBuilder status = new StringBuilder();

            if (testFailed()) {
                status.append("FAIL ");
            } else {
                status.append("PASS ");
            }

            status.append(lpadz(mTestNum + "", 4));

            status.append(" ");

            int pass = mNumChecks - mNumCheckFails;
            status.append(lpad(pass + "/" + mNumChecks, 8));

            status.append(" ");

            status.append(lpad(mTime + "ms", 7));

            status.append("    ");

            status.append(rpad(getDocReqName(), 40));

            return status.toString();
        }

        /**
         * @@return
         */
        public boolean dumpTest() {
            return "true".equals(mTest.getAttribute(A_DUMP, null));
        }
    }

    public static void usage(Options options) {
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp(SoapTestHarness.class.getName(), options);
        System.exit(1);
    }

    public static void main(String args[]) throws HarnessException, IOException, ServiceException {
        SoapTestHarness harness = new SoapTestHarness();
        harness.runTests(args);
    }

    public SoapTestHarness() {
        mProps = new HashMap<String, String>();
        mSoapProto = SoapProtocol.Soap12;
        mResponseProto = SoapProtocol.Soap12;
        mTests = new ArrayList<Test>();
    }

    public void runTests(String args[]) throws HarnessException, IOException, ServiceException {

        CliUtil.toolSetup();
        SoapTransport.setDefaultUserAgent("SoapTestHarness", null);

        CommandLineParser parser = new GnuParser();
        Options options = new Options();

        options.addOption("h", "help", false, "print usage");
        options.addOption("d", "debug", false, "debug");
        options.addOption("s", "expandsystemprops", false, "exoand system properties");

        Option fileOpt = new Option("f", "file", true, "input document");
        fileOpt.setArgName("request-document");
        fileOpt.setRequired(true);
        options.addOption(fileOpt);

        CommandLine cl = null;
        boolean err = false;
        try {
            cl = parser.parse(options, args);
        } catch (ParseException pe) {
            System.err.println("error: " + pe.getMessage());
            err = true;
        }

        if (err || cl.hasOption("help"))
            usage(options);

        mDebug = cl.hasOption("d");
        String file = cl.getOptionValue("f");

        if (cl.hasOption("s"))
            expandSystemProperties();
        ;

        String docStr = new String(ByteUtil.getContent(new File(file)), "utf-8");
        doTests(Element.parseXML(docStr));

        //Element request = doc.getRootElement();
        //Element response = trans.invoke(request, isRaw);
        //System.out.println(DomUtil.toString(response, true));

        if (mTransport != null)
            mTransport.shutdown();
    }

    private void setProperty(String name, String value) throws HarnessException {
        mProps.put(name, value);
        if (name.equals("uri")) {
            if (value == null)
                throw new HarnessException("need value for uri");
            mUri = value;
            mTransport = new SoapHttpTransport(mUri);
        } else if (name.equals("authToken")) {
            if (value == null)
                throw new HarnessException("need value for authToken");
            mAuthToken = value;
        } else if (name.equals("sessionId")) {
            if (value != null)
                mSessionId = value;
        } else if (name.equals("target")) {
            mTargetUser = (value == null || value.equals("") ? null : value);
        } else if (name.equals("protocol")) {
            mResponseProto = "js".equalsIgnoreCase(value) ? SoapProtocol.SoapJS : SoapProtocol.Soap12;
        }
    }

    private void doEcho(Element e) {
        System.out.println(e.getTextTrim());
    }

    private void checkGlobals(Element e) throws HarnessException {
        if (e.getQName().equals(E_ECHO)) {
            doEcho(e);
        } else if (e.getQName().equals(E_PROPERTY)) {
            doProperty(e);
        } else if (e.getQName().equals(E_NAMESPACE)) {
            doNamespace(e);
        }
    }

    private void doProperty(Element property) throws HarnessException {
        String name = property.getAttribute(A_NAME, null);
        String value = property.getAttribute(A_VALUE, null);
        if (name == null)
            throw new HarnessException("<property> tag missing name");
        if (value == null)
            throw new HarnessException("<property> tag missing value");
        setProperty(name, value);
        //System.out.println(name + "=" + value);
    }

    private void doNamespace(Element property) throws HarnessException {
        String prefix = property.getAttribute(A_PREFIX, null);
        String uri = property.getAttribute(A_URI, null);
        if (prefix == null)
            throw new HarnessException("<namespace> tag missing prefix");
        if (uri == null)
            throw new HarnessException("<namespace> tag missing uri");
        getURIs(SoapProtocol.Soap12).put(prefix, uri);
        getURIs(SoapProtocol.SoapJS).put(prefix, uri);
    }

    protected void doTests(Element root) throws HarnessException, IOException, ServiceException {
        if (!root.getQName().equals(E_TESTS))
            throw new HarnessException("root document node must be " + E_TESTS.getQualifiedName());

        for (Element e : root.listElements()) {
            //            e = expandProps(e.createCopy());
            e = expandProps(e);
            if (e.getQName().equals(E_TEST)) {
                if (!doTest(e))
                    break;
            } else {
                checkGlobals(e);
            }
        }

        for (Test t : mTests)
            System.out.println(t.getStatus());
    }

    private boolean doTest(Element test) throws IOException, HarnessException, ServiceException {
        mCurrent = new Test();
        mTests.add(mCurrent);
        mCurrent.mTestNum = ++mTestNum;
        mCurrent.mTest = test;

        for (Element e : test.listElements()) {
            if (e.getQName().equals(E_REQUEST)) {
                doRequest(e);
            } else if (e.getQName().equals(E_RESPONSE)) {
                doResponse(e);
            } else {
                checkGlobals(e);
            }
        }

        //System.out.println(mCurrent.getStatus());
        //System.out.println("  Response: " + mDocResponse.getQualifiedName());
        if (mDebug || mCurrent.testFailed() || mCurrent.dumpTest()) {
            System.out.println("----");
            System.out.println(mCurrent.mSoapRequest.prettyPrint());
            System.out.println("----");
            System.out.println(mCurrent.mSoapResponse.prettyPrint());
            System.out.println("----");
        }

        if (!mCurrent.isRequired())
            return true;
        else
            return !mCurrent.testFailed();

        //return !mCurrent.isRequired() || !mCurrent.testFailed();
    }

    private String expandAllProps(String text) throws HarnessException {
        Matcher matcher = mPropPattern.matcher(text);
        StringBuffer sb = new StringBuffer();
        //System.out.println("("+text+")");
        while (matcher.find()) {
            String name = matcher.group(2);
            String replace = mProps.get(name);
            if (replace == null) {
                if (name.equals("TIME")) {
                    replace = System.currentTimeMillis() + "";
                } else if (name.equals("COUNTER")) {
                    replace = ++mCounter + "";
                }
            }

            if (replace != null) {
                matcher.appendReplacement(sb, replace);
            } else {
                throw new HarnessException("unknown property: " + matcher.group(1));
                //matcher.appendReplacement(sb, matcher.group(1));
            }
        }
        matcher.appendTail(sb);
        text = sb.toString();
        //System.out.println("("+text+")");
        return text;
    }

    private Element expandProps(Element doc) throws HarnessException {
        for (Element e : doc.listElements()) {
            expandProps(e);
        }
        for (Element.Attribute attr : doc.listAttributes()) {
            String text = attr.getValue();
            if (text.indexOf("${") != -1)
                attr.setValue(expandAllProps(text));
        }
        String text = doc.getText();
        if (text.indexOf("${") != -1)
            doc.setText(expandAllProps(text));
        return doc;
    }

    static String lpad(String s, int width) {
        return pad(s, width, false, true);
    }

    static String rpad(String s, int width) {
        return pad(s, width, false, false);
    }

    static String lpadz(String s, int width) {
        return pad(s, width, true, true);
    }

    static String rpadz(String s, int width) {
        return pad(s, width, true, false);
    }

    /**
     * @@param testNum
     * @@return
     */
    private static String pad(String s, int width, boolean withZero, boolean left) {
        int needed = width - s.length();

        if (needed <= 0)
            return s;

        StringBuilder sb = new StringBuilder(width);

        if (left) {
            while (needed-- > 0) {
                sb.append(withZero ? '0' : ' ');
            }
            sb.append(s);
        } else {
            sb.append(s);
            while (needed-- > 0) {
                sb.append(withZero ? '0' : ' ');
            }
        }
        return sb.toString();
    }

    private void doRequest(Element request) throws IOException, ServiceException {
        mCurrent.mDocRequest = request.elementIterator().next();
        mCurrent.mDocRequest.detach();

        ZAuthToken zat = mAuthToken == null ? null : new ZAuthToken(null, mAuthToken, null);
        Element ctxt = SoapUtil.toCtxt(mSoapProto, zat, mSessionId, -1);
        if (mTargetUser != null)
            SoapUtil.addTargetAccountToCtxt(ctxt, null, mTargetUser);
        if (mResponseProto == SoapProtocol.SoapJS)
            SoapUtil.addResponseProtocolToCtxt(ctxt, mResponseProto);
        mCurrent.mSoapRequest = mSoapProto.soapEnvelope(mCurrent.mDocRequest, ctxt);

        long start = System.currentTimeMillis();
        mCurrent.mSoapResponse = mTransport.invokeRaw(mCurrent.mSoapRequest);
        long finish = System.currentTimeMillis();

        mCurrent.mTime = finish - start;
        mCurrent.mDocResponse = mResponseProto.getBodyElement(mCurrent.mSoapResponse);
    }

    private static Map<String, String> mURIs = null;
    private static Map<String, String> mJSURIs = null;

    private static Map<String, String> getURIs(SoapProtocol proto) {
        if (mURIs == null) {
            mURIs = new HashMap<String, String>();
            mURIs.put("zimbra", "urn:zimbra");
            mURIs.put("acct", "urn:zimbraAccount");
            mURIs.put("mail", "urn:zimbraMail");
            mURIs.put("admin", "urn:zimbraAdmin");
            mURIs.put("soap", "http://www.w3.org/2003/05/soap-envelope");
            mURIs.put("soap12", "http://www.w3.org/2003/05/soap-envelope");
            mURIs.put("soap11", "http://schemas.xmlsoap.org/soap/envelope/");

            mJSURIs = new HashMap<String, String>();
            mJSURIs.put("zimbra", "urn:zimbra");
            mJSURIs.put("acct", "urn:zimbraAccount");
            mJSURIs.put("mail", "urn:zimbraMail");
            mJSURIs.put("admin", "urn:zimbraAdmin");
            mJSURIs.put("soap", "urn:zimbraSoap");
            mJSURIs.put("soapJS", "urn:zimbraSoap");
        }
        return proto == SoapProtocol.SoapJS ? mJSURIs : mURIs;
    }

    /*
    XPath xpath = response.createXPath(path);
    Map uris = new HashMap();
    uris.put("acct", "urn:zimbraAccount");
    xpath.setNamespaceURIs(uris);
    xpath.selectSingleNode(response);
    System.out.println(xpath.selectSingleNode(response));
     */

    private void doSelect(Element context, Element parent)
            throws SoapFaultException, IOException, HarnessException {

        String path = parent.getAttribute(A_PATH, null);
        String attr = parent.getAttribute(A_ATTR, null);
        String match = parent.getAttribute(A_MATCH, null);

        Element se;
        if (path != null) {
            // FIXME: hacky!
            org.dom4j.Element d4context = context.toXML();
            org.dom4j.XPath xpath = d4context.createXPath(path);
            xpath.setNamespaceURIs(getURIs(mResponseProto));
            org.dom4j.Node node = xpath.selectSingleNode(d4context);
            //System.out.println("path = " + path + " node = " + node);
            if (!(node instanceof org.dom4j.Element)) {
                mCurrent.check(false, "select failed: " + path);
                return;
            } else {
                se = Element.convertDOM((org.dom4j.Element) node);
                mCurrent.check(true, "select ok: " + path);
            }
        } else {
            se = context;
        }

        String value;
        if (attr != null) {
            value = se.getAttribute(attr, null);
        } else {
            value = se.getText();
        }

        if (match != null) {
            boolean ok = Pattern.matches(match, value);
            mCurrent.check(ok, "match " + (ok ? "ok" : "failed") + " (" + match + ")" + " (" + value + ")");
        }

        //System.out.println(se.getText());
        String property = parent.getAttribute(A_SET, null);
        if (property != null) {
            //System.out.println(property+" "+value);
            setProperty(property, value);
        }

        for (Element e : parent.listElements()) {
            if (e.getQName().equals(E_SELECT)) {
                doSelect(se, e);
            } else {
                checkGlobals(e);
            }
        }
    }

    private void doResponse(Element test) throws SoapFaultException, IOException, HarnessException {
        for (Element e : test.listElements()) {
            if (e.getQName().equals(E_SELECT)) {
                doSelect(mCurrent.mDocResponse, e);
            } else {
                checkGlobals(e);
            }
        }
    }

    @SuppressWarnings("serial")
    public static class HarnessException extends Exception {
        public HarnessException(String message) {
            super(message);
        }

        public HarnessException(String message, Throwable cause) {
            super(message, cause);
        }
    }

    private void expandSystemProperties() throws HarnessException {

        Properties props = System.getProperties();

        if (props != null) {
            Enumeration<?> keys = props.propertyNames();
            while (keys.hasMoreElements()) {
                String key = keys.nextElement().toString();
                String value = props.getProperty(key).toString();
                if (value != null)
                    setProperty(key, value);
                else
                    throw new HarnessException("Invalid Global Property file");
            }
        }
    }

}