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    }