/* * JBoss, Home of Professional Open Source * Copyright 2013, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.richfaces.resource; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Date; import java.util.Locale; import java.util.Map; import java.util.TimeZone; import javax.faces.context.FacesContext; import org.easymock.IAnswer; import org.easymock.classextension.EasyMock; import org.jboss.test.faces.AbstractFacesTest; import org.jboss.test.faces.mock.FacesMock; /** * @author Nick Belaevski * @since 4.0 */ @SuppressWarnings("deprecation") public class AbstractCacheableResourceTest extends AbstractFacesTest { private static final int MILLISECONDS_IN_HOUR = 60 * 60 * 1000; /* HTTP Date format required by the HTTP/1.1 RFC */ private static final String RFC1123_DATE_PATTERN = "EEE, dd MMM yyyy HH:mm:ss zzz"; private static final SimpleDateFormat RFC1123_DATE_FORMATTER; static { SimpleDateFormat format = new SimpleDateFormat(RFC1123_DATE_PATTERN, Locale.US); format.setTimeZone(TimeZone.getTimeZone("GMT")); RFC1123_DATE_FORMATTER = format; } @Override public void setUp() throws Exception { super.setUp(); setupFacesRequest(); } /* * (non-Javadoc) * * @see org.richfaces.test.AbstractFacesTest#tearDown() */ @Override public void tearDown() throws Exception { super.tearDown(); } public void testInitialRequest() throws Exception { MockAbstractResource defaultResource = new MockAbstractResource(); assertNull(defaultResource.isMatchesEntityTag(facesContext)); assertNull(defaultResource.isMatchesLastModified(facesContext)); MockAbstractResource entityTaggedResource = new MockAbstractResource(); entityTaggedResource.setEntityTag("3456"); assertNull(entityTaggedResource.isMatchesEntityTag(facesContext)); assertNull(entityTaggedResource.isMatchesLastModified(facesContext)); MockAbstractResource timeKnownResource = new MockAbstractResource(); timeKnownResource.setLastModified(new Date(System.currentTimeMillis() - MILLISECONDS_IN_HOUR)); assertNull(timeKnownResource.isMatchesEntityTag(facesContext)); assertNull(timeKnownResource.isMatchesLastModified(facesContext)); } public void testEntityTag() throws Exception { this.connection.addRequestHeaders(Collections.singletonMap("If-None-Match", "\"3456\"")); MockAbstractResource defaultResource = new MockAbstractResource(); assertFalse(defaultResource.isMatchesEntityTag(facesContext)); MockAbstractResource entityTaggedResource = new MockAbstractResource(); entityTaggedResource.setEntityTag("\"3456\""); assertTrue(entityTaggedResource.isMatchesEntityTag(facesContext)); MockAbstractResource unmatchedTaggedResource = new MockAbstractResource(); unmatchedTaggedResource.setEntityTag("\"123\""); assertFalse(unmatchedTaggedResource.isMatchesEntityTag(facesContext)); } public void testWeakEntityTag() throws Exception { this.connection.addRequestHeaders(Collections.singletonMap("If-None-Match", "W/\"3456\"")); MockAbstractResource defaultResource = new MockAbstractResource(); assertEquals(Boolean.FALSE, defaultResource.isMatchesEntityTag(facesContext)); MockAbstractResource entityTaggedResource = new MockAbstractResource(); entityTaggedResource.setEntityTag("\"3456\""); assertEquals(Boolean.TRUE, entityTaggedResource.isMatchesEntityTag(facesContext)); MockAbstractResource unmatchedTaggedResource = new MockAbstractResource(); unmatchedTaggedResource.setEntityTag("\"123\""); assertEquals(Boolean.FALSE, unmatchedTaggedResource.isMatchesEntityTag(facesContext)); } public void testMultiEntityTags() throws Exception { this.connection.addRequestHeaders(Collections.singletonMap("If-None-Match", "W/\"3456\", \"012\"")); MockAbstractResource defaultResource = new MockAbstractResource(); assertEquals(Boolean.FALSE, defaultResource.isMatchesEntityTag(facesContext)); MockAbstractResource entityTaggedResource = new MockAbstractResource(); entityTaggedResource.setEntityTag("\"3456\""); assertEquals(Boolean.TRUE, entityTaggedResource.isMatchesEntityTag(facesContext)); MockAbstractResource anotherTaggedResource = new MockAbstractResource(); anotherTaggedResource.setEntityTag("W/\"012\""); assertEquals(Boolean.TRUE, anotherTaggedResource.isMatchesEntityTag(facesContext)); MockAbstractResource unmatchedTaggedResource = new MockAbstractResource(); unmatchedTaggedResource.setEntityTag("\"123\""); assertEquals(Boolean.FALSE, unmatchedTaggedResource.isMatchesEntityTag(facesContext)); } public void testIfModified() throws Exception { Date baseDate = new Date(); Date beforeBaseDate = new Date(baseDate.getTime() - MILLISECONDS_IN_HOUR); Date afterBaseDate = new Date(baseDate.getTime() + MILLISECONDS_IN_HOUR); this.connection .addRequestHeaders(Collections.singletonMap("If-Modified-Since", RFC1123_DATE_FORMATTER.format(baseDate))); MockAbstractResource defaultResource = new MockAbstractResource(); assertEquals(Boolean.FALSE, defaultResource.isMatchesLastModified(facesContext)); MockAbstractResource actualResource = new MockAbstractResource(); actualResource.setLastModified(beforeBaseDate); assertEquals(Boolean.TRUE, actualResource.isMatchesLastModified(facesContext)); MockAbstractResource expiredResource = new MockAbstractResource(); expiredResource.setLastModified(afterBaseDate); assertEquals(Boolean.FALSE, expiredResource.isMatchesLastModified(facesContext)); } public void testUserAgentNeedsUpdate() throws Exception { BooleanAnswer tagMatches = new BooleanAnswer(); BooleanAnswer lastModifiedMatches = new BooleanAnswer(); BooleanAnswer cacheable = new BooleanAnswer(); AbstractCacheableResource resource = FacesMock.createControl().createMock(AbstractTestResource.class, AbstractTestResource.class.getDeclaredMethods()); EasyMock.expect(resource.isCacheable(facesContext)).andStubAnswer(cacheable); String matchHeaderValue = RFC1123_DATE_FORMATTER.format(new Date()); this.connection.addRequestHeaders(Collections.singletonMap("If-None-Match", matchHeaderValue)); EasyMock.expect(resource.isMatchesEntityTag(facesContext, matchHeaderValue)).andStubAnswer(tagMatches); String modifiedCondition = "\"1234\""; this.connection.addRequestHeaders(Collections.singletonMap("If-Modified-Since", modifiedCondition)); EasyMock.expect(resource.isMatchesLastModified(facesContext, modifiedCondition)).andStubAnswer(lastModifiedMatches); EasyMock.replay(resource); cacheable.setValue(false); assertTrue(resource.userAgentNeedsUpdate(facesContext)); cacheable.setValue(true); tagMatches.setValue(Boolean.TRUE); lastModifiedMatches.setValue(Boolean.TRUE); assertFalse(resource.userAgentNeedsUpdate(facesContext)); tagMatches.setValue(Boolean.FALSE); lastModifiedMatches.setValue(Boolean.FALSE); assertTrue(resource.userAgentNeedsUpdate(facesContext)); tagMatches.setValue(Boolean.FALSE); lastModifiedMatches.setValue(Boolean.TRUE); assertTrue(resource.userAgentNeedsUpdate(facesContext)); tagMatches.setValue(Boolean.TRUE); lastModifiedMatches.setValue(Boolean.FALSE); assertTrue(resource.userAgentNeedsUpdate(facesContext)); this.connection.addRequestHeaders(Collections.singletonMap("If-None-Match", (String) null)); lastModifiedMatches.setValue(Boolean.FALSE); assertTrue(resource.userAgentNeedsUpdate(facesContext)); lastModifiedMatches.setValue(Boolean.TRUE); assertFalse(resource.userAgentNeedsUpdate(facesContext)); this.connection.addRequestHeaders(Collections.singletonMap("If-None-Match", matchHeaderValue)); this.connection.addRequestHeaders(Collections.singletonMap("If-Modified-Since", (String) null)); tagMatches.setValue(Boolean.FALSE); assertTrue(resource.userAgentNeedsUpdate(facesContext)); tagMatches.setValue(Boolean.TRUE); assertFalse(resource.userAgentNeedsUpdate(facesContext)); this.connection.addRequestHeaders(Collections.singletonMap("If-Modified-Since", (String) null)); this.connection.addRequestHeaders(Collections.singletonMap("If-None-Match", (String) null)); assertTrue(resource.userAgentNeedsUpdate(facesContext)); cacheable.setValue(false); assertTrue(resource.userAgentNeedsUpdate(facesContext)); } public abstract static class AbstractTestResource extends AbstractCacheableResource { @Override public String toString() { return "mock"; } @Override public boolean isCacheable(FacesContext context) { return false; } @Override protected boolean isMatchesEntityTag(FacesContext context, String matchHeaderValue) { return super.isMatchesEntityTag(context, matchHeaderValue); } @Override protected boolean isMatchesLastModified(FacesContext context, String modifiedCondition) { return super.isMatchesLastModified(context, modifiedCondition); } } private static class BooleanAnswer implements IAnswer<Boolean> { private Boolean value; public Boolean answer() throws Throwable { return value; } public void setValue(Boolean value) { this.value = value; } } } class MockAbstractResource extends AbstractCacheableResource { private String entityTag; private Date lastModified; public void setEntityTag(String entityTag) { this.entityTag = entityTag; } public void setLastModified(Date lastModified) { this.lastModified = lastModified; } @Override public String getEntityTag(FacesContext context) { return entityTag; } @Override protected Date getLastModified(FacesContext context) { return lastModified; } @Override public InputStream getInputStream() throws IOException { throw new UnsupportedOperationException(); } @Override public String getRequestPath() { throw new UnsupportedOperationException(); } @Override public Map<String, String> getResponseHeaders() { throw new UnsupportedOperationException(); } @Override public URL getURL() { throw new UnsupportedOperationException(); } @Override public boolean isCacheable(FacesContext context) { throw new UnsupportedOperationException(); } }