

import java.io.IOException;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 *
 * @see <a href="https://docs.oracle.com/javaee/7/tutorial/servlets.htm">17 Java Servlet Technology</a>
 * @see <a href="https://docs.oracle.com/javaee/7/tutorial/servlets012.htm">17.12 Asynchronous Processing</a>
 * @see <a href="https://docs.oracle.com/javaee/7/tutorial/servlets013.htm">17.13 Nonblocking I/O</a>
 *
 */
@WebServlet( loadOnStartup = 1, urlPatterns = { "/test/*" }, asyncSupported = true )
public class AsyncIOServletTest extends HttpServlet
{
    private static final long serialVersionUID = 1L;

    private static final Logger LOGGER = LoggerFactory.getLogger( AsyncIOServletTest.class );

    @Override
    public void doPost( HttpServletRequest request, HttpServletResponse response ) throws IOException
    {

        LOGGER.info( "doPost(..)" ); //$NON-NLS-1$

        final AsyncContext context = request.startAsync();

        context.addListener( new AsyncListener()
        {

            @Override
            public void onComplete( AsyncEvent event )
            {

                LOGGER.info( "onComplete called: " + event.toString() ); //$NON-NLS-1$
            }

            @Override
            public void onTimeout( AsyncEvent event )
            {

                LOGGER.info( "onTimeout called: " + event.toString() ); //$NON-NLS-1$
            }

            @Override
            public void onError( AsyncEvent event )
            {

                LOGGER.info( "onError called: " + event.toString() ); //$NON-NLS-1$
            }

            @Override
            public void onStartAsync( AsyncEvent event )
            {

                LOGGER.info( "onStartAsync called: " + event.toString() ); //$NON-NLS-1$
            }
        } );



//        handleRequestAsPerJeeTutorial( request, response, context  );

        handleRequestAsPerJeeTutorialWithInputStreamIssue( request, response, context );
    }


    private void handleRequestAsPerJeeTutorial(
        final HttpServletRequest request,
        @SuppressWarnings( "unused" ) HttpServletResponse response,
        final AsyncContext acontext )
                throws IOException
    {
        LOGGER.info( "handleRequestAsPerJeeTutorial(..)" ); //$NON-NLS-1$

        final ServletInputStream input = request.getInputStream();

        input.setReadListener( new ReadListener()
        {
            byte buffer[] = new byte[4 * 1024];
            StringBuilder sbuilder = new StringBuilder();

            @Override
            public void onDataAvailable()
            {
                LOGGER.info( "onDataAvailable()" ); //$NON-NLS-1$
                try
                {
                    do
                    {
                        int length = input.read( buffer );
                        sbuilder.append( new String( buffer, 0, length ) );
                    } while ( input.isReady() );
                }
                catch ( IOException ex )
                {
                    LOGGER.error( "Unexpected exception!", ex ); //$NON-NLS-1$
                }
            }

            @Override
            public void onAllDataRead()
            {
                LOGGER.info( "onAllDataRead()" ); //$NON-NLS-1$
                try
                {
                    acontext.getResponse().getWriter().write( "...the response..." ); //$NON-NLS-1$
                }
                catch ( IOException ex )
                {
                    LOGGER.error( "Unexpected exception!", ex ); //$NON-NLS-1$
                }
                acontext.complete();
            }

            @Override
            public void onError( Throwable t )
            {
                LOGGER.error( "Unexpected error!", t ); //$NON-NLS-1$
            }
        } );
    }

    /**
     * @param context
     * @param charsetName
     * @throws IOException
     */
    private void handleRequestAsPerJeeTutorialWithInputStreamIssue(
        final HttpServletRequest request,
        @SuppressWarnings( "unused" ) HttpServletResponse response,
        final AsyncContext context )
                throws IOException
    {

        try ( final ServletInputStream input = request.getInputStream() )
        {

            input.setReadListener( new ReadListener()
            {

                StringBuilder sbuilder = new StringBuilder();

                byte buffer[] = new byte[4 * 1024];

                @Override
                public void onDataAvailable()
                {
                    LOGGER.info( "onDataAvailable()" ); //$NON-NLS-1$
                    try
                    {
                        do
                        {
                            int length = input.read( buffer );
                            sbuilder.append( new String( buffer, 0, length ) );
                        } while ( input.isReady() );
                    }
                    catch ( IOException ex )
                    {
                        LOGGER.error( "Unexpected exception!", ex ); //$NON-NLS-1$
                    }
                }

                @Override
                public void onAllDataRead()
                {
                    LOGGER.info( "onAllDataRead()" ); //$NON-NLS-1$
                    try
                    {
                        context.getResponse().getWriter().write( "...the response..." ); //$NON-NLS-1$
                    }
                    catch ( IOException ex )
                    {
                        LOGGER.error( "Unexpected exception!", ex ); //$NON-NLS-1$
                    }
                    context.complete();
                }

                @Override
                public void onError( Throwable t )
                {
                    LOGGER.error( "Unexpected error!", t ); //$NON-NLS-1$
                }
            } );
        }
    }
}