001    package com.mockrunner.mock.web;
002    
003    import java.lang.reflect.Constructor;
004    
005    import javax.servlet.http.HttpServletRequest;
006    import javax.servlet.http.HttpServletResponse;
007    import javax.servlet.jsp.JspFactory;
008    
009    import com.mockrunner.base.NestedApplicationException;
010    
011    /**
012     * Used to create all types of web mock objects. Maintains
013     * the necessary dependencies between the mock objects.
014     * If you use the mock objects returned by this
015     * factory in your tests you can be sure that they are all
016     * up to date.
017     */
018    public class WebMockObjectFactory
019    {
020        private HttpServletRequest wrappedRequest;
021        private HttpServletResponse wrappedResponse;
022        private MockHttpServletRequest request;
023        private MockHttpServletResponse response;
024        private MockServletConfig config;
025        private MockServletContext context;
026        private MockHttpSession session;
027        private MockPageContext pageContext;
028        private MockFilterConfig filterConfig;
029        private MockFilterChain filterChain;
030        private JspFactory jspFactory;
031    
032        /**
033         * Creates a new set of mock objects.
034         */
035        public WebMockObjectFactory()
036        {
037            createMockObjects();
038        }
039        
040        /**
041         * Creates a set of mock objects based on another one.
042         * The created mock objects will have their own
043         * request and session objects, but they will share
044         * one <code>ServletContext</code>.
045         * @param factory the other factory
046         * @see com.mockrunner.base.BaseTestCase#createWebMockObjectFactory(WebMockObjectFactory)
047         */
048        public WebMockObjectFactory(WebMockObjectFactory factory)
049        {
050            createMockObjectsBasedOn(factory);
051        }
052        
053        /**
054         * Creates a set of mock objects based on another one.
055         * You can specify, if the created mock objects should
056         * share the same session. They will share one
057         * <code>ServletContext</code> anyway.
058         * @param factory the other factory
059         * @param createNewSession <code>true</code> creates a new session,
060         *                         <code>false</code> uses the session from factory
061         * @see com.mockrunner.base.BaseTestCase#createWebMockObjectFactory(WebMockObjectFactory, boolean)
062         */
063        public WebMockObjectFactory(WebMockObjectFactory factory, boolean createNewSession)
064        {
065            createMockObjectsBasedOn(factory, createNewSession);
066        }
067     
068        private void createMockObjects()
069        {
070            createNewMockObjects(true);
071            context = createMockServletContext();
072            setUpDependencies();
073            JspFactory.setDefaultFactory(jspFactory);
074        }
075    
076        private void createMockObjectsBasedOn(WebMockObjectFactory factory)
077        {
078            createMockObjectsBasedOn(factory, true);
079        }
080        
081        private void createMockObjectsBasedOn(WebMockObjectFactory factory, boolean createNewSession)
082        {
083            createNewMockObjects(createNewSession);
084            if(!createNewSession) session = factory.getMockSession();
085            context = factory.getMockServletContext();
086            setUpDependencies();
087            JspFactory.setDefaultFactory(jspFactory);
088        }
089    
090        private void createNewMockObjects(boolean createNewSession)
091        {
092            request = createMockRequest();
093            response = createMockResponse();
094            wrappedRequest = request;
095            wrappedResponse = response;
096            if(createNewSession) session = createMockSession();
097            config = createMockServletConfig();
098            filterChain = createMockFilterChain();
099            filterConfig = createMockFilterConfig();
100            jspFactory = createMockJspFactory();
101        }
102    
103        private void setUpDependencies()
104        {
105            config.setServletContext(context);
106            request.setSession(session);
107            session.setupServletContext(context);
108            pageContext = createMockPageContext();
109            pageContext.setServletConfig(config);
110            pageContext.setServletRequest(request);
111            pageContext.setServletResponse(response);
112            filterConfig.setupServletContext(context);
113            setUpJspFactory();
114        }
115    
116        private void setUpJspFactory()
117        {
118            if(jspFactory instanceof MockJspFactory)
119            {
120                ((MockJspFactory)jspFactory).setPageContext(pageContext);
121            }
122        }
123        
124        /**
125         * Sets the default <code>JspFactory</code> by calling
126         * <code>JspFactory.setDefaultFactory()</code>.
127         * @param jspFactory the <code>JspFactory</code>
128         */
129        public void setDefaultJspFactory(JspFactory jspFactory)
130        {
131            JspFactory.setDefaultFactory(jspFactory);
132            this.jspFactory = jspFactory;
133            setUpJspFactory();
134        }
135        
136        /**
137         * Refreshes the mock objects dependencies. May be called after setting request
138         * and response wrappers.
139         */
140        public void refresh()
141        {
142            pageContext = new MockPageContext(config, wrappedRequest, wrappedResponse);
143            setUpJspFactory();
144        }
145        
146        /**
147         * Creates the {@link com.mockrunner.mock.web.MockServletContext} using <code>new</code>.
148         * This method can be overridden to return a subclass of {@link com.mockrunner.mock.web.MockServletContext}.
149         * @return the {@link com.mockrunner.mock.web.MockServletContext}
150         */
151        public MockServletContext createMockServletContext()
152        {
153            return new MockServletContext();
154        }
155    
156        /**
157         * Creates the {@link com.mockrunner.mock.web.MockServletConfig} using <code>new</code>.
158         * This method can be overridden to return a subclass of {@link com.mockrunner.mock.web.MockServletConfig}.
159         * @return the {@link com.mockrunner.mock.web.MockServletConfig}
160         */
161        public MockServletConfig createMockServletConfig()
162        {
163            return new MockServletConfig();
164        }
165        
166        /**
167         * Creates the {@link com.mockrunner.mock.web.MockHttpServletResponse} using <code>new</code>.
168         * This method can be overridden to return a subclass of {@link com.mockrunner.mock.web.MockHttpServletResponse}.
169         * @return the {@link com.mockrunner.mock.web.MockHttpServletResponse}
170         */
171        public MockHttpServletResponse createMockResponse()
172        {
173            return new MockHttpServletResponse();
174        }
175    
176        /**
177         * Creates the {@link com.mockrunner.mock.web.MockHttpServletRequest} using <code>new</code>.
178         * This method can be overridden to return a subclass of {@link com.mockrunner.mock.web.MockHttpServletRequest}.
179         * @return the {@link com.mockrunner.mock.web.MockHttpServletRequest}
180         */
181        public MockHttpServletRequest createMockRequest()
182        {
183            return new MockHttpServletRequest();
184        }
185        
186        /**
187         * Creates the {@link com.mockrunner.mock.web.MockHttpSession} using <code>new</code>.
188         * This method can be overridden to return a subclass of {@link com.mockrunner.mock.web.MockHttpSession}.
189         * @return the {@link com.mockrunner.mock.web.MockHttpSession}
190         */
191        public MockHttpSession createMockSession()
192        {
193            return new MockHttpSession();
194        }
195        
196        /**
197         * Creates the {@link com.mockrunner.mock.web.MockPageContext} using <code>new</code>.
198         * This method can be overridden to return a subclass of {@link com.mockrunner.mock.web.MockPageContext}.
199         * @return the {@link com.mockrunner.mock.web.MockPageContext}
200         */
201        public MockPageContext createMockPageContext()
202        {
203            return new MockPageContext();
204        }
205        
206        /**
207         * Creates the {@link com.mockrunner.mock.web.MockFilterConfig} using <code>new</code>.
208         * This method can be overridden to return a subclass of {@link com.mockrunner.mock.web.MockFilterConfig}.
209         * @return the {@link com.mockrunner.mock.web.MockFilterConfig}
210         */
211        public MockFilterConfig createMockFilterConfig()
212        {
213            return new MockFilterConfig();
214        }
215    
216        /**
217         * Creates the {@link com.mockrunner.mock.web.MockFilterChain} using <code>new</code>.
218         * This method can be overridden to return a subclass of {@link com.mockrunner.mock.web.MockFilterChain}.
219         * @return the {@link com.mockrunner.mock.web.MockFilterChain}
220         */
221        public MockFilterChain createMockFilterChain()
222        {
223            return new MockFilterChain();
224        }
225        
226        /**
227         * Creates the {@link com.mockrunner.mock.web.MockJspFactory} using <code>new</code>.
228         * This method can be overridden to return a subclass of {@link com.mockrunner.mock.web.MockJspFactory}.
229         * @return the {@link com.mockrunner.mock.web.MockJspFactory}
230         */
231        public MockJspFactory createMockJspFactory()
232        {
233            return new MockJspFactory();
234        }
235        
236        /**
237         * Returns the <code>MockServletConfig</code>
238         * @return the <code>MockServletConfig</code>
239         */
240        public MockServletConfig getMockServletConfig()
241        {
242            return config;
243        }
244    
245        /**
246         * Returns the {@link com.mockrunner.mock.web.MockServletContext}.
247         * @return the {@link com.mockrunner.mock.web.MockServletContext}
248         */
249        public MockServletContext getMockServletContext()
250        {
251            return context;
252        }
253    
254        /**
255         * Returns the {@link com.mockrunner.mock.web.MockHttpServletRequest}.
256         * @return the {@link com.mockrunner.mock.web.MockHttpServletRequest}
257         */
258        public MockHttpServletRequest getMockRequest()
259        {
260            return request;
261        }
262    
263        /**
264         * Returns the {@link com.mockrunner.mock.web.MockHttpServletResponse}.
265         * @return the {@link com.mockrunner.mock.web.MockHttpServletResponse}
266         */
267        public MockHttpServletResponse getMockResponse()
268        {
269            return response;
270        }
271        
272        /**
273         * Returns the wrapped <code>HttpServletRequest</code>. If no
274         * wrapper is specified, this method returns the mock request itself.
275         * @return the wrapped <code>HttpServletRequest</code>
276         */
277        public HttpServletRequest getWrappedRequest()
278        {
279            return wrappedRequest;
280        }
281    
282        /**
283         * Returns the wrapped <code>HttpServletResponse</code>. If no
284         * wrapper is specified, this method returns the mock response itself.
285         * @return the wrapped <code>HttpServletRequest</code>
286         */
287        public HttpServletResponse getWrappedResponse()
288        {
289            return wrappedResponse;
290        }
291    
292        /**
293         * Returns the {@link com.mockrunner.mock.web.MockHttpSession}.
294         * @return the {@link com.mockrunner.mock.web.MockHttpSession}
295         */
296        public MockHttpSession getMockSession()
297        {
298            return session;
299        }
300    
301        /**
302         * Returns the {@link com.mockrunner.mock.web.MockHttpSession}.
303         * @return the {@link com.mockrunner.mock.web.MockHttpSession}
304         * @deprecated use {@link #getMockSession}
305         */
306        public MockHttpSession getSession()
307        {
308            return getMockSession();
309        }
310    
311        /**
312         * Returns the {@link com.mockrunner.mock.web.MockPageContext}.
313         * @return the {@link com.mockrunner.mock.web.MockPageContext}
314         */
315        public MockPageContext getMockPageContext()
316        {
317            return pageContext;
318        }
319        
320        /**
321         * Returns the {@link com.mockrunner.mock.web.MockFilterConfig}.
322         * @return the {@link com.mockrunner.mock.web.MockFilterConfig}
323         */
324        public MockFilterConfig getMockFilterConfig()
325        {
326            return filterConfig;
327        }
328    
329        /**
330         * Returns the {@link com.mockrunner.mock.web.MockFilterChain}.
331         * @return the {@link com.mockrunner.mock.web.MockFilterChain}
332         */
333        public MockFilterChain getMockFilterChain()
334        {
335            return filterChain;
336        }
337        
338        /**
339         * Returns the {@link com.mockrunner.mock.web.MockJspFactory}.
340         * If the current <code>JspFactory</code> is not an instance
341         * of {@link com.mockrunner.mock.web.MockJspFactory}, <code>null</code>
342         * will be returned.
343         * @return the {@link com.mockrunner.mock.web.MockJspFactory}
344         */
345        public MockJspFactory getMockJspFactory()
346        {
347            if(jspFactory instanceof MockJspFactory)
348            {
349                return (MockJspFactory)jspFactory;
350            }
351            return null;
352        }
353        
354        /**
355         * Returns the <code>JspFactory</code>.
356         * @return the <code>JspFactory</code>
357         */
358        public JspFactory getJspFactory()
359        {
360            return jspFactory;
361        }
362     
363        /**
364         * Can be used to add a request wrapper. All the
365         * test modules are using the wrapped request returned by
366         * {@link #getWrappedRequest}. The method {@link #getMockRequest}
367         * returns the mock request without any wrapper.
368         * Usually the wrapper is of type <code>javax.servlet.http.HttpServletRequestWrapper</code>.
369         * That's not absolutely necessary but the wrapper must define a constructor
370         * that takes a single <code>javax.servlet.http.HttpServletRequest</code> argument
371         * and must implement <code>javax.servlet.http.HttpServletRequest</code>.
372         * @param wrapper the wrapper class
373         */
374        public void addRequestWrapper(Class wrapper)
375        {
376            try
377            {
378                Constructor constructor = wrapper.getConstructor(new Class[] {HttpServletRequest.class});
379                wrappedRequest = (HttpServletRequest)constructor.newInstance(new Object[] {wrappedRequest});
380            }
381            catch(Exception exc)
382            {
383                throw new NestedApplicationException(exc);
384            }
385        }
386        
387        /**
388         * Can be used to add a request wrapper. 
389         * All the test modules are using the wrapped request returned by
390         * {@link #getWrappedRequest}. The method {@link #getMockRequest}
391         * returns the mock request without any wrapper. Usually the wrapper is
392         * an instance of <code>javax.servlet.http.HttpServletRequestWrapper</code>
393         * and wraps the current request but that's not absolutely necessary. 
394         * However, be careful if you want to add custom mock versions of 
395         * <code>javax.servlet.http.HttpServletRequest</code>.
396         * @param wrapper the request wrapper
397         */
398        public void addRequestWrapper(HttpServletRequest wrapper)
399        {
400            wrappedRequest = wrapper;
401        }
402        
403        /**
404         * Can be used to add a response wrapper. All the
405         * test modules are using the wrapped response returned by
406         * {@link #getWrappedResponse}. The method {@link #getMockResponse}
407         * returns the mock response without any wrapper.
408         * Usually the wrapper is of type <code>javax.servlet.http.HttpServletResponseWrapper</code>.
409         * That's not absolutely necessary but the wrapper must define a constructor
410         * that takes a single <code>javax.servlet.http.HttpServletResponse</code> argument
411         * and must implement <code>javax.servlet.http.HttpServletResponse</code>.
412         * @param wrapper the wrapper class
413         */
414        public void addResponseWrapper(Class wrapper)
415        {
416            try
417            {
418                Constructor constructor = wrapper.getConstructor(new Class[] {HttpServletResponse.class});
419                wrappedResponse = (HttpServletResponse)constructor.newInstance(new Object[] {wrappedResponse});
420            }
421            catch(Exception exc)
422            {
423                throw new NestedApplicationException(exc);
424            }
425        }
426        
427        /**
428         * Can be used to add a response wrapper. 
429         * All the test modules are using the wrapped response returned by
430         * {@link #getWrappedResponse}. The method {@link #getMockResponse}
431         * returns the mock response without any wrapper. Usually the wrapper is
432         * an instance of <code>javax.servlet.http.HttpServletResponseWrapper</code>
433         * and wraps the current response but that's not absolutely necessary. 
434         * However, be careful if you want to add custom mock versions of 
435         * <code>javax.servlet.http.HttpServletResponse</code>.
436         * @param wrapper the wrapper
437         */
438        public void addResponseWrapper(HttpServletResponse wrapper)
439        {
440            wrappedResponse = wrapper;
441        }
442    }