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