Initial Contribution

Signed-off-by: Vinay Vishal <vinay.vishal@oracle.com>
diff --git a/appserver/tests/appserv-tests/devtests/web/servlet-3.1/nonBlockingInputOutput/WebTest.java b/appserver/tests/appserv-tests/devtests/web/servlet-3.1/nonBlockingInputOutput/WebTest.java
new file mode 100644
index 0000000..f36a7ba
--- /dev/null
+++ b/appserver/tests/appserv-tests/devtests/web/servlet-3.1/nonBlockingInputOutput/WebTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+import java.io.*;
+import java.net.*;
+import java.util.List;
+import java.util.Map;
+import com.sun.ejte.ccl.reporter.*;
+
+/*
+ * Unit test for Non blocking Input and then non blocking Output
+ */
+public class WebTest {
+
+    private static String TEST_NAME = "servlet-3.1-non-blocking-Input-Output";
+    private static String EXPECTED_RESPONSE = "HelloWorld";
+
+    private static SimpleReporterAdapter stat
+        = new SimpleReporterAdapter("appserv-tests");
+
+    public static void main(String[] args) {
+        String host = args[0];
+        int port = Integer.parseInt(args[1]);
+        String contextRoot = args[2];
+        stat.addDescription("Unit test for non blocking read and then non blocking write");
+
+        try {
+            URL url = new URL("http://" + host + ":" + port + "/" + contextRoot + "/test?testname=abc&data=1");
+            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
+            conn.setRequestMethod("POST");
+            conn.setRequestProperty("Content-type", "text/plain; charset=utf-8");
+            conn.setChunkedStreamingMode(5);
+            conn.setDoOutput(true);
+            conn.connect();
+
+            BufferedReader input = null;
+            BufferedWriter output = null;
+            boolean expected = false;
+            try {
+                output = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
+                try {
+                    String data = "Hello";
+                    output.write(data);
+                    output.flush();
+                    int sleepInSeconds = 3;
+                    System.out.format("Sleeping %d sec\n", sleepInSeconds);
+                    Thread.sleep(sleepInSeconds * 1000);
+                    data = "World";
+                    output.write(data);
+                    output.flush();
+                    output.close();
+                } catch(Exception ex) {
+                    ex.printStackTrace();
+                }
+                input = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+                String line = null;
+                while ((line = input.readLine()) != null) {
+                    System.out.println(line);
+                    int slashInd = line.indexOf("/", 1);
+                    expected = line.replace("/", "").equals(EXPECTED_RESPONSE);
+                    if (expected) {
+                        break;
+                    }
+                }
+            } finally {
+                try {
+                    if (input != null) {
+                        input.close();
+                    }
+                } catch(Exception ex) {
+                }
+
+                try {
+                    if (output != null) {
+                        output.close();
+                    }
+                } catch(Exception ex) {
+                }
+            }
+            
+            stat.addStatus(TEST_NAME, ((expected) ? stat.PASS : stat.FAIL));
+        } catch(Exception ex) {
+            ex.printStackTrace();
+            stat.addStatus(TEST_NAME, stat.FAIL);
+        }
+
+        stat.printSummary();
+    }
+}
diff --git a/appserver/tests/appserv-tests/devtests/web/servlet-3.1/nonBlockingInputOutput/build.properties b/appserver/tests/appserv-tests/devtests/web/servlet-3.1/nonBlockingInputOutput/build.properties
new file mode 100644
index 0000000..1a41e29
--- /dev/null
+++ b/appserver/tests/appserv-tests/devtests/web/servlet-3.1/nonBlockingInputOutput/build.properties
@@ -0,0 +1,23 @@
+<!--
+
+    Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+ 
+
+<property name="module" value="web"/>
+<property name="appname" value="${module}-servlet-3.1-non-blocking-input"/>
+<property name="assemble" value="${build.classes.dir}/archive"/>
+<property name="contextroot" value="/${appname}"/>
diff --git a/appserver/tests/appserv-tests/devtests/web/servlet-3.1/nonBlockingInputOutput/build.xml b/appserver/tests/appserv-tests/devtests/web/servlet-3.1/nonBlockingInputOutput/build.xml
new file mode 100644
index 0000000..997beda
--- /dev/null
+++ b/appserver/tests/appserv-tests/devtests/web/servlet-3.1/nonBlockingInputOutput/build.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+
+    Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<!DOCTYPE project [
+<!ENTITY commonSetup SYSTEM "./../../../../config/properties.xml">
+<!ENTITY commonBuild SYSTEM "./../../../../config/common.xml">
+<!ENTITY run SYSTEM "./../../../../config/run.xml">
+<!ENTITY testproperties SYSTEM "./build.properties">
+]>
+
+<project name="webcontainer_unittest" default="all" basedir=".">
+
+    &commonSetup;
+    &commonBuild;
+    &testproperties;
+    &run;
+
+    <target name="all" depends="build,deploy,run,undeploy"/>
+
+    <target name="clean" depends="init-common">
+      <antcall target="clean-common"/>
+      <delete>
+          <fileset dir="." includes="*.class"/>
+      </delete>
+    </target>
+       
+    <target name="compile" depends="clean">
+        <antcall target="compile-common">
+            <param name="src" value="servlet"/>
+        </antcall>
+    </target>
+
+    <target name="build" depends="compile">
+        <antcall target="webclient-war-common">
+            <param name="hasWebclient" value="yes"/>
+        </antcall>
+
+        <javac srcdir="." classpath="${env.APS_HOME}/lib/reportbuilder.jar"
+               includes="WebTest.java">
+            <classpath>
+                <pathelement location="${env.APS_HOME}/lib/reportbuilder.jar"/>
+            </classpath>
+        </javac>
+    </target> 
+
+    <target name="deploy" depends="init-common">
+        <antcall target="deploy-war-common"/>
+    </target>
+
+    <target name="run" depends="init-common">
+        <java classname="WebTest">
+            <sysproperty key="debug" value="true"/>
+            <arg value="${http.host}"/>
+            <arg value="${http.port}"/>
+            <arg value="${contextroot}"/>
+            <classpath>
+                <pathelement location="${env.APS_HOME}/lib/reportbuilder.jar"/>
+                <pathelement location="."/>
+            </classpath>
+        </java>
+    </target>
+
+    <target name="undeploy" depends="init-common">
+        <antcall target="undeploy-war-common"/>
+    </target>   
+
+    <target name="usage">
+        <antcall target="usage-common"/>
+    </target>
+
+</project>
diff --git a/appserver/tests/appserv-tests/devtests/web/servlet-3.1/nonBlockingInputOutput/servlet/test/TestServlet.java b/appserver/tests/appserv-tests/devtests/web/servlet-3.1/nonBlockingInputOutput/servlet/test/TestServlet.java
new file mode 100644
index 0000000..f7be1b7
--- /dev/null
+++ b/appserver/tests/appserv-tests/devtests/web/servlet-3.1/nonBlockingInputOutput/servlet/test/TestServlet.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package test;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.ReadListener;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@WebServlet(urlPatterns="/test", asyncSupported=true)
+public class TestServlet extends HttpServlet {
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse res)
+            throws IOException, ServletException {
+
+        AsyncContext ac = req.startAsync();
+        ac.addListener(new AsyncListener() {
+            public void onComplete(AsyncEvent event) throws IOException {
+                System.out.println("my asyncListener.onComplete");
+            }
+            public void onError(AsyncEvent event) {
+                System.out.println("my asyncListener.onError: " + event.getThrowable());
+            }
+            public void onStartAsync(AsyncEvent event) {
+                System.out.println("my asyncListener.onStartAsync");
+            }
+            public void onTimeout(AsyncEvent event) {
+                System.out.println("my asyncListener.onTimeout");
+            }
+        });
+
+        ServletInputStream input = req.getInputStream();
+        // read all data first
+        ReadListener readListener = new ReadListenerImpl(input, res, ac);
+        input.setReadListener(readListener);
+    }
+
+    static class ReadListenerImpl implements ReadListener {
+        private ServletInputStream input = null;
+        private HttpServletResponse res = null;
+        private AsyncContext ac = null;
+        private Queue<String> queue = new LinkedBlockingQueue<String>();
+
+        ReadListenerImpl(ServletInputStream in, HttpServletResponse r,
+                AsyncContext c) {
+            input = in;
+            res = r;
+            ac = c;
+        }
+
+        public void onDataAvailable() throws IOException {
+            StringBuilder sb = new StringBuilder();
+            System.out.println("--> onDataAvailable");
+            int len = -1;
+            byte b[] = new byte[1024];
+            while (input.isReady() 
+                    && (len = input.read(b)) != -1) {
+                String data = new String(b, 0, len);
+                System.out.println("--> " + data);
+                sb.append(data);
+            }
+            queue.add(sb.toString());
+        }
+
+        public void onAllDataRead() throws IOException {
+            System.out.println("--> onAllDataRead");
+            // now all data are read, write the result
+            ServletOutputStream output = res.getOutputStream();
+            WriteListener writeListener = new WriteListenerImpl(output, queue, ac);
+            output.setWriteListener(writeListener);
+        }
+
+        public void onError(final Throwable t) {
+            ac.complete();
+            t.printStackTrace();
+        }
+    }
+
+    static class WriteListenerImpl implements WriteListener {
+        private ServletOutputStream output = null;
+        private Queue<String> queue = null;
+        private AsyncContext ac = null;
+
+        WriteListenerImpl(ServletOutputStream sos, Queue<String> q,
+                AsyncContext c) {
+            output = sos;
+            queue = q;
+            ac = c;
+        }
+
+        public void onWritePossible() throws IOException {
+            System.out.println("--> onWritePossible");
+            System.out.println("--> queue: " + queue);
+            while (queue.peek() != null && output.isReady()) {
+                String data = queue.poll();
+                System.out.println("--> data = " + data);
+                output.print(data);
+            }
+            System.out.println("--> ac.complete");
+            if (queue.peek() == null) {
+                ac.complete();
+            }
+        }
+
+        public void onError(final Throwable t) {
+            ac.complete();
+            t.printStackTrace();
+        }
+    }
+}