001 // Copyright 2004, 2005 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 package org.apache.tapestry.asset; 015 016 import org.apache.commons.logging.LogFactory; 017 import org.apache.hivemind.ClassResolver; 018 import org.apache.hivemind.ApplicationRuntimeException; 019 import org.apache.hivemind.impl.DefaultClassResolver; 020 import org.apache.tapestry.IRequestCycle; 021 import org.apache.tapestry.TestBase; 022 import org.apache.tapestry.error.RequestExceptionReporter; 023 import org.apache.tapestry.util.ContentType; 024 import org.apache.tapestry.web.WebContext; 025 import org.apache.tapestry.web.WebRequest; 026 import org.apache.tapestry.web.WebResponse; 027 import org.easymock.EasyMock; 028 import static org.easymock.EasyMock.checkOrder; 029 import static org.easymock.EasyMock.expect; 030 import org.testng.annotations.Test; 031 032 import javax.servlet.http.HttpServletResponse; 033 import java.io.ByteArrayOutputStream; 034 import java.net.URLConnection; 035 036 037 /** 038 * Tests functionality of {@link AssetService}. 039 * 040 * @author jkuhnert 041 */ 042 @Test(sequential=true) 043 public class TestAssetService extends TestBase { 044 045 public void test_Cached_Resource_Null_Modified() 046 { 047 WebRequest request = newMock(WebRequest.class); 048 checkOrder(request, false); 049 050 AssetService service = new AssetService(); 051 service.setRequest(request); 052 service.setLog(LogFactory.getLog("test")); 053 054 expect(request.getDateHeader("If-Modified-Since")).andReturn(-1l); 055 056 replay(); 057 058 assertFalse(service.cachedResource(null)); 059 060 verify(); 061 } 062 063 public void test_Cached_Resource_Stale() 064 { 065 WebRequest request = newMock(WebRequest.class); 066 checkOrder(request, false); 067 068 AssetService service = new AssetService(); 069 service.setRequest(request); 070 service.setLog(LogFactory.getLog("test")); 071 072 URLConnection url = org.easymock.classextension.EasyMock.createMock(URLConnection.class); 073 074 long modifiedSince = System.currentTimeMillis() - 1000; 075 076 expect(request.getDateHeader("If-Modified-Since")).andReturn(modifiedSince); 077 expect(url.getLastModified()).andReturn(System.currentTimeMillis()); 078 079 replay(); 080 org.easymock.classextension.EasyMock.replay(url); 081 082 assertFalse(service.cachedResource(url)); 083 084 verify(); 085 org.easymock.classextension.EasyMock.verify(url); 086 } 087 088 public void test_Cached_Resource_Good() 089 throws Exception 090 { 091 WebRequest request = newMock(WebRequest.class); 092 checkOrder(request, false); 093 WebResponse response = newMock(WebResponse.class); 094 095 AssetService service = new AssetService(); 096 service.setRequest(request); 097 service.setResponse(response); 098 service.setLog(LogFactory.getLog("test")); 099 100 URLConnection url = org.easymock.classextension.EasyMock.createMock(URLConnection.class); 101 102 long lastModified = System.currentTimeMillis() - 4000; 103 long modifiedSince = System.currentTimeMillis(); 104 105 expect(request.getDateHeader("If-Modified-Since")).andReturn(modifiedSince); 106 expect(url.getLastModified()).andReturn(lastModified); 107 108 response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); 109 110 replay(); 111 org.easymock.classextension.EasyMock.replay(url); 112 113 assertTrue(service.cachedResource(url)); 114 115 verify(); 116 org.easymock.classextension.EasyMock.verify(url); 117 } 118 119 public void test_ETag_Header_Response() 120 throws Exception 121 { 122 String requestedResource = "/org/apache/tapestry/asset/tapestry-in-action.png"; 123 WebRequest request = newMock(WebRequest.class); 124 checkOrder(request, false); 125 WebResponse response = newMock(WebResponse.class); 126 WebContext context = newMock(WebContext.class); 127 IRequestCycle cycle = newMock(IRequestCycle.class); 128 ResourceMatcher matcher = newMock(ResourceMatcher.class); 129 130 ClassResolver resolver = new DefaultClassResolver(); 131 URLConnection url = resolver.getResource(requestedResource).openConnection(); 132 133 AssetService service = new AssetService(); 134 service.setRequest(request); 135 service.setResponse(response); 136 service.setLog(LogFactory.getLog("test")); 137 service.setUnprotectedMatcher(matcher); 138 service.setClassResolver(resolver); 139 service.setContext(context); 140 141 expect(cycle.getParameter("path")).andReturn(requestedResource); 142 expect(cycle.getParameter("digest")).andReturn(null); 143 144 expect(matcher.containsResource(requestedResource)).andReturn(true); 145 146 expect(request.getDateHeader("If-Modified-Since")).andReturn(-1L); 147 expect(context.getMimeType(requestedResource)).andReturn("image/png"); 148 149 response.setDateHeader("Last-Modified", url.getLastModified()); 150 response.setDateHeader("Expires", service._expireTime); 151 response.setHeader("Cache-Control", "public, max-age=" + (AssetService.MONTH_SECONDS * 3)); 152 153 expect(request.getHeader("User-Agent")).andReturn("Mozilla").anyTimes(); 154 155 response.setHeader("ETag", "W/\"" + url.getContentLength() + "-" + url.getLastModified() + "\""); 156 response.setContentLength(url.getContentLength()); 157 158 expect(response.getOutputStream(new ContentType("image/png"))).andReturn(new ByteArrayOutputStream()); 159 160 replay(); 161 162 service.service(cycle); 163 164 verify(); 165 } 166 167 public void test_Invalid_Resource() 168 throws Exception 169 { 170 String requestedResource = "/org/apache/tapestry/asset/tapestry-in-action-missing.png"; 171 WebRequest request = newMock(WebRequest.class); 172 checkOrder(request, false); 173 WebResponse response = newMock(WebResponse.class); 174 WebContext context = newMock(WebContext.class); 175 IRequestCycle cycle = newMock(IRequestCycle.class); 176 ResourceMatcher matcher = newMock(ResourceMatcher.class); 177 ResourceDigestSource digestSource = newMock(ResourceDigestSource.class); 178 RequestExceptionReporter exceptionReporter = newMock(RequestExceptionReporter.class); 179 180 // digester throws exception for invalid resources 181 expect(digestSource.getDigestForResource(requestedResource)) 182 .andThrow(new ApplicationRuntimeException("error")) 183 .anyTimes(); 184 // in which case the exception reporter has to show them 185 exceptionReporter.reportRequestException((String)EasyMock.anyObject(), (Throwable)EasyMock.anyObject()); 186 EasyMock.expectLastCall().anyTimes(); 187 188 ClassResolver resolver = new DefaultClassResolver(); 189 190 AssetService service = new AssetService(); 191 service.setRequest(request); 192 service.setResponse(response); 193 service.setLog(LogFactory.getLog("test")); 194 service.setUnprotectedMatcher(matcher); 195 service.setClassResolver(resolver); 196 service.setContext(context); 197 service.setDigestSource(digestSource); 198 service.setExceptionReporter(exceptionReporter); 199 200 expect(cycle.getParameter("path")).andReturn(requestedResource); 201 expect(cycle.getParameter("digest")).andReturn(null); 202 203 expect(matcher.containsResource(requestedResource)).andReturn(false); 204 205 // make sure that a 404 is sent - instead of an exception thrown 206 response.setStatus(HttpServletResponse.SC_NOT_FOUND); 207 208 replay(); 209 210 service.service(cycle); 211 212 verify(); 213 } 214 }