net.sf.ehcache.constructs.web.filter.CachingFilterTest.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.ehcache.constructs.web.filter.CachingFilterTest.java

Source

/**
 *  Copyright 2003-2007 Luck Consulting Pty Ltd
 *
 *  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 net.sf.ehcache.constructs.web.filter;

import com.meterware.httpunit.HttpInternalErrorException;
import com.meterware.httpunit.WebResponse;
import junit.framework.AssertionFailedError;
import net.sf.ehcache.constructs.web.AbstractWebTest;
import net.sf.ehcache.constructs.web.PageInfo;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.HeadMethod;
import org.xml.sax.SAXException;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.List;

/**
 * @author <a href="mailto:gluck@thoughtworks.com">Greg Luck</a>
 * @version $Id: CachingFilterTest.java 604 2008-04-25 02:20:57Z gregluck $
 */
public class CachingFilterTest extends AbstractWebTest {

    private String cachedPageUrl = "/CachedPage.jsp";

    private void resetCachedPageUrl() {
        cachedPageUrl = "/CachedPage.jsp";
    }

    /**
     * Tests that a page which should be cached is cached.
     * Also, check that the pages returned are good.
     */
    public void testCachedPageIsCached() throws Exception {
        assertResponseGoodAndCached(cachedPageUrl, true);
    }

    /**
     * HEAD methods return an empty response body. If a HEAD request populates
     * a cache and then a GET follorws, a blank page will result.
     * This test ensures that the SimplePageCachingFilter implements calculateKey
     * properly to avoid this problem.
     */
    public void testHeadThenGetOnCachedPage() throws Exception {
        HttpClient httpClient = new HttpClient();
        HttpMethod httpMethod = new HeadMethod(buildUrl(cachedPageUrl));
        int responseCode = httpClient.executeMethod(httpMethod);
        //httpclient follows redirects, so gets the home page.
        assertEquals(HttpURLConnection.HTTP_OK, responseCode);
        String responseBody = httpMethod.getResponseBodyAsString();
        assertNull(responseBody);
        assertNull(httpMethod.getResponseHeader("Content-Encoding"));

        httpMethod = new GetMethod(buildUrl(cachedPageUrl));
        responseCode = httpClient.executeMethod(httpMethod);
        responseBody = httpMethod.getResponseBodyAsString();
        assertNotNull(responseBody);

    }

    /**
     * Check that cyrillic (unicode) characters are handled on the first hit and subsequent hits
     */
    public void testCachedPageMultilingual() throws Exception {
        WebResponse response = getResponseFromAcceptGzipRequest(cachedPageUrl);
        //Check that we are dealing with Cyrillic characters ok
        assertTrue(response.getText().indexOf("&#8593;") != -1);
        //Check non ascii symbol
        assertTrue(response.getText().indexOf("&#1052;") != -1);

        //cache hit
        response = getResponseFromAcceptGzipRequest(cachedPageUrl);
        //Check that we are dealing with Cyrillic characters ok
        assertTrue(response.getText().indexOf("&#8593;") != -1);
        //Check non ascii symbol
        assertTrue(response.getText().indexOf("&#1052;") != -1);

    }

    /**
     * Tests whether the page is gzipped using the rawer HttpClient library.
     * Lets us check that the responseBody is really gzipped.
     */
    public void testCachedPageIsGzippedWhenEncodingHeaderSet() throws IOException {
        HttpClient httpClient = new HttpClient();
        HttpMethod httpMethod = new GetMethod(buildUrl(cachedPageUrl));
        httpMethod.setRequestHeader(new Header("Accept-encoding", "gzip"));
        httpClient.executeMethod(httpMethod);
        byte[] responseBody = httpMethod.getResponseBody();
        assertTrue(PageInfo.isGzipped(responseBody));
    }

    /**
     * Tests whether the page is gzipped using the rawer HttpClient library.
     * Lets us check that the responseBody is really not gzipped.
     */
    public void testCachedPageIsNotGzippedWhenEncodingHeaderNotSet() throws IOException {
        HttpClient httpClient = new HttpClient();
        HttpMethod httpMethod = new GetMethod(buildUrl(cachedPageUrl));
        //httpMethod.setRequestHeader(new Header("Accept-encoding", "gzip"));
        httpClient.executeMethod(httpMethod);
        byte[] responseBody = httpMethod.getResponseBody();
        assertFalse(PageInfo.isGzipped(responseBody));
    }

    /**
     * Checks sequence
     *
     * @throws IOException
     */
    public void testSequenceNonGzipThenGzipThenNonGzip() throws IOException {
        testCachedPageIsNotGzippedWhenEncodingHeaderNotSet();
        testCachedPageIsGzippedWhenEncodingHeaderSet();
        testCachedPageIsNotGzippedWhenEncodingHeaderNotSet();
    }

    /**
     * Checks sequence
     *
     * @throws IOException
     */
    public void testSequenceGzipThenNonGzipThenGzip() throws IOException {
        testCachedPageIsGzippedWhenEncodingHeaderSet();
        testCachedPageIsNotGzippedWhenEncodingHeaderNotSet();
        testCachedPageIsGzippedWhenEncodingHeaderSet();
    }

    /**
     * The reentry check instruments the thread name to indicate it has entered a CachingFilter.
     * The name is reset at the end. Check that this works for concurrent situations.
     */
    public void testCachedPageConcurrent() throws Exception {

        final List executables = new ArrayList();
        for (int i = 0; i < 40; i++) {
            final AbstractWebTest.Executable executable = new AbstractWebTest.Executable() {
                public void execute() throws Exception {
                    testCachedPageIsCached();
                }
            };
            executables.add(executable);
        }
        runThreads(executables);
    }

    /**
     * Tests that a cached full page can be included in another page which calls it with
     * a jsp:include
     * <p/>
     * Fails when client request gzip
     * wget -S  --header='Accept-encoding: gzip' http://localhost:9080/legaldispatchtocachedpage/Include.jsp
     * --15:01:49--  http://localhost:9080/legaldispatchtocachedpage/Include.jsp
     * => `Include.jsp.9'
     * Resolving localhost... done.
     * Connecting to localhost[127.0.0.1]:9080... connected.
     * HTTP request sent, awaiting response...
     * 1 HTTP/1.1 200 OK
     * 2 Date: Mon, 22 Nov 2004 05:01:48 GMT
     * 3 Server: Orion/2.0.3
     * 4 Set-Cookie: JSESSIONID=EFEIDKLICCGB; Path=/
     * 5 Cache-Control: private
     * 6 Connection: Close
     * 7 Content-Type: text/html
     * <p/>
     * [ <=>                                                                                                                                             ] 734          716.80K/s
     * <p/>
     * 15:01:49 (716.80 KB/s) - `Include.jsp.9' saved [734]
     *
     * @throws Exception
     */
    public void testFromJSPInclude() throws Exception {
        try {
            WebResponse response = getResponseFromAcceptGzipRequest("/legaldispatchtocachedpage/Include.jsp");
            assertPropertlyFormed(response);
            fail();
        } catch (AssertionFailedError e) {
            //noop Page is actually an error message in Orion
        } catch (HttpInternalErrorException e) {
            //noop 500 error in Tomcat. The log shows ResponseHeadersNotModifiableException when we try to set Gzip
            //because it is already gzipped.
        }

        //No point doing the rest of the tests because the page is always blank
    }

    /**
     * Tests that a cached full page can be included in another page which calls it with
     * a jsp:include, where the client does not accept gzip encoding.
     *
     * @throws Exception
     */
    public void testFromJSPIncludeNoGzip() throws Exception {
        try {
            WebResponse response = getResponseFromNonAcceptGzipRequest("/legaldispatchtocachedpage/Include.jsp");
            assertPropertlyFormed(response);
            fail();
        } catch (AssertionFailedError e) {
            //noop Page is actually an error message
        } catch (HttpInternalErrorException e) {
            //noop 500 error in Tomcat. The log shows ResponseHeadersNotModifiableException when we try to set Gzip
            //because it is already gzipped.
        }

        //No point doing the rest of the tests because the page is always blank
    }

    /**
     * Tests that a cached full page included using the RequestDispatcher in a servlet
     * throws a Server 500 error. In the server log a ResponseHeadersNotModifiableException
     * with the message "Failure when attempting to set Content-Encoding: gzip" will be thrown.
     * <p/>
     * If we did not throw an error the client would get a strange result:
     * <pre>
     * wget -S  --header='Accept-encoding: gzip' http://localhost:9080/servletdispatchtocachedpage/IncludeCachedPageServlet
     * --20:43:29--  http://localhost:9080/servletdispatchtocachedpage/IncludeCachedPageServlet
     * => `IncludeCachedPageServlet.2'
     * Resolving localhost... done.
     * Connecting to localhost[127.0.0.1]:9080... connected.
     * HTTP request sent, awaiting response...
     * 1 HTTP/1.1 200 OK
     * 2 Date: Mon, 22 Nov 2004 10:43:28 GMT
     * 3 Server: Orion/2.0.3
     * 4 Connection: Close
     * 5 Content-Type: text/plain
     * </pre>
     * Though gzip, the header is not set. The content-type is text/plain, because in our case the
     * test servlet did not set a type.
     *
     * @see {@link net.sf.ehcache.constructs.web.ResponseHeadersNotModifiableException}
     */
    public void testFromServletInclude() {
        String url = "/servletdispatchtocachedpage/IncludeCachedPageServlet";
        try {
            getResponseFromAcceptGzipRequest(url);
            fail();
        } catch (HttpInternalErrorException e) {
            //this is what should happen.
        } catch (IOException e) {
            fail();
        } catch (SAXException e) {
            fail();
        }

    }

    /**
     * Tests that a cached full page can be forwarded to using the RequestDispatcher in a servlet.
     *
     * @throws Exception
     */
    public void testFromServletForward() throws Exception {
        String url = "/servletdispatchtocachedpage/ForwardToCachedPageServlet";
        WebResponse response = getResponseFromAcceptGzipRequest(url);
        assertResponseOk(response);
        assertHeadersSane(response);
        assertPageNotBlank(response);
        assertPropertlyFormed(response);

        //runStandardTestsOnUrl(url);
    }

    /**
     * This sequence returns a valid page with 0 bytes!
     * This will be seen as an empty page in the browser.
     * wget response looks like:
     * wget -S  --header='Accept-encoding: gzip' http://localhost:9080//blankpageproblem/Include.jsp
     * --10:13:00--  http://localhost:9080/legaldispatchtocachedpage/Include.jsp
     * => `Include.jsp.2'
     * Resolving localhost... done.
     * Connecting to localhost[127.0.0.1]:9080... connected.
     * HTTP request sent, awaiting response...
     * 1 HTTP/1.1 200 OK
     * 2 Date: Mon, 22 Nov 2004 00:13:00 GMT
     * 3 Server: Orion/2.0.3
     * 4 Set-Cookie: JSESSIONID=JAPIBBCHOFKO; Path=/
     * 5 Cache-Control: private
     * 6 Connection: Close
     * 7 Content-Type: text/html
     * <p/>
     * [ <=>                                                                                                                                             ] 0             --.--K/s
     * <p/>
     * 10:13:03 (0.00 B/s) - `Include.jsp.2' saved [0]
     * <p/>
     * The problem is a filter inplementation which does not write the response wrapper content
     * when it detects a response already committed so nothing is writen by the filter.
     *
     * @throws Exception
     */
    public void testBlankPageProblemFilterFromJSPInclude() throws Exception {
        WebResponse response = getResponseFromAcceptGzipRequest("/blankpageproblem/Include.jsp");
        assertResponseOk(response);
        assertHeadersSane(response);
        try {
            assertPageNotBlank(response);
            fail();
        } catch (AssertionFailedError e) {
            //noop Page is blank. Should fail.
        }

        //No point doing the rest of the tests because the page is always blank
    }

    /**
     * Tests including a page cached with SimplePageCachingFilter, after setting Filter.noFilter(true)
     * This works. It does not produce a blank page.
     * wget response looks like:
     * wget -S  --header='Accept-encoding: gzip' http://localhost:9080/legaldispatchtocachedpage/IncludeWithNoFilter.jsp
     * --13:19:12--  http://localhost:9080/legaldispatchtocachedpage/IncludeWithNoFilter.jsp
     * => `IncludeWithNoFilter.jsp'
     * Resolving localhost... done.
     * Connecting to localhost[127.0.0.1]:9080... connected.
     * HTTP request sent, awaiting response...
     * 1 HTTP/1.1 200 OK
     * 2 Date: Mon, 22 Nov 2004 03:19:12 GMT
     * 3 Server: Orion/2.0.3
     * 4 Set-Cookie: JSESSIONID=LCIFMEDBEJKP; Path=/
     * 5 Cache-Control: private
     * 6 Connection: Close
     * 7 Content-Type: text/html
     * <p/>
     * [ <=>                                                                                                                                             ] 1,846          1.76M/s
     * <p/>
     * 13:19:12 (1.76 MB/s) - `IncludeWithNoFilter.jsp' saved [1846]
     * The problem is that the response is already committed so nothing is writen by the filter.
     *
     * @throws Exception
     */
    public void testFromJSPIncludeWithNoFilter() throws Exception {
        WebResponse response = getResponseFromAcceptGzipRequest(
                "/legaldispatchtocachedpage/IncludeWithNoFilter.jsp");
        assertResponseOk(response);
        assertHeadersSane(response);
        assertPageNotBlank(response);
        //Delay because of Windows timer inaccuracy
        Thread.sleep(20);
        assertResponseGoodAndNotCached("/legaldispatchtocachedpage/IncludeWithNoFilter.jsp", true);

        //No point doing the rest of the tests because the page is not cached
    }

    /**
     * This test calls a page which forwards to the cached page using jsp:forward.
     * <p/>
     * This works fine.
     * <p/>
     * wget response looks like:
     * wget -S  --header='Accept-encoding: gzip' http://localhost:9080/legaldispatchtocachedpage/Forward.jsp
     * --10:17:43--  http://localhost:9080/legaldispatchtocachedpage/Forward.jsp
     * => `Forward.jsp'
     * Resolving localhost... done.
     * Connecting to localhost[127.0.0.1]:9080... connected.
     * HTTP request sent, awaiting response...
     * 1 HTTP/1.1 200 OK
     * 2 Date: Mon, 22 Nov 2004 00:17:56 GMT
     * 3 Server: Orion/2.0.3
     * 4 Content-Location: http://localhost:9080/CachedPage.jsp
     * 5 Content-Length: 735
     * 6 Set-Cookie: JSESSIONID=ICDJCBCHOFKO; Path=/
     * 7 Cache-Control: private
     * 8 Connection: Close
     * 9 Content-Type: text/html
     * 10 Content-Encoding: gzip
     * <p/>
     * 10:17:56 (717.77 KB/s) - `Forward.jsp' saved [735/735]
     *
     * @throws Exception
     */
    public void testFromJSPForward() throws Exception {
        WebResponse response = getResponseFromAcceptGzipRequest("/legaldispatchtocachedpage/Forward.jsp");
        assertResponseGood(response, true);
        runStandardTestsOnUrl("/legaldispatchtocachedpage/Forward.jsp");
    }

    /**
     * This test calls a page which forwards to the cached page using jsp:forward.
     * <p/>
     * This works fine.
     * <p/>
     * wget response looks like:
     * wget -S  --header='Accept-encoding: gzip' http://localhost:9080/legaldispatchtocachedpage/Forward.jsp
     * --10:17:43--  http://localhost:9080/legaldispatchtocachedpage/Forward.jsp
     * => `Forward.jsp'
     * Resolving localhost... done.
     * Connecting to localhost[127.0.0.1]:9080... connected.
     * HTTP request sent, awaiting response...
     * 1 HTTP/1.1 200 OK
     * 2 Date: Mon, 22 Nov 2004 00:17:56 GMT
     * 3 Server: Orion/2.0.3
     * 4 Content-Location: http://localhost:9080/CachedPage.jsp
     * 5 Content-Length: 735
     * 6 Set-Cookie: JSESSIONID=ICDJCBCHOFKO; Path=/
     * 7 Cache-Control: private
     * 8 Connection: Close
     * 9 Content-Type: text/html
     * 10 Content-Encoding: gzip
     * <p/>
     * 10:17:56 (717.77 KB/s) - `Forward.jsp' saved [735/735]
     *
     * @throws Exception
     */
    public void testCallCachedPageFromRedirect() throws Exception {
        WebResponse response = getResponseFromAcceptGzipRequest("/legaldispatchtocachedpage/Redirect.jsp");
        assertResponseGood(response, true);

        runStandardTestsOnUrl("/legaldispatchtocachedpage/Redirect.jsp");
    }

    /**
     * @throws Exception Always reset the URL so we don't get dependencies
     */
    protected void tearDown() throws Exception {
        resetCachedPageUrl();
    }

    /**
     * Some standard tests that should work.
     *
     * @param url
     * @throws Exception
     */
    private void runStandardTestsOnUrl(String url) throws Exception {
        cachedPageUrl = url;
        testCachedPageIsCached();

        cachedPageUrl = url;
        testCachedPageIsGzippedWhenEncodingHeaderSet();

        cachedPageUrl = url;
        testCachedPageIsNotGzippedWhenEncodingHeaderNotSet();

        cachedPageUrl = url;
        testSequenceGzipThenNonGzipThenGzip();

        cachedPageUrl = url;
        testSequenceNonGzipThenGzipThenNonGzip();

        cachedPageUrl = url;
        testCachedPageConcurrent();
    }

    /**
     * A 0 length body should give a 0 length nongzipped body and content length
     * Manual Test: wget -d --server-response --timestamping --header='If-modified-Since: Fri, 13 May 3006 23:54:18 GMT' --header='Accept-Encoding: gzip' http://localhost:9080/empty_caching_filter/empty.html
     */
    public void testIfModifiedZeroLengthHTML() throws Exception {

        String url = "http://localhost:9080/empty_caching_filter/empty.html";
        HttpClient httpClient = new HttpClient();
        HttpMethod httpMethod = new GetMethod(url);
        httpMethod.addRequestHeader("If-modified-Since", "Fri, 13 May 3006 23:54:18 GMT");
        httpMethod.addRequestHeader("Accept-Encoding", "gzip");
        int responseCode = httpClient.executeMethod(httpMethod);
        assertTrue(
                HttpURLConnection.HTTP_OK == responseCode || HttpURLConnection.HTTP_NOT_MODIFIED == responseCode);
        String responseBody = httpMethod.getResponseBodyAsString();
        assertTrue("".equals(responseBody) || null == responseBody);
        checkNullOrZeroContentLength(httpMethod);
    }

    /**
     * Servlets and JSPs can send content even when the response is set to no content.
     * In this case there should not be a body but there is. Orion seems to kill the body
     * after is has left the Servlet filter chain. To avoid wget going into an inifinite
     * retry loop, and presumably some other web clients, the content length should be 0
     * and the body 0.
     * <p/>
     * wget -d --server-response --timestamping --header='If-modified-Since: Fri, 13 May 3006 23:54:18 GMT' --header='Accept-Encoding: gzip' http://localhost:9080/empty_caching_filter/SC_NOT_MODIFIED.jsp
     */
    public void testNotModifiedJSPGzipFilter() throws Exception {

        String url = "http://localhost:9080/empty_caching_filter/SC_NOT_MODIFIED.jsp";
        HttpClient httpClient = new HttpClient();
        HttpMethod httpMethod = new GetMethod(url);
        httpMethod.addRequestHeader("If-modified-Since", "Fri, 13 May 3006 23:54:18 GMT");
        httpMethod.addRequestHeader("Accept-Encoding", "gzip");
        int responseCode = httpClient.executeMethod(httpMethod);
        assertEquals(HttpURLConnection.HTTP_NOT_MODIFIED, responseCode);
        String responseBody = httpMethod.getResponseBodyAsString();
        assertEquals(null, responseBody);
        assertNull(httpMethod.getResponseHeader("Content-Encoding"));
        assertNotNull(httpMethod.getResponseHeader("Last-Modified").getValue());
        checkNullOrZeroContentLength(httpMethod);

    }

    /**
     * Servlets and JSPs can send content even when the response is set to no content.
     * In this case there should not be a body but there is. Orion seems to kill the body
     * after is has left the Servlet filter chain. To avoid wget going into an inifinite
     * retry loop, and presumably some other web clients, the content length should be 0
     * and the body 0.
     * <p/>
     * Manual Test: wget -d --server-response --timestamping --header='If-modified-Since: Fri, 13 May 3006 23:54:18 GMT' --header='Accept-Encoding: gzip' http://localhost:9080/empty_caching_filter/SC_NO_CONTENT.jsp
     */
    public void testNoContentJSPGzipFilter() throws Exception {

        String url = "http://localhost:9080/empty_caching_filter/SC_NO_CONTENT.jsp";
        HttpClient httpClient = new HttpClient();
        HttpMethod httpMethod = new GetMethod(url);
        httpMethod.addRequestHeader("If-modified-Since", "Fri, 13 May 3006 23:54:18 GMT");
        httpMethod.addRequestHeader("Accept-Encoding", "gzip");
        int responseCode = httpClient.executeMethod(httpMethod);
        assertEquals(HttpURLConnection.HTTP_NO_CONTENT, responseCode);
        String responseBody = httpMethod.getResponseBodyAsString();
        assertEquals(null, responseBody);
        assertNull(httpMethod.getResponseHeader("Content-Encoding"));
        assertNotNull(httpMethod.getResponseHeader("Last-Modified").getValue());
        checkNullOrZeroContentLength(httpMethod);

    }

    /**
     * When the servlet container generates a 404 page not found, we want to pass
     * it through without caching and without adding anything to it.
     * <p/>
     * Manual Test: wget -d --server-response --header='Accept-Encoding: gzip'  http://localhost:9080/non_ok/PageNotFound.jsp
     */
    public void testNotFound() throws Exception {

        String url = "http://localhost:9080/non_ok/PageNotFound.jsp";
        HttpClient httpClient = new HttpClient();
        HttpMethod httpMethod = new GetMethod(url);
        httpMethod.addRequestHeader("If-modified-Since", "Fri, 13 May 3006 23:54:18 GMT");
        httpMethod.addRequestHeader("Accept-Encoding", "gzip");
        int responseCode = httpClient.executeMethod(httpMethod);
        assertEquals(HttpURLConnection.HTTP_NOT_FOUND, responseCode);
        String responseBody = httpMethod.getResponseBodyAsString();
        assertNotNull(responseBody);
        assertNull(httpMethod.getResponseHeader("Content-Encoding"));
    }

    /**
     * When the servlet container generates a 404 page not found, we want to pass
     * it through without caching and without adding anything to it.
     * <p/>
     * Manual Test: wget -d --server-response --header='Accept-Encoding: gzip'  http://localhost:9080/non_ok/SendRedirect.jsp
     */
    public void testRedirect() throws Exception {

        String url = "http://localhost:9080/non_ok/SendRedirect.jsp";
        HttpClient httpClient = new HttpClient();
        HttpMethod httpMethod = new GetMethod(url);
        httpMethod.addRequestHeader("Accept-Encoding", "gzip");
        int responseCode = httpClient.executeMethod(httpMethod);
        //httpclient follows redirects, so gets the home page.
        assertEquals(HttpURLConnection.HTTP_OK, responseCode);
        String responseBody = httpMethod.getResponseBodyAsString();
        assertNotNull(responseBody);
        assertNull(httpMethod.getResponseHeader("Content-Encoding"));
    }

}