/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.pluto.testsuite.test;

import java.io.IOException;

import javax.portlet.PortletContext;
import javax.portlet.PortletException;
import javax.portlet.PortletRequest;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.PortletResponse;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.pluto.testsuite.TestResult;
import org.apache.pluto.testsuite.TestUtils;

public class DispatcherRequestTest extends AbstractReflectivePortletTest {
	
	/** Logger. */
	private static final Logger LOG = LoggerFactory.getLogger(DispatcherRequestTest.class);
	
	/** The path to the companion servlet. */
    private static final String SERVLET_PATH = "/test/DispatcherRequestTest_Servlet";
    
    private static final String CHECK_PATH_INFO = "/checkPathInfo";
    private static final String CHECK_REQUEST_URI = "/checkRequestURI";
    private static final String CHECK_CONTEXT_PATH = "/checkContextPath";
    private static final String CHECK_SERVLET_PATH = "/checkServletPath";
    private static final String CHECK_QUERY_STRING = "/checkQueryString";
    
    private static final String GET_REQUEST_URI = "/getRequestURI";
    private static final String GET_CONTEXT_PATH = "/getContextPath";
    private static final String GET_SERVLET_PATH = "/getServletPath";
    private static final String GET_QUERY_STRING = "/getQueryString";
    
    /** The query string appended to the dispatch URI. */
    private static final String QUERY_STRING = "paramName=paramValue";
    
    /** The request attribute key which associates the test result. */
    private static final String RESULT_KEY =
            DispatcherRequestTest.class.getName() + ".RESULT_KEY";
    
    /** The key for the expected request URI. */
    private static final String EXPECTED_REQUEST_URI =
            DispatcherRequestTest.class.getName() + ".REQUEST_URI";
    
    /** The key for the expected context path. */
    private static final String EXPECTED_CONTEXT_PATH =
            DispatcherRequestTest.class.getName() + ".CONTEXT_PATH";
    
	
    // Test Methods ------------------------------------------------------------
    
    protected TestResult checkPathInfo(PortletContext context,
                                       PortletRequest request,
                                       PortletResponse response)
    throws IOException, PortletException {
    	return doCheckIncludedAttribute(context, request, response, CHECK_PATH_INFO);
    }
    
    protected TestResult checkRequestURI(PortletContext context,
                                         PortletRequest request,
                                         PortletResponse response)
    throws IOException, PortletException {
    	return doCheckIncludedAttribute(context, request, response, CHECK_REQUEST_URI);
    }
    	
    protected TestResult checkGetRequestURI(PortletContext context,
                                            PortletRequest request,
                                            PortletResponse response)
    throws IOException, PortletException {
    	return doCheckIncludedAttribute(context, request, response, GET_REQUEST_URI);
    }

    protected TestResult checkContextPath(PortletContext context,
                                          PortletRequest request,
                                          PortletResponse response)
    throws IOException, PortletException {
    	return doCheckIncludedAttribute(context, request, response, CHECK_CONTEXT_PATH);
    }
    
    protected TestResult checkGetContextPath(PortletContext context,
                                             PortletRequest request,
                                             PortletResponse response)
    throws IOException, PortletException {
    	return doCheckIncludedAttribute(context, request, response, GET_CONTEXT_PATH);
    }

    protected TestResult checkServletPath(PortletContext context,
                                          PortletRequest request,
                                          PortletResponse response)
    throws IOException, PortletException {
    	return doCheckIncludedAttribute(context, request, response, CHECK_SERVLET_PATH);
    }
    
    protected TestResult checkGetServletPath(PortletContext context,
                                             PortletRequest request,
                                             PortletResponse response)
    throws IOException, PortletException {
    	return doCheckIncludedAttribute(context, request, response, GET_SERVLET_PATH);
    }

    protected TestResult checkQueryString(PortletContext context,
                                          PortletRequest request,
                                          PortletResponse response)
    throws IOException, PortletException {
    	return doCheckIncludedAttribute(context, request, response, CHECK_QUERY_STRING);
    }
    
    protected TestResult checkGetQueryString(PortletContext context,
                                             PortletRequest request,
                                             PortletResponse response)
    throws IOException, PortletException {
    	return doCheckIncludedAttribute(context, request, response, GET_QUERY_STRING);
    }

    
    // Private Methods ---------------------------------------------------------
    
    private TestResult doCheckIncludedAttribute(PortletContext context,
                                                PortletRequest request,
                                                PortletResponse response,
                                                String pathInfo)
    throws IOException, PortletException {
    	
    	// Save expected values as request attributes.
    	String contextPath = request.getContextPath();
    	String requestUri = contextPath + SERVLET_PATH + pathInfo;
    	request.setAttribute(EXPECTED_REQUEST_URI, requestUri);
    	request.setAttribute(EXPECTED_CONTEXT_PATH, contextPath);

    	// Dispatch to the companion servlet: call checkParameters().
    	StringBuffer buffer = new StringBuffer();
    	buffer.append(SERVLET_PATH).append(pathInfo).append("?")
    			.append(QUERY_STRING);
    	if (LOG.isDebugEnabled()) {
    		LOG.debug("Dispatching to: " + buffer.toString());
    	}
    	PortletRequestDispatcher dispatcher = context.getRequestDispatcher(
    			buffer.toString());
    	dispatcher.include((RenderRequest) request, (RenderResponse) response);
    	
    	// Retrieve test result returned by the companion servlet.
    	TestResult result = (TestResult) request.getAttribute(RESULT_KEY);
    	request.removeAttribute(RESULT_KEY);
    	request.removeAttribute(EXPECTED_REQUEST_URI);
    	request.removeAttribute(EXPECTED_CONTEXT_PATH);
    	return result;
    }
    
    
    // Nested Companion Servlet Class ------------------------------------------
    
    public static class CompanionServlet extends HttpServlet {
    	
		private static final long serialVersionUID = -6032809452145653960L;
		
		private static final String KEY_REQUEST_URI = "javax.servlet.include.request_uri";
        private static final String KEY_CONTEXT_PATH = "javax.servlet.include.context_path";
        private static final String KEY_SERVLET_PATH = "javax.servlet.include.servlet_path";
        private static final String KEY_PATH_INFO = "javax.servlet.include.path_info";
        private static final String KEY_QUERY_STRING = "javax.servlet.include.query_string";
        
        
        // GenericServlet Impl -------------------------------------------------
        
        public String getServletInfo() {
        	return getClass().getName();
        }
        
        /**
         * This method uses <code>HttpServletRequest.getPathInfo</code> to
         * retrieve the requested path info, and forwards to the corresponding
         * check method to perform the test.
         */
        public void service(HttpServletRequest request,
                            HttpServletResponse response)
        throws ServletException, IOException {
        	TestResult result = null;
        	String pathInfo = getPathInfo(request);
        	if (CHECK_PATH_INFO.equals(pathInfo)) {
        		result = checkPathInfo(request, pathInfo);
        	} else if (CHECK_REQUEST_URI.equals(pathInfo)) {
        		result = checkRequestURI(request);
        	} else if (CHECK_CONTEXT_PATH.equals(pathInfo)) {
        		result = checkContextPath(request);
        	} else if (CHECK_SERVLET_PATH.equals(pathInfo)) {
        		result = checkServletPath(request);
        	} else if (CHECK_QUERY_STRING.equals(pathInfo)) {
        		result = checkQueryString(request);
        	} else if (GET_REQUEST_URI.equals(pathInfo)) {
        		result = checkGetRequestURI(request);
        	} else if (GET_CONTEXT_PATH.equals(pathInfo)) {
        		result = checkGetContextPath(request);
        	} else if (GET_SERVLET_PATH.equals(pathInfo)) {
        		result = checkGetServletPath(request);
        	} else if (GET_QUERY_STRING.equals(pathInfo)) {
        		result = checkGetQueryString(request);
        	} else {
        		result = failOnPathInfo(pathInfo);
        	}
            request.setAttribute(RESULT_KEY, result);
        }
        
        
        // Private Methods -----------------------------------------------------
        
        /**
         * Retrieves the path info from servlet request by invoking method
         * <code>HttpServletRequest.getPathInfo()</code>.
         * @param request  the servlet request dispatched from test portlet.
         * @return the path info retrieved from servlet request.
         */
        private String getPathInfo(HttpServletRequest request) {
            return request.getPathInfo();
        }
        
        private TestResult checkPathInfo(HttpServletRequest request,
                                         String expected) {
        	TestResult result = new TestResult();
        	result.setDescription("Ensure that included attribute '"
        			+ KEY_PATH_INFO + "' is available in servlet request.");
        	result.setSpecPLT("16.3.1");
        	
            String pathInfo = (String) request.getAttribute(KEY_PATH_INFO);
            if (pathInfo != null && pathInfo.equals(expected)) {
            	result.setReturnCode(TestResult.PASSED);
            } else {
            	TestUtils.failOnAssertion(KEY_PATH_INFO,
            			pathInfo, expected, result);
            }
            return result;
        }
        
        private TestResult checkRequestURI(HttpServletRequest request) {
        	TestResult result = new TestResult();
        	result.setDescription("Ensure that included attribute '"
        			+ KEY_REQUEST_URI + "' is available in servlet request.");
        	result.setSpecPLT("16.3.1");
        	
            String expected = (String) request.getAttribute(EXPECTED_REQUEST_URI);
            String requestURI = (String) request.getAttribute(KEY_REQUEST_URI);
            if (requestURI != null && requestURI.equals(expected)) {
            	result.setReturnCode(TestResult.PASSED);
            } else {
            	TestUtils.failOnAssertion(KEY_REQUEST_URI,
            			requestURI, expected, result);
            }
            return result;
        }
        
        private TestResult checkGetRequestURI(HttpServletRequest request) {
        	TestResult result = new TestResult();
        	result.setDescription("Ensure that method request.getRequestURI() "
        			+ "returns correct value.");
        	result.setSpecPLT("16.3.3");
        	
        	String expected = (String) request.getAttribute(EXPECTED_REQUEST_URI);
        	String requestURI = request.getRequestURI();
        	if (requestURI != null && requestURI.equals(expected)) {
        		result.setReturnCode(TestResult.PASSED);
        	} else {
        		TestUtils.failOnAssertion("request.getRequestURI()",
        				requestURI, expected, result);
        	}
        	return result;
        }
        
        private TestResult checkContextPath(HttpServletRequest request) {
        	TestResult result = new TestResult();
        	result.setDescription("Ensure that included attribute '"
        			+ KEY_CONTEXT_PATH + "' is available in servlet request.");
        	result.setSpecPLT("16.3.1");
        	
            String expected = (String) request.getAttribute(EXPECTED_CONTEXT_PATH);
            String contextPath = (String) request.getAttribute(KEY_CONTEXT_PATH);
            if (contextPath != null && contextPath.equals(expected)) {
            	result.setReturnCode(TestResult.PASSED);
            } else {
            	TestUtils.failOnAssertion(KEY_CONTEXT_PATH,
            			contextPath, expected, result);
            }
            return result;
        }
        
        private TestResult checkGetContextPath(HttpServletRequest request) {
        	TestResult result = new TestResult();
        	result.setDescription("Ensure that method request.getContextPath() "
        			+ "returns the correct value.");
        	result.setSpecPLT("16.3.3");
        	
            String expected = (String) request.getAttribute(EXPECTED_CONTEXT_PATH);
            String contextPath = request.getContextPath();
            if (contextPath != null && contextPath.equals(expected)) {
            	result.setReturnCode(TestResult.PASSED);
            } else {
            	TestUtils.failOnAssertion("request.getContextPath()",
            			contextPath, expected, result);
            }
            return result;
        }

        private TestResult checkServletPath(HttpServletRequest request) {
        	TestResult result = new TestResult();
        	result.setDescription("Ensure that included attribute '"
        			+ KEY_SERVLET_PATH + "' is available in servlet request.");
        	result.setSpecPLT("16.3.1");
        	
            String servletPath = (String) request.getAttribute(KEY_SERVLET_PATH);
            if (SERVLET_PATH.equals(servletPath)) {
            	result.setReturnCode(TestResult.PASSED);
            } else {
            	TestUtils.failOnAssertion(KEY_SERVLET_PATH,
            			servletPath, SERVLET_PATH, result);
            }
            return result;
        }
        
        private TestResult checkGetServletPath(HttpServletRequest request) {
        	TestResult result = new TestResult();
        	result.setDescription("Ensure that method request.getServletPath() "
        			+ "returns the correct value.");
        	result.setSpecPLT("16.3.3");
        	
            String servletPath = request.getServletPath();
            if (SERVLET_PATH.equals(servletPath)) {
            	result.setReturnCode(TestResult.PASSED);
            } else {
            	TestUtils.failOnAssertion("request.getServletPath()",
            			servletPath, SERVLET_PATH, result);
            }
            return result;
        }
        
        private TestResult checkQueryString(HttpServletRequest request) {
        	TestResult result = new TestResult();
        	result.setDescription("Ensure that included attribute '"
        			+ KEY_QUERY_STRING + "' is available in servlet request.");
        	result.setSpecPLT("16.3.1");
        	
            String queryString = (String) request.getAttribute(KEY_QUERY_STRING);
            if (QUERY_STRING.equals(queryString)) {
            	result.setReturnCode(TestResult.PASSED);
            } else {
            	TestUtils.failOnAssertion(KEY_QUERY_STRING,
            			queryString, QUERY_STRING, result);
            }
            return result;
        }
        
        private TestResult checkGetQueryString(HttpServletRequest request) {
        	TestResult result = new TestResult();
        	result.setDescription("Ensure that method request.getQueryString() "
        			+ "returns the correct value.");
        	result.setSpecPLT("16.3.3");
        	
            String queryString = request.getQueryString();
            if (QUERY_STRING.equals(queryString)) {
            	result.setReturnCode(TestResult.PASSED);
            } else {
            	TestUtils.failOnAssertion("request.getQueryString()",
            			queryString, QUERY_STRING, result);
            }
            return result;
        }
        
        private TestResult failOnPathInfo(String pathInfo) {
        	TestResult result = new TestResult();
        	result.setDescription("Ensure that included attribute '"
        			+ KEY_PATH_INFO + "' is available in servlet request.");
        	result.setSpecPLT("16.3.1");
        	
        	String[] expectedPathInfos = new String[] {
        			CHECK_REQUEST_URI, CHECK_CONTEXT_PATH,
        			CHECK_SERVLET_PATH, CHECK_QUERY_STRING, };
        	TestUtils.failOnAssertion(KEY_PATH_INFO,
        	                          new String[] { pathInfo },
        	                          expectedPathInfos,
        	                          result);
        	return result;
        }

    }
    
}


