org.testdwr.plain.Test.java Source code

Java tutorial

Introduction

Here is the source code for org.testdwr.plain.Test.java

Source

/*
 * Copyright 2005 Joe Walker
 *
 * Licensed 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.testdwr.plain;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import junit.framework.Assert;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.directwebremoting.Browser;
import org.directwebremoting.Container;
import org.directwebremoting.ScriptBuffer;
import org.directwebremoting.ScriptSession;
import org.directwebremoting.ScriptSessionFilter;
import org.directwebremoting.ScriptSessions;
import org.directwebremoting.ServerContext;
import org.directwebremoting.ServerContextFactory;
import org.directwebremoting.WebContext;
import org.directwebremoting.WebContextFactory;
import org.directwebremoting.dwrunit.Verify;
import org.directwebremoting.event.ScriptSessionBindingEvent;
import org.directwebremoting.event.ScriptSessionBindingListener;
import org.directwebremoting.extend.InboundContext;
import org.directwebremoting.impl.DefaultContainer;
import org.directwebremoting.impl.ImplWidenScope;
import org.directwebremoting.io.FileTransfer;
import org.directwebremoting.io.JavascriptFunction;
import org.directwebremoting.ui.browser.Document;
import org.directwebremoting.ui.browser.Window;
import org.directwebremoting.util.ClasspathScanner;
import org.directwebremoting.util.VersionUtil;
import org.junit.runner.Description;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
import org.testdwr.convert.AbstractBase;
import org.testdwr.convert.ConcreteBBase;
import org.testdwr.convert.ConcreteCBase;
import org.testdwr.convert.ConcreteIFace;
import org.testdwr.convert.IFace;
import org.testdwr.convert.MyFancyException;
import org.testdwr.convert.MyFancyExceptionInPackage;
import org.testdwr.convert.MyUnmappedException;
import org.testdwr.convert.ObjectWithLightClassMapping;
import org.testdwr.convert.OnePackageObject;
import org.testdwr.convert.StrangeNameObject;
import org.testdwr.convert.StrangeNameWithPackageObject;
import org.testdwr.convert.TwoPackagesObject;
import org.testdwr.convert.wildcards.flat.WildcardObjectContainer;
import org.testdwr.convert.wildcards.recursive.WildcardRecursiveObjectContainer;
import org.testdwr.event.Test2ScriptSessionListener;
import org.testdwr.event.TestScriptSessionListener;
import org.xml.sax.SAXParseException;

/**
 * Methods to help unit test DWR.
 * @author Joe Walker [joe at getahead dot ltd dot uk]
 */
@SuppressWarnings({ "UnnecessaryFullyQualifiedName" })
public class Test {
    public Verify checkContext(String contextPath) {
        ServerContext serverContext = ServerContextFactory.get();
        Container container = serverContext.getContainer();
        Verify verify = new Verify();

        verify.equals("ContextPath", contextPath, serverContext.getContextPath());
        verify.equals("Version", VersionUtil.getLabel(), serverContext.getVersion());
        verify.equals("Container.class", DefaultContainer.class.getName(), container.getClass().getName());
        verify.equals("Container.getBean", "DwrServlet", container.getBean("ContainerType"));

        return verify;
    }

    public void debug() {
        ImplWidenScope.defaultScriptSessionManagerDebug();
    }

    public String getPath() {
        return WebContextFactory.get().getContextPath();
    }

    public void throwNPE() {
        // This is exported by dwr.xml
        throw new NullPointerException("No message for NPE");
    }

    public void throwNPE(String message) {
        // This is exported by dwr.xml
        throw new NullPointerException(message);
    }

    public void throwIAE() {
        // This is NOT exported by dwr.xml
        throw new IllegalArgumentException("No message for IAE");
    }

    public void throwIAE(String message) {
        // This is NOT exported by dwr.xml
        throw new IllegalArgumentException(message);
    }

    public void throwSPE() throws SAXParseException {
        // This is exported by dwr.xml as a result of it being a SAXException
        throw new SAXParseException("No message for SPE", "publicId", "systemId", 42, 24,
                new NullPointerException("No message for NPE"));
    }

    public void throwSPE(String messageSpe, String messageNpe) throws SAXParseException {
        // This is exported by dwr.xml as a result of it being a SAXException
        throw new SAXParseException(messageSpe, "publicId", "systemId", 42, 24,
                new NullPointerException(messageNpe));
    }

    public int waitFor(int wait) {
        try {
            Thread.sleep(wait);
            return wait;
        } catch (InterruptedException ex) {
            return 0;
        }
    }

    public void slowAsync(final int wait, final String function) {
        WebContext context = WebContextFactory.get();
        final String sessionId = context.getScriptSession().getId();

        new Thread() {
            @Override
            public void run() {
                Browser.withSession(sessionId, new Runnable() {
                    public void run() {
                        try {
                            Thread.sleep(wait);
                            ScriptSessions.addFunctionCall(function);
                        } catch (InterruptedException ex) {
                        }
                    }
                });
            }
        }.start();
    }

    public void doNothing() {
    }

    public boolean areIdentical(List<String> a, List<String> b) {
        return a == b;
    }

    public void voidParam() {
    }

    //
    // PRIMITIVES
    //

    public boolean booleanParam(boolean test) {
        return test;
    }

    public byte byteParam(byte test) {
        return test;
    }

    public char charParam(char test) {
        return test;
    }

    public short shortParam(short test) {
        return test;
    }

    public int intParam(int test) {
        return test;
    }

    public long longParam(long test) {
        return test;
    }

    public float floatParam(float test) {
        return test;
    }

    public double doubleParam(double test) {
        return test;
    }

    public BigInteger bigIntegerParam(BigInteger test) {
        return test;
    }

    public BigDecimal bigDecimalParam(BigDecimal test) {
        return test;
    }

    public String stringParam(String test) {
        return test;
    }

    //
    // ARRAYS OF PRIMITIVES
    //

    public boolean[] booleanArrayParam(boolean[] test) {
        return test;
    }

    public char[] charArrayParam(char[] test) {
        return test;
    }

    public byte[] byteArrayParam(byte[] test) {
        return test;
    }

    public short[] shortArrayParam(short[] test) {
        return test;
    }

    public int[] intArrayParam(int[] test) {
        return test;
    }

    public long[] longArrayParam(long[] test) {
        return test;
    }

    public float[] floatArrayParam(float[] test) {
        return test;
    }

    public double[] doubleArrayParam(double[] test) {
        return test;
    }

    public double[][] double2DArrayParam(double[][] test) {
        return test;
    }

    public double[][][] double3DArrayParam(double[][][] test) {
        return test;
    }

    public double[][][][] double4DArrayParam(double[][][][] test) {
        return test;
    }

    public double[][][][][] double5DArrayParam(double[][][][][] test) {
        return test;
    }

    public String[] stringArrayParam(String[] test) {
        return test;
    }

    public String[][] string2DArrayParam(String[][] test) {
        return test;
    }

    public String[][][] string3DArrayParam(String[][][] test) {
        return test;
    }

    public String[][][][] string4DArrayParam(String[][][][] test) {
        return test;
    }

    public String[][][][][] string5DArrayParam(String[][][][][] test) {
        return test;
    }

    //
    // BEANS
    //

    public TestBean beanParam(TestBean test) {
        return test;
    }

    public ObjA getLooped() {
        ObjA objA = new ObjA();
        ObjB objB = new ObjB();
        objA.setObjB(objB);
        objB.setObjA(objA);
        return objA;
    }

    public ObjA beanLoopParam(ObjA objA) {
        ObjA nestedA = objA.getObjB().getObjA();

        if (nestedA != objA) {
            throw new IllegalStateException("Non matching obja != obja.objb.obja");
        }

        if (nestedA.getObjB() != objA.getObjB()) {
            throw new IllegalStateException("Non matching objb != objb.obja.objb");
        }

        return objA;
    }

    static public class BrokenConstructorBean extends TestBean {
        public BrokenConstructorBean() {
            throw new RuntimeException("Exception thrown in bean constructor.");
        }
    }

    public BrokenConstructorBean brokenConstructorBeanParam(BrokenConstructorBean test) {
        return test;
    }

    static public class BeanWithBrokenConstructorBean extends TestBean {
        public void setSubBean(BrokenConstructorBean bean) {
            super.setTestBean(bean);
        }
    }

    static public class BrokenSetterBean extends TestBean {
        @Override
        public void setInteger(int integer) {
            throw new RuntimeException("Exception thrown in bean setter.");
        }
    }

    public BrokenSetterBean brokenSetterBeanParam(BrokenSetterBean test) {
        return test;
    }

    static public class BrokenGetterBean extends TestBean {
        @Override
        public int getInteger() {
            throw new RuntimeException("Exception thrown in bean getter.");
        }
    }

    public BrokenGetterBean brokenGetterBeanReturn() {
        return new BrokenGetterBean();
    }

    public BeanWithBrokenConstructorBean beanWithBrokenConstructorBeanParam(BeanWithBrokenConstructorBean test) {
        return test;
    }

    static public class BeanWithBrokenSetterBean extends TestBean {
        public void setSubBean(BrokenSetterBean bean) {
            super.setTestBean(bean);
        }
    }

    public BeanWithBrokenSetterBean beanWithBrokenSetterBeanParam(BeanWithBrokenSetterBean test) {
        return test;
    }

    static public class BeanWithBrokenGetterBean extends TestBean {
        public BrokenGetterBean getSubBean() {
            return new BrokenGetterBean();
        }
    }

    public BeanWithBrokenGetterBean beanWithBrokenGetterBeanReturn() {
        return new BeanWithBrokenGetterBean();
    }

    //
    // COLLECTIONS OF BEANS
    //

    public Set<TestBean> beanSetParam(Set<TestBean> test) {
        if (test.size() > 1) {
            for (Iterator<TestBean> it = test.iterator(); it.hasNext();) {
                TestBean ele = it.next();
                TestBean ignore = ele;
                ele = ignore;
            }
        }

        return test;
    }

    public String emptyListParams(List<String> list1, List<String> list2) {
        if (list1 == list2) {
            try {
                list1.add("new item");
                return "error: same lists and modifiable";
            } catch (RuntimeException ex) {
                return "same lists but unmodifiable";
            }
        } else {
            return "different lists";
        }
    }

    public List<TestBean> beanListParam(List<TestBean> test) {
        if (test.size() > 1) {
            for (Iterator<TestBean> it = test.iterator(); it.hasNext();) {
                TestBean ele = it.next();
                TestBean ignore = ele;
                ele = ignore;
            }
        }

        return test;
    }

    public List<BrokenConstructorBean> brokenConstructorBeanListParam(List<BrokenConstructorBean> test) {
        return test;
    }

    public List<BrokenSetterBean> brokenSetterBeanListParam(List<BrokenSetterBean> test) {
        return test;
    }

    public List<BrokenGetterBean> brokenGetterBeanListReturn() {
        ArrayList<BrokenGetterBean> list = new ArrayList<>();
        list.add(new BrokenGetterBean());
        return list;
    }

    static public class BrokenConstructorList<T> extends ArrayList<T> {
        public BrokenConstructorList() {
            throw new RuntimeException("Exception thrown in list constructor.");
        }
    }

    public BrokenConstructorList<TestBean> brokenConstructorListParam(BrokenConstructorList<TestBean> test) {
        return test;
    }

    static public class BrokenAdderList<T> extends ArrayList<T> {
        @Override
        public boolean add(T e) {
            throw new RuntimeException("Exception thrown in list add().");
        }
    }

    public BrokenAdderList<TestBean> brokenAdderListParam(BrokenAdderList<TestBean> test) {
        return test;
    }

    static public class BrokenHasNextList<T> extends ArrayList<T> {
        @Override
        public Iterator<T> iterator() {
            return new Iterator<T>() {
                @Override
                public boolean hasNext() {
                    throw new RuntimeException("Exception thrown in list hasNext().");
                }

                @Override
                public T next() {
                    return null;
                }
            };
        }
    }

    public BrokenHasNextList<TestBean> brokenHasNextListReturn() {
        return new BrokenHasNextList<>();
    }

    static public class BrokenNextList<T> extends ArrayList<T> {
        @Override
        public Iterator<T> iterator() {
            return new Iterator<T>() {
                @Override
                public boolean hasNext() {
                    return true;
                }

                @Override
                public T next() {
                    throw new RuntimeException("Exception thrown in list next().");
                }
            };
        }
    }

    public BrokenNextList<TestBean> brokenNextListReturn() {
        return new BrokenNextList<>();
    }

    public TestBeanWithList beanWithListParam(TestBeanWithList test) {
        if (test.getList().size() > 1) {
            for (Iterator<TestBean> it = test.getList().iterator(); it.hasNext();) {
                TestBean ele = it.next();
                TestBean ignore = ele;
                ele = ignore;
            }
        }

        return test;
    }

    static public class BeanWithBrokenConstructorList extends TestBean {
        public void setList(BrokenConstructorList<TestBean> list) {
        }
    }

    public BeanWithBrokenConstructorList beanWithBrokenConstructorListParam(BeanWithBrokenConstructorList test) {
        return test;
    }

    static public class BeanWithBrokenAdderList extends TestBean {
        public void setList(BrokenAdderList<TestBean> list) {
        }
    }

    public BeanWithBrokenAdderList beanWithBrokenAdderListParam(BeanWithBrokenAdderList test) {
        return test;
    }

    static public class BeanWithBrokenHasNextList extends TestBean {
        public BrokenHasNextList<TestBean> getList() {
            return new BrokenHasNextList<>();
        }
    }

    public BeanWithBrokenHasNextList beanWithBrokenHasNextListReturn() {
        return new BeanWithBrokenHasNextList();
    }

    static public class BeanWithBrokenNextList extends TestBean {
        public BrokenNextList<TestBean> getList() {
            return new BrokenNextList<>();
        }
    }

    public BeanWithBrokenNextList beanWithBrokenNextListReturn() {
        return new BeanWithBrokenNextList();
    }

    public List<TestObject> objectListParam(List<TestObject> test) {
        if (test.size() > 1) {
            for (Iterator<TestObject> it = test.iterator(); it.hasNext();) {
                TestObject ele = it.next();
                TestObject ignore = ele;
                ele = ignore;
            }
        }

        return test;
    }

    public TestObjectWithList objectWithListParam(TestObjectWithList test) {
        if (test.list.size() > 1) {
            for (Iterator<TestObject> it = test.list.iterator(); it.hasNext();) {
                TestObject ele = it.next();
                TestObject ignore = ele;
                ele = ignore;
            }
        }

        return test;
    }

    @SuppressWarnings("unchecked")
    public List<?> untypedBeanListParam(List<?> test) {
        if (test.size() > 1) {
            for (Iterator<TestBean> it = (Iterator<TestBean>) test.iterator(); it.hasNext();) {
                TestBean ele = it.next();
                TestBean ignore = ele;
                ele = ignore;
            }
        }

        return test;
    }

    //
    // COLLECTIONS
    //

    public Collection<String> stringCollectionParam(Collection<String> test) {
        return test;
    }

    public LinkedList<String> stringLinkedListParam(LinkedList<String> test) {
        return test;
    }

    public ArrayList<String> stringArrayListParam(ArrayList<String> test) {
        return test;
    }

    public List<String> stringListParam(List<String> test) {
        return test;
    }

    public Set<String> stringSetParam(Set<String> test) {
        return test;
    }

    public org.dom4j.Element dom4jElementParam(org.dom4j.Element test) {
        return test;
    }

    public org.dom4j.Document dom4jDocumentParam(org.dom4j.Document test) {
        return test;
    }

    public nu.xom.Element xomElementParam(nu.xom.Element test) {
        return test;
    }

    public nu.xom.Document xomDocumentParam(nu.xom.Document test) {
        return test;
    }

    public org.jdom.Element jdomElementParam(org.jdom.Element test) {
        return test;
    }

    public org.jdom.Document jdomDocumentParam(org.jdom.Document test) {
        return test;
    }

    public org.w3c.dom.Element domElementParam(org.w3c.dom.Element test) {
        return test;
    }

    public org.w3c.dom.Document domDocumentParam(org.w3c.dom.Document test) {
        return test;
    }

    public List<TestBean> testBeanListReturn() {
        List<TestBean> testBeanList = new ArrayList<TestBean>();
        TestBean childTestBean = new TestBean(1, true, "ChildTestBean", null);
        TestBean testBean = new TestBean(1, true, "TestBean", childTestBean);
        testBeanList.add(testBean);
        testBeanList.add(testBean);
        return testBeanList;
    }

    public List<TestBean> testBeanListReturnForJSONP() {
        List<TestBean> testBeanList = testBeanListReturn();
        // Add the bean to the list again, to test that DWR does not create references for JSON.
        testBeanList.add(testBeanList.get(0));
        return testBeanList;
    }

    public HashSet<String> stringHashSetParam(HashSet<String> test) {
        return test;
    }

    public TreeSet<String> stringTreeSetParam(TreeSet<String> test) {
        return test;
    }

    public Map<String, Boolean> stringBooleanMapParam(Map<String, Boolean> map) {
        return map;
    }

    public Map<String, String> stringStringMapParam(Map<String, String> test) {
        return test;
    }

    public Map<String, String[]> stringArrayMapParam(Map<String, String[]> test) {
        return test;
    }

    public Map<String, TestBean[]> stringBeanArrayMapParam(Map<String, TestBean[]> test) {
        return test;
    }

    public Map<Character, TestBean> characterBeanMapParam(Map<Character, TestBean> test) {
        return test;
    }

    public Map<Object, Boolean> nullKeyMap() {
        Map<Object, Boolean> map = new HashMap<Object, Boolean>();
        map.put(null, true);
        return map;
    }

    public Map<String, String> stringStringHashMapParam(HashMap<String, String> test) {
        return test;
    }

    public Map<String, String> stringStringTreeMapParam(TreeMap<String, String> test) {
        return test;
    }

    public TestBean[] testBeanArrayParam(TestBean[] test) {
        return test;
    }

    public FinalBean finalBeanParam(FinalBean test) {
        return test;
    }

    public FinalBean[] finalBeanArrayParam(FinalBean[] test) {
        return test;
    }

    public List<List<Map<String, TestBean>>> testComplex(List<List<Map<String, TestBean>>> test) {
        return test;
    }

    public FileTransfer binary(String text) {
        String html = "<html><body><p>" + text + "</p></body></html>";
        return new FileTransfer(null, "text/html", html.getBytes());
    }

    public TestBean inheritanceTest(int type) {
        switch (type) {
        case 0:
            return new TestBean();

        case 1:
            return new StaticInnerSubTestBean();

        case 2:
            return new InnerSubTestBean();

        case 3:
            return new TestBean() {
            };

        case 4:
            return (TestBean) Proxy.newProxyInstance(TestBean.class.getClassLoader(),
                    new Class[] { TestBean.class }, new TestBeanInvocationHandler());

        default:
            throw new IllegalArgumentException("" + type);
        }
    }

    public String[] stringVarArgs(String... value) {
        return value;
    }

    public TestBean[] testBeanVarArgs(TestBean... value) {
        return value;
    }

    public TestBean[] testStringBeanVarArgs(String value, TestBean... testBean) {
        return testBean;
    }

    public class InnerSubTestBean extends TestBean {
    }

    public static class StaticInnerSubTestBean extends TestBean {
    }

    static class TestBeanInvocationHandler implements InvocationHandler {
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("getInteger".equals(method.getName())) {
                return 42;
            }

            if ("getString".equals(method.getName())) {
                return "Slartibartfast";
            }

            if ("equals".equals(method.getName())) {
                return equals(args[0]);
            }

            if ("hashCode".equals(method.getName())) {
                return hashCode();
            }

            log.error("Failed on method: " + method);
            return null;
        }
    }

    public Map<String, Comparable<?>> dateTest(Date client) {
        Date server = new Date();

        Map<String, Comparable<?>> reply = new HashMap<String, Comparable<?>>();

        reply.put("client-object", client);
        reply.put("client-string", client.toString());
        reply.put("server-object", server);
        reply.put("server-string", server.toString());

        return reply;
    }

    public Foo inheritanceFooTest(int type) {
        switch (type) {
        case 0:
            return new InnerFoo();

        case 1:
            return new Foo() {
                public String getString() {
                    return "anon foo";
                }
            };

        case 4:
            return (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class },
                    new TestBeanInvocationHandler());

        default:
            throw new IllegalArgumentException("" + type);
        }
    }

    public interface Foo {
        String getString();
    }

    public class InnerFoo implements Foo {
        public String getString() {
            return "inner foo";
        }
    }

    public String httpServletRequestParam(HttpServletRequest req) {
        return req.getRemoteAddr();
    }

    @SuppressWarnings("unchecked")
    public Map<String, String> listParameters(HttpServletRequest request) {
        Map<String, String> reply = new HashMap<String, String>();

        Enumeration<String> names = request.getAttributeNames();
        while (names.hasMoreElements()) {
            String name = names.nextElement();
            String value = request.getAttribute(name).toString();
            reply.put(name, value);
        }

        return reply;
    }

    @SuppressWarnings("unchecked")
    public Map<String, String> listHeaders(HttpServletRequest request) {
        Map<String, String> reply = new HashMap<String, String>();

        Enumeration<String> names = request.getHeaderNames();
        while (names.hasMoreElements()) {
            String name = names.nextElement();
            Enumeration<String> values = request.getHeaders(name);
            StringBuilder value = new StringBuilder();
            while (values.hasMoreElements()) {
                String single = values.nextElement();
                value.append(single);
                if (values.hasMoreElements()) {
                    value.append(", ");
                }
            }

            reply.put(name, value.toString());
        }
        return reply;
    }

    public HttpObjectParamsBean httpObjectParams(HttpServletRequest req, int i, HttpServletResponse resp, String s,
            HttpSession session, short[] ss, ServletContext scx, Date d, ServletConfig scfg) {
        HttpObjectParamsBean httpObjectParamsBean = new HttpObjectParamsBean();
        httpObjectParamsBean.setIntValue(i);
        httpObjectParamsBean.setStringValue(s);
        httpObjectParamsBean.setShortArrayValue(ss);
        httpObjectParamsBean.setDateValue(d);
        httpObjectParamsBean.setRequestHeaderValue(listParameters(req));
        return httpObjectParamsBean;
    }

    public TestBean httpSessionAndMoreParam(HttpSession session, TestBean testBean, int i) {
        return testBean;
    }

    public TestBean httpSessionAndMoreParam2(TestBean testBean, HttpSession session, int i) {
        return testBean;
    }

    public TestBean httpSessionAndMoreParam3(TestBean testBean, int i, HttpSession session) {
        return testBean;
    }

    public TestBean[] getNestingTest() {
        TestBean a = new TestBean(0, true, "!\"$%^&*()_1", null);
        TestBean b = new TestBean(0, true, "!\"$%^&*()_2", a);
        TestBean c = new TestBean(0, true, "!\"$%^&*()_3", b);
        TestBean d = new TestBean(0, true, "!\"$%^&*()_4", c);

        TestBean[] reply = new TestBean[] { a, c, d, d, };

        return reply;
    }

    public String slowStringParam(String param, long delay) throws InterruptedException {
        log.debug("About to wait for: " + delay);
        synchronized (this) {
            wait(delay);
        }
        log.debug("Done waiting for: " + delay);

        return param;
    }

    public String delete() {
        return "You can't touch me";
    }

    protected String protectedMethod() {
        privateMethod();
        return "You can't touch me";
    }

    private String privateMethod() {
        return "You can't touch me";
    }

    public static String staticMethod() {
        return "static SpringServletTest.staticMethod() says hello.";
    }

    public String dangerOverload(String param1, int param2) {
        return param1 + "," + param2;
    }

    public String dangerOverload(String param1) {
        return param1;
    }

    public String dangerOverload() {
        return "hello";
    }

    public String dangerOverload(@SuppressWarnings("unused") String[] params) {
        return "helloarray";
    }

    public String dangerOverload(@SuppressWarnings("unused") TestBean test) {
        return "hellotestbean";
    }

    public int dangerOverload(int param1) {
        return param1;
    }

    public String error(InboundContext cx) {
        return "You should not see this: " + cx;
    }

    public AbstractBase[] downloadMapped() {
        return new AbstractBase[] { new ConcreteBBase(), new ConcreteCBase() };
    }

    public String uploadMapped(AbstractBase abstractBase) {
        if (abstractBase == null) {
            return "null";
        }
        return abstractBase.getClass().getName();
    }

    public String uploadMappedToUnmappedParamClass(Object unmappedBase) {
        if (unmappedBase == null) {
            return "null";
        }
        return unmappedBase.getClass().getName();
    }

    public ObjectWithLightClassMapping downloadLightlyMapped() {
        ObjectWithLightClassMapping lightObj = new ObjectWithLightClassMapping();
        lightObj.obj = new ObjectWithLightClassMapping();
        lightObj.array = new ObjectWithLightClassMapping[] { new ObjectWithLightClassMapping() };
        return lightObj;
    }

    public boolean uploadLightlyMapped(ObjectWithLightClassMapping lightObj) {
        Assert.assertSame(lightObj.getClass(), ObjectWithLightClassMapping.class);
        Assert.assertSame(lightObj.obj.getClass(), ObjectWithLightClassMapping.class);
        Assert.assertSame(lightObj.array[0].getClass(), ObjectWithLightClassMapping.class);
        return true;
    }

    public ConcreteIFace readOnlyProperty() {
        return new ConcreteIFace() {
            {
                setI(10);
                setFieldAbstract(5.0);
            }
        };
    }

    public void throwMapped() throws MyFancyException {
        throw new MyFancyException("fancy");
    }

    public void throwUnmapped() throws MyUnmappedException {
        throw new MyUnmappedException("unmapped");
    }

    public String uploadInterface(IFace impl) {
        if (impl == null) {
            return "null";
        }
        return impl.getClass().getName();
    }

    public OnePackageObject package1(OnePackageObject obj) {
        obj.i++;
        return obj;
    }

    public TwoPackagesObject package2(TwoPackagesObject obj) {
        obj.i++;
        return obj;
    }

    public void packagedException() throws MyFancyExceptionInPackage {
        throw new MyFancyExceptionInPackage("fancy");
    }

    public WildcardObjectContainer flatWildcardObjects(WildcardObjectContainer in) {
        return in;
    }

    public WildcardRecursiveObjectContainer recursiveWildcardObjects(WildcardRecursiveObjectContainer in) {
        return in;
    }

    public StrangeNameObject strangeName(StrangeNameObject obj) {
        obj.i++;
        return obj;
    }

    public StrangeNameWithPackageObject strangeNameWithPackage(StrangeNameWithPackageObject obj) {
        obj.i++;
        return obj;
    }

    public String serverChecks() {
        ScriptSession scriptSession = WebContextFactory.get().getScriptSession();
        scriptSession.invalidate();

        if (scriptSession.isInvalidated()) {
            return "invalidateMe() succeeded";
        } else {
            return "invalidateMe() failed";
        }
    }

    public String setValue(String elementId, String value) {
        ScriptSessions.addFunctionCall("dwr.util.setValue", elementId, value);
        return value;
    }

    public void setCookie(String name, String value) {
        Document.setCookie(name, value);
    }

    public Verify checkScriptSessionBindingListener() {
        final AtomicInteger bound = new AtomicInteger();
        final AtomicInteger unbound = new AtomicInteger();

        ScriptSession session = WebContextFactory.get().getScriptSession();
        ScriptSessionBindingListener listener = new ScriptSessionBindingListener() {
            /* (non-Javadoc)
             * @see org.directwebremoting.event.ScriptSessionBindingListener#valueBound(org.directwebremoting.event.ScriptSessionBindingEvent)
             */
            public void valueBound(ScriptSessionBindingEvent event) {
                bound.addAndGet(1);
            }

            /* (non-Javadoc)
             * @see org.directwebremoting.event.ScriptSessionBindingListener#valueUnbound(org.directwebremoting.event.ScriptSessionBindingEvent)
             */
            public void valueUnbound(ScriptSessionBindingEvent event) {
                unbound.addAndGet(1);
            }
        };

        Verify verify = new Verify();
        verify.equals("Start, bound", bound.get(), 0);
        verify.equals("Start, unbound", unbound.get(), 0);

        session.setAttribute("test", listener);
        verify.equals("After set, bound", bound.get(), 1);
        verify.equals("After set, unbound", unbound.get(), 0);

        session.setAttribute("irrelevant", 0);
        verify.equals("After set, bound", bound.get(), 1);
        verify.equals("After set, unbound", unbound.get(), 0);

        session.removeAttribute("test");
        verify.equals("After set, bound", bound.get(), 1);
        verify.equals("After set, unbound", unbound.get(), 1);

        return verify;
    }

    public Verify checkScriptSessionListener(final JavascriptFunction progress1,
            final JavascriptFunction progress2) {
        final ServerContext serverContext = ServerContextFactory.get();
        final String testPage = serverContext.getContextPath() + "/checkSession.html";

        Verify verify = new Verify();

        final int createdBefore = TestScriptSessionListener.created;
        final int createdBefore2 = Test2ScriptSessionListener.created;
        final int destroyedBefore = TestScriptSessionListener.destroyed;
        final int destroyedBefore2 = Test2ScriptSessionListener.destroyed;

        // At least one test window is open ...
        verify.isTrue("createdBefore > 0", createdBefore > 0);
        verify.isTrue("createdBefore2 > 0", createdBefore2 > 0);

        // Open a new window
        Window.open(testPage, "checkSession");

        // We'll fill these in in the first cron, and use them in the second
        final AtomicInteger createdMid = new AtomicInteger();
        final AtomicInteger createdMid2 = new AtomicInteger();
        final AtomicInteger destroyedMid = new AtomicInteger();
        final AtomicInteger destroyedMid2 = new AtomicInteger();

        // Give it a second to open, check counters and close it
        ScheduledThreadPoolExecutor executorService = serverContext.getContainer()
                .getBean(ScheduledThreadPoolExecutor.class);
        executorService.schedule(new Runnable() {
            public void run() {
                createdMid.set(TestScriptSessionListener.created);
                createdMid2.set(Test2ScriptSessionListener.created);
                destroyedMid.set(TestScriptSessionListener.destroyed);
                destroyedMid2.set(Test2ScriptSessionListener.destroyed);

                Verify verify1 = new Verify();
                verify1.isTrue("createdMid > createdBefore", createdMid.intValue() > createdBefore);
                verify1.isTrue("createdMid2 > createdBefore2", createdMid2.intValue() > createdBefore2);
                verify1.equals("destroyedMid == destroyedBefore", destroyedMid.intValue(), destroyedBefore);
                verify1.equals("destroyedMid2 == destroyedBefore2", destroyedMid2.intValue(), destroyedBefore2);

                // Find it and close it
                Browser.withPage(testPage, new Runnable() {
                    public void run() {
                        Window.close();
                    }
                });
                progress1.executeAndClose(verify1);
            }
        }, 1, TimeUnit.SECONDS);

        // Give it 2 seconds to open and be closed then check counters again
        executorService.schedule(new Runnable() {
            public void run() {
                int createdAfter = TestScriptSessionListener.created;
                int createdAfter2 = Test2ScriptSessionListener.created;
                int destroyedAfter = TestScriptSessionListener.destroyed;
                int destroyedAfter2 = Test2ScriptSessionListener.destroyed;

                Verify verify2 = new Verify();
                verify2.equals("createdAfter == createdMid", createdAfter, createdMid.intValue());
                verify2.equals("createdAfter2 == createdMid2", createdAfter2, createdMid2.intValue());
                verify2.isTrue("destroyedAfter > destroyedMid", destroyedAfter > destroyedMid.intValue());
                verify2.isTrue("destroyedAfter2 > destroyedMid2", destroyedAfter2 > destroyedMid2.intValue());

                progress2.executeAndClose(verify2);
            }
        }, 2, TimeUnit.SECONDS);

        return verify;
    }

    public Verify variousChecks() {
        Verify verify = new Verify();

        if (!Test1Filter.isFiltered()) {
            verify.fail("Missing Test1Filter (should be global in dwr.xml)");
        }

        if (!Test2Filter.isFiltered()) {
            verify.fail("Missing Test2Filter (should be local to Test in dwr.xml)");
        }

        return verify;
    }

    public void testRedirectResponse(HttpServletResponse response) throws IOException {
        response.setStatus(302);
    }

    public Verify checkImHere() {
        WebContext webContext = WebContextFactory.get();
        ScriptSession scriptSession = webContext.getScriptSession();

        String attributeName = "attr:" + scriptSession.getId() + ":" + scriptSession.getPage();
        Verify verify = new Verify();

        scriptSession.setAttribute(attributeName, true);

        ScriptSessionFilter filter = new TestScriptSessionFilter(attributeName);
        String page = webContext.getCurrentPage();

        Browser.withPage(page, new CheckCount(1, attributeName, false, "withPage", verify));
        Browser.withAllSessions(new CheckCount(1, attributeName, false, "withAllSessions", verify));
        Browser.withCurrentPage(new CheckCount(1, attributeName, false, "withCurrentPage", verify));
        Browser.withSession(scriptSession.getId(), new CheckCount(1, attributeName, false, "withSession", verify));

        Browser.withPageFiltered(page, filter, new CheckCount(1, attributeName, true, "withPageFiltered", verify));
        Browser.withAllSessionsFiltered(filter,
                new CheckCount(1, attributeName, true, "withAllSessionsFiltered", verify));
        Browser.withCurrentPageFiltered(filter,
                new CheckCount(1, attributeName, true, "withCurrentPageFiltered", verify));

        ServerContext serverContext = ServerContextFactory.get();
        log.debug("** Testing Browser against local context: " + serverContext);

        Browser.withPage(serverContext, page, new CheckCount(1, attributeName, false, "withPage+Context", verify));
        Browser.withAllSessions(serverContext,
                new CheckCount(1, attributeName, false, "withAllSessions+Context", verify));
        Browser.withSession(serverContext, scriptSession.getId(),
                new CheckCount(1, attributeName, false, "withSession+Context", verify));

        Browser.withPageFiltered(serverContext, page, filter,
                new CheckCount(1, attributeName, true, "withPageFiltered+Context", verify));
        Browser.withAllSessionsFiltered(serverContext, filter,
                new CheckCount(1, attributeName, true, "withAllSessionsFiltered+Context", verify));

        /*
        // Using a single page talking to multiple DWR servlets is not supported
        Collection<ServerContext> contexts = StartupUtil.getAllServerContexts();
        for (ServerContext otherContext : contexts)
        {
        if (otherContext.getServletConfig().equals(serverContext.getServletConfig()))
        {
            log.debug("** Skipping current config: " + otherContext);
            continue;
        }
            
        log.debug("** Testing Browser against other context: " + otherContext);
            
        Browser.withPage(otherContext, page, new CheckCount(0, attributeName, false, "withPage+Other", verify));
        Browser.withAllSessions(otherContext, new CheckCount(0, attributeName, false, "withAllSessions+Other", verify));
        Browser.withSession(otherContext, scriptSession.getId(), new CheckCount(0, attributeName, false, "withSession+Other", verify));
            
        Browser.withPageFiltered(otherContext, page, filter, new CheckCount(0, attributeName, true, "withPageFiltered+Other", verify));
        }
        */
        return verify;
    }

    public String createSession() throws Exception {
        WebContext webCtx = WebContextFactory.get();
        return webCtx.getHttpServletRequest().getSession().getId();
    }

    public void invalidateSession() throws Exception {
        WebContext webCtx = WebContextFactory.get();
        webCtx.getHttpServletRequest().getSession().invalidate();
    }

    public String createSessionWithDelay() throws Exception {
        Thread.sleep(2000);
        WebContext webCtx = WebContextFactory.get();
        String scriptSessionId = webCtx.getScriptSession().getId();
        String sessionId = webCtx.getHttpServletRequest().getSession().getId();
        return "scriptSessionId:" + scriptSessionId + ", sessionId:" + sessionId;
    }

    public void expireJsessionIdCookie() throws Exception {
        WebContext webCtx = WebContextFactory.get();
        String path = webCtx.getHttpServletRequest().getContextPath();
        if (path.equals(""))
            path = "/";
        webCtx.getHttpServletResponse().addHeader("Set-Cookie",
                "JSESSIONID=novalue; Expires=Thu, 01-Jan-1970 00:00:00 GMT; Path=" + path + "; HttpOnly");
        if (!path.endsWith("/")) {
            webCtx.getHttpServletResponse().addHeader("Set-Cookie",
                    "JSESSIONID=novalue; Expires=Thu, 01-Jan-1970 00:00:00 GMT; Path=" + path + "/; HttpOnly");
        }
    }

    protected class TestScriptSessionFilter implements ScriptSessionFilter {
        public TestScriptSessionFilter(String attributeName) {
            this.attributeName = attributeName;
        }

        /* (non-Javadoc)
         * @see org.directwebremoting.ScriptSessionFilter#match(org.directwebremoting.ScriptSession)
         */
        public boolean match(ScriptSession session) {
            Object check = session.getAttribute(attributeName);
            return (check != null && check.equals(Boolean.TRUE));
        }

        private String attributeName;
    }

    public String getSecretFilePath() {
        return WebContextFactory.get().getServletContext().getRealPath("WEB-INF/secretfile.txt");
    }

    public void reverseAjaxCallFunction(final String clientFunction, final int[] callIntervals) {
        final String scriptSessionId = WebContextFactory.get().getScriptSession().getId();
        final ServerContext serverContext = WebContextFactory.get();
        Runnable r = new Runnable() {
            public void run() {
                try {
                    for (int i = 0; i < callIntervals.length; i++) {
                        final int callIndex = i;
                        Thread.sleep(callIntervals[i]);
                        Browser.withAllSessionsFiltered(serverContext, new ScriptSessionFilter() {
                            public boolean match(ScriptSession session) {
                                return session.getId().equals(scriptSessionId);
                            }
                        }, new Runnable() {
                            public void run() {
                                ScriptSessions.addFunctionCall(clientFunction, callIndex);
                            }
                        });
                    }
                } catch (InterruptedException e) {
                    // Ignore
                }
            }
        };
        Thread t = new Thread(r);
        t.start();
    }

    public void reverseAjaxCheckHttpSessionId(final String funcName) {
        final WebContext webCtx = WebContextFactory.get();
        Runnable r = new Runnable() {
            public void run() {
                try {
                    Thread.sleep(500);
                    ScriptSession scriptSession = webCtx.getScriptSession();
                    ScriptBuffer script = new ScriptBuffer();
                    script.appendCall(funcName, scriptSession.getHttpSessionId());
                    scriptSession.addScript(script);
                } catch (InterruptedException e) {
                    // Ignore
                }
            }
        };
        Thread t = new Thread(r);
        t.start();
    }

    public void reverseAjaxCreateSessionFromWorkerThread(final String replyFuncName) {
        final ScriptSession scriptSession = WebContextFactory.get().getScriptSession();
        HttpServletRequest callRequest = WebContextFactory.get().getHttpServletRequest();
        if (callRequest.getSession(false) != null) {
            callReverseAjaxFunc(scriptSession, replyFuncName, "Error: session already exists");
            return;
        }
        Runnable r = new Runnable() {
            public void run() {
                try {
                    Thread.sleep(500);
                    scriptSession.addRunnable(new Runnable() {
                        public void run() {
                            HttpServletRequest pollRequest1 = WebContextFactory.get().getHttpServletRequest();
                            HttpSession session = pollRequest1.getSession();
                            if (!session.isNew()) {
                                callReverseAjaxFunc(scriptSession, replyFuncName,
                                        "Error: session is already confirmed");
                                return;
                            }
                            scriptSession.addRunnable(new Runnable() {
                                public void run() {
                                    HttpServletRequest pollRequest2 = WebContextFactory.get()
                                            .getHttpServletRequest();
                                    HttpSession session = pollRequest2.getSession();
                                    if (session.isNew()) {
                                        callReverseAjaxFunc(scriptSession, replyFuncName,
                                                "Error: session is not confirmed");
                                        return;
                                    }
                                    callReverseAjaxFunc(scriptSession, replyFuncName, "ok: " + session.getId());
                                }
                            });
                        }
                    });
                } catch (InterruptedException e) {
                    // Ignore
                }
            }
        };
        Thread t = new Thread(r);
        t.start();
    }

    private void callReverseAjaxFunc(ScriptSession scriptSession, String funcName, Object... args) {
        ScriptBuffer script = new ScriptBuffer();
        script.appendCall(funcName, args);
        scriptSession.addScript(script);
    }

    /**
     * A checker that ensures there are a given number of matching sessions
     */
    protected class CheckCount implements Runnable {
        /**
         * Ctor if we wish to filter by attributeName
         */
        protected CheckCount(int expected, String attributeName, boolean filtered, String debugMsg, Verify verify) {
            this.expected = expected;
            this.attributeName = attributeName;
            this.filtered = filtered;
            this.debugMsg = debugMsg;
            this.verify = verify;
        }

        /* (non-Javadoc)
         * @see java.lang.Runnable#run()
         */
        public void run() {
            Collection<ScriptSession> sessions = Browser.getTargetSessions();
            if (filtered) {
                verify.equals(debugMsg + "+WrongCount", expected, sessions.size());

                for (ScriptSession session : sessions) {
                    verify.isNotNull(debugMsg + "+NoAttr+" + session, session.getAttribute(attributeName));
                }
            } else {
                int found = 0;
                for (ScriptSession session : sessions) {
                    Object check = session.getAttribute(attributeName);
                    if (check != null) {
                        found++;
                    }
                }
                verify.equals(debugMsg, expected, found);
            }
        }

        private final int expected;
        private final String attributeName;
        private final boolean filtered;
        private final String debugMsg;
        private final Verify verify;
    }

    /**
     * JUnit test runner!
     * @throws InterruptedException 
     */
    public Verify runAllJUnitTests(final JavascriptFunction noteProgressInScratch) throws InterruptedException {
        final Verify verify = new Verify();

        Runnable r = new Runnable() {
            public void run() {
                ClasspathScanner scanner = new ClasspathScanner("org.directwebremoting", true);
                Set<String> classNames = scanner.getClasses();
                Set<Class<?>> tests = new HashSet<Class<?>>();

                for (Iterator<String> it = classNames.iterator(); it.hasNext();) {
                    String className = it.next();

                    if (className.endsWith("Test")) {
                        try {
                            Class<?> type = Class.forName(className);
                            tests.add(type);
                        } catch (ClassNotFoundException ex) {
                            // ignore
                        }
                    }
                }

                JUnitCore core = new JUnitCore();
                core.addListener(new RunListener() {
                    int completed = 0;
                    int failures = 0;

                    @Override
                    public void testFailure(Failure failure) throws Exception {
                        failures++;
                    }

                    @Override
                    public void testFinished(Description description) throws Exception {
                        completed++;
                        noteProgressInScratch.execute();
                    }
                });

                Class<?>[] testArray = tests.toArray(new Class<?>[tests.size()]);
                Result results = core.run(testArray);

                for (Failure failure : results.getFailures()) {
                    verify.fail("Desc: " + failure.getDescription() + "Header: " + failure.getTestHeader()
                            + "Message: " + failure.getMessage());
                }
            }
        };

        // Run in a separate thread to avoid interference when run from the web
        Thread t = new Thread(r);
        t.start();
        t.join();

        return verify;
    }

    /**
     * The log stream
     */
    protected static final Log log = LogFactory.getLog(Test.class);
}