info.magnolia.cms.filters.ServletDispatchingFilterTest.java Source code

Java tutorial

Introduction

Here is the source code for info.magnolia.cms.filters.ServletDispatchingFilterTest.java

Source

/**
 * This file Copyright (c) 2007-2012 Magnolia International
 * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
 *
 *
 * This file is dual-licensed under both the Magnolia
 * Network Agreement and the GNU General Public License.
 * You may elect to use one or the other of these licenses.
 *
 * This file is distributed in the hope that it will be
 * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
 * Redistribution, except as permitted by whichever of the GPL
 * or MNA you select, is prohibited.
 *
 * 1. For the GPL license (GPL), you can redistribute and/or
 * modify this file under the terms of the GNU General
 * Public License, Version 3, as published by the Free Software
 * Foundation.  You should have received a copy of the GNU
 * General Public License, Version 3 along with this program;
 * if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * 2. For the Magnolia Network Agreement (MNA), this file
 * and the accompanying materials are made available under the
 * terms of the MNA which accompanies this distribution, and
 * is available at http://www.magnolia-cms.com/mna.html
 *
 * Any modifications to this file must keep this entire header
 * intact.
 *
 */
package info.magnolia.cms.filters;

import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.createStrictMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.getCurrentArguments;
import static org.easymock.EasyMock.isA;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.same;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import info.magnolia.cms.core.AggregationState;
import info.magnolia.cms.util.CustomFilterConfig;
import info.magnolia.cms.util.ServletUtils;
import info.magnolia.content2bean.Content2BeanException;
import info.magnolia.context.MgnlContext;
import info.magnolia.context.WebContext;
import info.magnolia.jcr.node2bean.Node2BeanException;
import info.magnolia.jcr.node2bean.impl.Node2BeanProcessorImpl;
import info.magnolia.jcr.node2bean.impl.Node2BeanTransformerImpl;
import info.magnolia.jcr.node2bean.impl.TypeMappingImpl;
import info.magnolia.test.ComponentsTestUtil;
import info.magnolia.test.MgnlTestCase;
import info.magnolia.test.mock.MockAggregationState;
import info.magnolia.test.mock.MockHierarchyManager;
import info.magnolia.test.mock.MockUtil;
import info.magnolia.voting.DefaultVoting;
import info.magnolia.voting.Voting;

import java.io.IOException;
import java.lang.reflect.Field;

import javax.jcr.RepositoryException;
import javax.servlet.FilterChain;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
import org.junit.Before;
import org.junit.Test;

import com.mockrunner.mock.web.MockHttpServletRequest;

/**
 * @version $Id$
 */
public class ServletDispatchingFilterTest extends MgnlTestCase {

    @Override
    @Before
    public void setUp() throws Exception {
        super.setUp();
        ComponentsTestUtil.setImplementation(WebContainerResources.class, WebContainerResourcesImpl.class);
    }

    @Test
    public void testEscapeMetaCharacters() {
        assertEquals("a\\{b\\)c\\(d\\}e\\^f\\]g\\[h\\*i\\$j\\+kl",
                Mapping.escapeMetaCharacters("a{b)c(d}e^f]g[h*i$j+kl"));
    }

    @Test
    public void testSupportsDefaultMapping() throws Exception {
        shouldMatch("/dms/some-doc.pdf", "", "/dms/some-doc.pdf", "/");
        shouldMatch(null, "", "/", "/");
    }

    @Test
    public void testSupportsPathMapping() throws Exception {
        shouldMatch("/hey/ho/lets/go", "/ramones", "/ramones/hey/ho/lets/go", "/ramones/*");
        shouldMatch(null, "/ramones", "/ramones/", "/ramones/*");
        shouldMatch("/wesh", "/yo", "/yo/wesh", "/yo/*");

        // path mapping end with /*, and as such /my/path should not match for /my/path/*
        shouldBypass("/ramones", "/ramones/*");
    }

    @Test
    public void testSupportsExtensionMapping() throws Exception {
        shouldMatch(null, "/dms/some-doc.pdf", "/dms/some-doc.pdf", "*.pdf");
    }

    @Test
    public void testSupportsRegexMappings() throws Exception {
        // the part of the request following the pattern match is the path info:
        shouldMatch("/123/456", "/test", "/test/123/456", "regex:/[a-z]{4}");

        // if pattern ends with a $, extra path info in the request uri does not match:
        shouldMatch(null, "/test", "/test", "regex:^/[a-z]{4}$");
        shouldBypass("/test/blah", "regex:^/[a-z]{4}$");
        shouldBypass("/testx", "regex:^/[a-z]{4}$");

        shouldMatch(null, "/foo/bar473", "/foo/bar473", "regex:^/foo/bar[0-9]{3}[A-Za-z]*");
        shouldMatch(null, "/foo/bar473wEsh", "/foo/bar473wEsh", "regex:^/foo/bar[0-9]{3}[A-Za-z]*");

        shouldMatch("/wEsh", "/foo/bar473", "/foo/bar473/wEsh", "regex:^/foo/bar[0-9]{3}");

        //shouldMatch("/wEsh", "/foo/bar473", "/foo/bar473wEsh", "regex:^/foo/bar[0-9]{3}");
        shouldBypass("/foo/bar473wEsh", "regex:^/foo/bar[0-9]{3}");

        // to avoid the previous case:
        shouldMatch(null, "/foo/bar473", "/foo/bar473", "regex:^/foo/bar[0-9]{3}");
        shouldMatch("/wEsh", "/foo/bar473", "/foo/bar473/wEsh", "regex:^/foo/bar[0-9]{3}");
    }

    @Test
    public void testShouldNotBypassWhenPathMappingMatches() throws Exception {
        shouldMatch("/some-doc.pdf", "/$d}^(m)+{s*", "/$d}^(m)+{s*/some-doc.pdf", "/$d}^(m)+{s*/*");
    }

    @Test
    public void testShouldNotBypassWhenExactMappingMatches() throws Exception {
        shouldMatch(null, "/exactMatch", "/exactMatch", "/exactMatch");
        shouldMatch(null, "/somepath*", "/somepath*", "/somepath*");
    }

    @Test
    public void testShouldBypassWhenMappingDoesNotMatch() throws Exception {
        shouldBypass("/bleh/foo.bar", "/dms/*");
        shouldBypass("/exactMatch/rightPathInfo", "/exactMatch");
        shouldBypass("/nonSpecConformantMapping.html", "/nonSpecConformantMapping*");
        shouldBypass("/nonSpecConformantMapping*/somePage.html", "/nonSpecConformantMapping*");
    }

    @Test
    public void testShouldBypassWhenMappingDoesNotMatchMAGNOLIA1984() throws Exception {
        shouldBypass("/modules/dms/managingdocs.html", "/dms/*");
    }

    @Test
    public void testPathInfoShouldAdhereToServletSpec() throws Exception {
        shouldMatch("/pathInfo.html", "/servlet", "/servlet/pathInfo.html", "/servlet/*");
        // The following doesn't work - spec isn't clear as to whether it should or not
        //shouldMatch("/test", "/some-some.foo", "/some-some.foo/test", "*.foo");
    }

    @Test
    public void testPathInfoShouldStateWhateverIsAfterTheRegexMapping() throws Exception {
        shouldMatch("/test", "/some-doc.pdf", "/some-doc.pdf/test", "regex:.*\\.pdf");
    }

    private void shouldBypass(String requestPath, String mapping) throws Exception {
        doTestBypassAndPathInfo(true, null, null, requestPath, mapping, false);
    }

    private void shouldMatch(final String expectedPathInfo, final String expectedServletPath,
            final String requestPath, String mapping) throws Exception {
        doTestBypassAndPathInfo(false, expectedPathInfo, expectedServletPath, requestPath, mapping, true);
    }

    private void doTestBypassAndPathInfo(final boolean shouldBypass, final String expectedPathInfo,
            final String expectedServletPath, final String requestPath, String mapping,
            boolean shouldCheckPathInfoAndServletPath) throws Exception {
        ComponentsTestUtil.setInstance(Voting.class, new DefaultVoting());
        WebContext ctx = createStrictMock(WebContext.class);
        MgnlContext.setInstance(ctx);
        final AggregationState state = new MockAggregationState();
        expect(ctx.getContextPath()).andReturn("/magnoliaAuthor").anyTimes();

        final FilterChain chain = createNiceMock(FilterChain.class);
        final HttpServletResponse res = createNiceMock(HttpServletResponse.class);
        final HttpServletRequest req = createMock(HttpServletRequest.class);
        expect(req.getAttribute(EasyMock.<String>anyObject())).andReturn(null).anyTimes();
        expect(ctx.getAggregationState()).andReturn(state).anyTimes();
        expect(req.getRequestURI()).andReturn("/magnoliaAuthor" + requestPath).anyTimes();
        expect(req.getContextPath()).andReturn("/magnoliaAuthor").anyTimes();
        expect(req.getServletPath()).andReturn("/magnoliaAuthor" + requestPath).anyTimes();
        expect(req.getPathInfo()).andReturn(null).anyTimes();
        expect(req.getQueryString()).andReturn(null).anyTimes();

        final Servlet servlet = createStrictMock(Servlet.class);
        if (!shouldBypass) {
            servlet.service(isA(HttpServletRequestWrapper.class), same(res));
            expectLastCall().andAnswer(new IAnswer<Object>() {
                @Override
                public Object answer() throws Throwable {
                    final HttpServletRequestWrapper requestWrapper = (HttpServletRequestWrapper) getCurrentArguments()[0];
                    final String pathInfo = requestWrapper.getPathInfo();
                    final String servletPath = requestWrapper.getServletPath();
                    assertEquals("pathInfo does not match", expectedPathInfo, pathInfo);
                    assertEquals("servletPath does not match", expectedServletPath, servletPath);
                    return null;
                }
            });
        }

        replay(chain, res, req, servlet, ctx);

        state.setCurrentURI(requestPath);
        final AbstractMgnlFilter filter = new ServletDispatchingFilter();
        final Field servletField = ServletDispatchingFilter.class.getDeclaredField("servlet");
        servletField.setAccessible(true);
        servletField.set(filter, servlet);

        filter.addMapping(mapping);

        assertEquals("Should " + (shouldBypass ? "" : "not ") + "have bypassed", shouldBypass,
                !filter.matches(req));
        if (!shouldBypass) {
            filter.doFilter(req, res, chain);
        }

        verify(chain, res, req, servlet, ctx);
    }

    @Test
    public void testWrapperRespectsForwards()
            throws RepositoryException, IOException, Content2BeanException, ServletException, Node2BeanException {

        String testContent = "" + "/server/filters/servlets/test.class=" + ServletDispatchingFilter.class.getName()
                + "\n" + "/server/filters/servlets/test.servletClass=" + TestServlet.class.getName() + "\n"
                + "/server/filters/servlets/test.servletName=TestServlet\n"
                + "/server/filters/servlets/test.comment=TestComment\n"
                + "/server/filters/servlets/test.parameters.param1=value1\n"
                + "/server/filters/servlets/test.parameters.param2=value2\n"
                + "/server/filters/servlets/test.mappings.mapping.pattern=/mapping/*";

        MockHierarchyManager hm = MockUtil.createHierarchyManager(testContent);

        Node2BeanProcessorImpl n2b = new Node2BeanProcessorImpl(new TypeMappingImpl(),
                new Node2BeanTransformerImpl());

        ServletDispatchingFilter filter = (ServletDispatchingFilter) n2b
                .toBean(hm.getContent("/server/filters/servlets/test").getJCRNode());

        assertEquals("Wrapper for TestServlet servlet", filter.getName());
        assertEquals("TestComment", filter.getComment());
        assertEquals(2, filter.getParameters().size());
        assertEquals("value1", filter.getParameters().get("param1"));
        assertEquals("value2", filter.getParameters().get("param2"));
        assertEquals(1, filter.getMappings().size());
        filter.init(new CustomFilterConfig("test", null));

        MockHttpServletRequest mock = new MockHttpServletRequest();
        mock.setRequestURI("/magnoliaAuthor/mapping/test.html");
        mock.setContextPath("/magnoliaAuthor");
        mock.setServletPath("");
        mock.setPathInfo("/mapping/test.html");

        MgnlContext.getWebContext().getAggregationState()
                .setCurrentURI(StringUtils.substringAfter(mock.getRequestURI(), mock.getContextPath()));
        filter.doFilter(mock, null, null);

        filter.destroy();
    }

    public static class TestServlet extends HttpServlet {

        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            assertEquals("/magnoliaAuthor/mapping/test.html", req.getRequestURI());
            assertEquals("/magnoliaAuthor", req.getContextPath());
            assertEquals("/mapping", req.getServletPath());
            assertEquals("/test.html", req.getPathInfo());
            assertEquals(null, req.getQueryString());

            // simulate a forward

            MockHttpServletRequest mock = ServletUtils.getWrappedRequest(req, MockHttpServletRequest.class);
            mock.setRequestURI("/magnoliaAuthor/.magnolia/somepage.html");
            mock.setContextPath("/magnoliaAuthor");
            mock.setServletPath("");
            mock.setPathInfo("/.magnolia/somepage.html");

            // test that the ServletDispatcherFitler wrapper lets the new values through
            assertEquals("/magnoliaAuthor/.magnolia/somepage.html", req.getRequestURI());
            assertEquals("/magnoliaAuthor", req.getContextPath());
            assertEquals("", req.getServletPath());
            assertEquals("/.magnolia/somepage.html", req.getPathInfo());
            assertEquals(null, req.getQueryString());
        }
    }
}