001: package com.mockrunner.servlet;
002:
003: import javax.servlet.Filter;
004: import javax.servlet.ServletException;
005: import javax.servlet.ServletRequest;
006: import javax.servlet.ServletResponse;
007: import javax.servlet.http.HttpServlet;
008:
009: import com.mockrunner.base.HTMLOutputModule;
010: import com.mockrunner.base.NestedApplicationException;
011: import com.mockrunner.mock.web.WebMockObjectFactory;
012:
013: /**
014: * Module for servlet and filter tests. Can test
015: * single servlets and filters and simulate a filter
016: * chain.
017: */
018: public class ServletTestModule extends HTMLOutputModule {
019: private WebMockObjectFactory mockFactory;
020: private HttpServlet servlet;
021: private boolean doChain;
022:
023: public ServletTestModule(WebMockObjectFactory mockFactory) {
024: super (mockFactory);
025: this .mockFactory = mockFactory;
026: doChain = false;
027: }
028:
029: /**
030: * Creates a servlet and initializes it. <code>servletClass</code> must
031: * be of the type <code>HttpServlet</code>, otherwise a
032: * <code>RuntimeException</code> will be thrown.
033: * Sets the specified servlet as the current servlet and
034: * initializes the filter chain with it.
035: * @param servletClass the class of the servlet
036: * @return instance of <code>HttpServlet</code>
037: * @throws RuntimeException if <code>servletClass</code> is not an
038: * instance of <code>HttpServlet</code>
039: */
040: public HttpServlet createServlet(Class servletClass) {
041: if (!HttpServlet.class.isAssignableFrom(servletClass)) {
042: throw new RuntimeException(
043: "servletClass must be an instance of javax.servlet.http.HttpServlet");
044: }
045: try {
046: HttpServlet theServlet = (HttpServlet) servletClass
047: .newInstance();
048: setServlet(theServlet, true);
049: return theServlet;
050: } catch (Exception exc) {
051: throw new NestedApplicationException(exc);
052: }
053: }
054:
055: /**
056: * Sets the specified servlet as the current servlet without initializing it.
057: * You have to set the <code>ServletConfig</code> on your own.
058: * Usually you can use
059: * {@link com.mockrunner.mock.web.WebMockObjectFactory#getMockServletConfig}.
060: * @param servlet the servlet
061: */
062: public void setServlet(HttpServlet servlet) {
063: setServlet(servlet, false);
064: }
065:
066: /**
067: * Sets the specified servlet as the current servlet.
068: * Initializes it, if <code>doInit</code> is <code>true</code>.
069: * @param servlet the servlet
070: * @param doInit should <code>init</code> be called
071: */
072: public void setServlet(HttpServlet servlet, boolean doInit) {
073: try {
074: this .servlet = servlet;
075: if (doInit) {
076: servlet.init(mockFactory.getMockServletConfig());
077: }
078: mockFactory.getMockFilterChain().setServlet(servlet);
079: } catch (Exception exc) {
080: throw new NestedApplicationException(exc);
081: }
082: }
083:
084: /**
085: * Returns the current servlet.
086: * @return the servlet
087: */
088: public HttpServlet getServlet() {
089: return servlet;
090: }
091:
092: /**
093: * Creates a filter, initializes it and adds it to the
094: * filter chain. <code>filterClass</code> must be of the type
095: * <code>Filter</code>, otherwise a <code>RuntimeException</code>
096: * will be thrown. You can loop through the filter chain with
097: * {@link #doFilter}. If you set <code>doChain</code> to
098: * <code>true</code> every call of one of the servlet methods
099: * will go through the filter chain before calling the servlet
100: * method.
101: * @param filterClass the class of the filter
102: * @return instance of <code>Filter</code>
103: * @throws RuntimeException if <code>filterClass</code> is not an
104: * instance of <code>Filter</code>
105: */
106: public Filter createFilter(Class filterClass) {
107: if (!Filter.class.isAssignableFrom(filterClass)) {
108: throw new RuntimeException(
109: "filterClass must be an instance of javax.servlet.Filter");
110: }
111: try {
112: Filter theFilter = (Filter) filterClass.newInstance();
113: addFilter(theFilter, true);
114: return theFilter;
115: } catch (Exception exc) {
116: throw new NestedApplicationException(exc);
117: }
118: }
119:
120: /**
121: * Adds the specified filter to the filter chain without
122: * initializing it.
123: * You have to set the <code>FilterConfig</code> on your own.
124: * Usually you can use
125: * {@link com.mockrunner.mock.web.WebMockObjectFactory#getMockFilterConfig}.
126: * @param filter the filter
127: */
128: public void addFilter(Filter filter) {
129: addFilter(filter, false);
130: }
131:
132: /**
133: * Adds the specified filter it to the filter chain. Initializes it,
134: * if <code>doInit</code> is <code>true</code>.
135: * @param filter the filter
136: * @param doInit should <code>init</code> be called
137: */
138: public void addFilter(Filter filter, boolean doInit) {
139: if (doInit) {
140: try {
141: filter.init(mockFactory.getMockFilterConfig());
142: } catch (Exception exc) {
143: throw new NestedApplicationException(exc);
144: }
145: }
146: mockFactory.getMockFilterChain().addFilter(filter);
147: }
148:
149: /**
150: * Deletes all filters in the filter chain.
151: */
152: public void releaseFilters() {
153: mockFactory.getMockFilterChain().release();
154: mockFactory.getMockFilterChain().setServlet(servlet);
155: }
156:
157: /**
158: * If <code>doChain</code> is set to <code>true</code>
159: * (default is <code>false</code>) every call of
160: * one of the servlet methods will go through the filter chain
161: * before calling the servlet method.
162: * @param doChain <code>true</code> if the chain should be called
163: */
164: public void setDoChain(boolean doChain) {
165: this .doChain = doChain;
166: }
167:
168: /**
169: * Loops through the filter chain and calls the current servlets
170: * <code>service</code> method at the end (only if a current servlet
171: * is set). You can use it to test single filters or the interaction
172: * of filters and servlets.
173: * If you set <i>doChain</i> to <code>true</code> (use {@link #setDoChain}),
174: * this method is called before any call of a servlet method. If a filter
175: * does not call it's chains <code>doFilter</code> method, the chain
176: * breaks and the servlet will not be called (just like it in the
177: * real container).
178: */
179: public void doFilter() {
180: try {
181: mockFactory.getMockFilterChain().doFilter(
182: mockFactory.getWrappedRequest(),
183: mockFactory.getWrappedResponse());
184: mockFactory.getMockFilterChain().reset();
185: } catch (Exception exc) {
186: throw new NestedApplicationException(exc);
187: }
188: }
189:
190: /**
191: * Calls the current servlets <code>init</code> method. Is automatically
192: * done when calling {@link #createServlet}.
193: */
194: public void init() {
195: try {
196: servlet.init(mockFactory.getMockServletConfig());
197: } catch (ServletException exc) {
198: throw new NestedApplicationException(exc);
199: }
200: }
201:
202: /**
203: * Calls the current servlets <code>doDelete</code> method.
204: * If you set <i>doChain</i> to <code>true</code> (use {@link #setDoChain}),
205: * the filter chain will be called before <code>doDelete</code>.
206: */
207: public void doDelete() {
208: mockFactory.getMockRequest().setMethod("DELETE");
209: callService();
210: }
211:
212: /**
213: * Calls the current servlets <code>doGet</code> method.
214: * If you set <i>doChain</i> to <code>true</code> (use {@link #setDoChain}),
215: * the filter chain will be called before <code>doGet</code>.
216: */
217: public void doGet() {
218: mockFactory.getMockRequest().setMethod("GET");
219: callService();
220: }
221:
222: /**
223: * Calls the current servlets <code>doOptions</code> method.
224: * If you set <i>doChain</i> to <code>true</code> (use {@link #setDoChain}),
225: * the filter chain will be called before <code>doOptions</code>.
226: */
227: public void doOptions() {
228: mockFactory.getMockRequest().setMethod("OPTIONS");
229: callService();
230: }
231:
232: /**
233: * Calls the current servlets <code>doPost</code> method.
234: * If you set <i>doChain</i> to <code>true</code> (use {@link #setDoChain}),
235: * the filter chain will be called before <code>doPost</code>.
236: */
237: public void doPost() {
238: mockFactory.getMockRequest().setMethod("POST");
239: callService();
240: }
241:
242: /**
243: * Calls the current servlets <code>doPut</code> method.
244: * If you set <i>doChain</i> to <code>true</code> (use {@link #setDoChain}),
245: * the filter chain will be called before <code>doPut</code>.
246: */
247: public void doPut() {
248: mockFactory.getMockRequest().setMethod("PUT");
249: callService();
250: }
251:
252: /**
253: * Calls the current servlets <code>doTrace</code> method.
254: * If you set <i>doChain</i> to <code>true</code> (use {@link #setDoChain}),
255: * the filter chain will be called before <code>doTrace</code>.
256: */
257: public void doTrace() {
258: mockFactory.getMockRequest().setMethod("TRACE");
259: callService();
260: }
261:
262: /**
263: * Calls the current servlets <code>doHead</code> method.
264: * If you set <i>doChain</i> to <code>true</code> (use {@link #setDoChain}),
265: * the filter chain will be called before <code>doHead</code>.
266: */
267: public void doHead() {
268: mockFactory.getMockRequest().setMethod("HEAD");
269: callService();
270: }
271:
272: /**
273: * Calls the current servlets <code>service</code> method.
274: * If you set <i>doChain</i> to <code>true</code> (use {@link #setDoChain}),
275: * the filter chain will be called before <code>service</code>.
276: */
277: public void service() {
278: callService();
279: }
280:
281: /**
282: * Returns the last request from the filter chain. Since
283: * filters can replace the request with a request wrapper,
284: * this method makes only sense after calling at least
285: * one filter, i.e. after calling {@link #doFilter} or
286: * after calling one servlet method with <i>doChain</i>
287: * set to <code>true</code>.
288: * @return the filtered request
289: */
290: public ServletRequest getFilteredRequest() {
291: return mockFactory.getMockFilterChain().getLastRequest();
292: }
293:
294: /**
295: * Returns the last response from the filter chain. Since
296: * filters can replace the response with a response wrapper,
297: * this method makes only sense after calling at least
298: * one filter, i.e. after calling {@link #doFilter} or
299: * after calling one servlet method with <i>doChain</i>
300: * set to <code>true</code>.
301: * @return the filtered response
302: */
303: public ServletResponse getFilteredResponse() {
304: return mockFactory.getMockFilterChain().getLastResponse();
305: }
306:
307: /**
308: * Returns the servlet output as a string. Flushes the output
309: * before returning it.
310: * @return the servlet output
311: */
312: public String getOutput() {
313: try {
314: mockFactory.getMockResponse().getWriter().flush();
315: } catch (Exception exc) {
316:
317: }
318: return mockFactory.getMockResponse().getOutputStreamContent();
319: }
320:
321: /**
322: * Clears the output content
323: */
324: public void clearOutput() {
325: mockFactory.getMockResponse().resetBuffer();
326: }
327:
328: private void callService() {
329: try {
330: if (doChain) {
331: doFilter();
332: } else {
333: servlet.service(mockFactory.getWrappedRequest(),
334: mockFactory.getWrappedResponse());
335: }
336: } catch (Exception exc) {
337: throw new NestedApplicationException(exc);
338: }
339: }
340: }
|