001: package com.meterware.servletunit;
002:
003: /********************************************************************************************************************
004: * $Id: InvocationContextImpl.java,v 1.17 2004/06/13 20:57:28 russgold Exp $
005: *
006: * Copyright (c) 2001-2004, Russell Gold
007: *
008: * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
009: * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
010: * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
011: * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
012: *
013: * The above copyright notice and this permission notice shall be included in all copies or substantial portions
014: * of the Software.
015: *
016: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
017: * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
018: * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
019: * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
020: * DEALINGS IN THE SOFTWARE.
021: *
022: *******************************************************************************************************************/
023: import com.meterware.httpunit.HttpException;
024: import com.meterware.httpunit.WebRequest;
025: import com.meterware.httpunit.WebResponse;
026: import com.meterware.httpunit.FrameSelector;
027:
028: import java.io.IOException;
029: import java.net.MalformedURLException;
030: import java.net.URL;
031: import java.util.Dictionary;
032: import java.util.Stack;
033:
034: import javax.servlet.*;
035:
036: import javax.servlet.http.Cookie;
037: import javax.servlet.http.HttpServletRequest;
038: import javax.servlet.http.HttpServletResponse;
039: import javax.servlet.http.HttpSession;
040:
041: /**
042: * This class represents the context in which a specific servlet request is being made.
043: * It contains the objects needed to unit test the methods of a servlet.
044: **/
045: class InvocationContextImpl implements InvocationContext {
046:
047: private Stack _contextStack = new Stack();
048: private URL _effectiveURL;
049:
050: private ServletUnitClient _client;
051: private WebApplication _application;
052: private FrameSelector _frame;
053:
054: private WebResponse _webResponse;
055:
056: /**
057: * Returns the request to be processed by the servlet or filter.
058: **/
059: public HttpServletRequest getRequest() {
060: return getContext().getRequest();
061: }
062:
063: /**
064: * Returns the response which the servlet or filter should modify during its operation.
065: **/
066: public HttpServletResponse getResponse() {
067: return getContext().getResponse();
068: }
069:
070: /**
071: * Invokes the current servlet or filter.
072: */
073: public void service() throws ServletException, IOException {
074: if (isFilterActive()) {
075: getFilter().doFilter(getRequest(), getResponse(),
076: getFilterChain());
077: } else {
078: getServlet().service(getRequest(), getResponse());
079: }
080: }
081:
082: /**
083: * Returns the selected servlet, initialized to provide access to sessions
084: * and servlet context information.
085: **/
086: public Servlet getServlet() throws ServletException {
087: return getContext().getServlet();
088: }
089:
090: /**
091: * Returns the final response from the servlet. Note that this method should
092: * only be invoked after all processing has been done to the servlet response.
093: **/
094: public WebResponse getServletResponse() throws IOException {
095: if (_contextStack.size() != 1)
096: throw new IllegalStateException(
097: "Have not returned from all request dispatchers");
098: if (_webResponse == null) {
099: HttpSession session = getRequest().getSession(
100: /* create */false);
101: if (session != null && session.isNew()) {
102: Cookie cookie = new Cookie(
103: ServletUnitHttpSession.SESSION_COOKIE_NAME,
104: session.getId());
105: cookie.setPath(_application.getContextPath());
106: getResponse().addCookie(cookie);
107: }
108: _webResponse = new ServletUnitWebResponse(_client, _frame,
109: _effectiveURL, getResponse(), _client
110: .getExceptionsThrownOnErrorStatus());
111: }
112: return _webResponse;
113: }
114:
115: public FrameSelector getFrame() {
116: return _frame;
117: }
118:
119: public void pushIncludeRequest(RequestDispatcher rd,
120: HttpServletRequest request, HttpServletResponse response)
121: throws ServletException {
122: if (isFilterActive())
123: throw new IllegalStateException(
124: "May not push an include request when no servlet is active");
125: _contextStack.push(new ExecutionContext(
126: DispatchedRequestWrapper.createIncludeRequestWrapper(
127: request, rd), response,
128: ((RequestDispatcherImpl) rd).getServletMetaData()));
129: }
130:
131: public void pushForwardRequest(RequestDispatcher rd,
132: HttpServletRequest request, HttpServletResponse response)
133: throws ServletException {
134: if (isFilterActive())
135: throw new IllegalStateException(
136: "May not push a forward request when no servlet is active");
137: _contextStack.push(new ExecutionContext(
138: DispatchedRequestWrapper.createForwardRequestWrapper(
139: request, rd), response,
140: ((RequestDispatcherImpl) rd).getServletMetaData()));
141: }
142:
143: public void popRequest() {
144: if (getContext().mayPopFilter()) {
145: getContext().popFilter();
146: } else if (_contextStack.size() == 1) {
147: throw new IllegalStateException(
148: "May not pop the initial request");
149: } else {
150: _contextStack.pop();
151: }
152: }
153:
154: public boolean isFilterActive() {
155: return getContext().isFilterActive();
156: }
157:
158: public Filter getFilter() throws ServletException {
159: return getContext().getFilter();
160: }
161:
162: public FilterChain getFilterChain() {
163: return new FilterChain() {
164: public void doFilter(ServletRequest servletRequest,
165: ServletResponse servletResponse)
166: throws IOException, ServletException {
167: pushFilter(servletRequest, servletResponse);
168: service();
169: popRequest();
170: }
171: };
172: }
173:
174: public void pushFilter(ServletRequest request,
175: ServletResponse response) {
176: getContext().pushFilter(request, response);
177: }
178:
179: class AccessDeniedException extends HttpException {
180: public AccessDeniedException(URL baseURL) {
181: super (403, "Access Denied", baseURL);
182: }
183: }
184:
185: //------------------------------ package methods ---------------------------------------
186:
187: /**
188: * Constructs a servlet invocation context for a specified servlet container,
189: * request, and cookie headers.
190: **/
191: InvocationContextImpl(ServletUnitClient client,
192: ServletRunner runner, FrameSelector frame,
193: WebRequest request, Dictionary clientHeaders,
194: byte[] messageBody) throws IOException,
195: MalformedURLException {
196: _client = client;
197: _application = runner.getApplication();
198: _frame = frame;
199:
200: URL requestURL = request.getURL();
201: final ServletUnitHttpRequest suhr = new ServletUnitHttpRequest(
202: _application.getServletRequest(requestURL), request,
203: runner.getContext(), clientHeaders, messageBody);
204: if (_application.usesBasicAuthentication())
205: suhr.readBasicAuthentication();
206: else if (_application.usesFormAuthentication())
207: suhr.readFormAuthentication();
208:
209: HttpSession session = suhr.getSession( /* create */false);
210: if (session != null)
211: ((ServletUnitHttpSession) session).access();
212:
213: _effectiveURL = computeEffectiveUrl(suhr, requestURL);
214: _contextStack.push(new ExecutionContext(suhr,
215: new ServletUnitHttpResponse(), _application
216: .getServletRequest(_effectiveURL)));
217: }
218:
219: private URL computeEffectiveUrl(HttpServletRequest request,
220: URL requestURL) {
221: if (!_application.requiresAuthorization(requestURL)
222: || userIsAuthorized(request, requestURL)) {
223: return requestURL;
224: } else if (request.getRemoteUser() != null) {
225: throw new AccessDeniedException(requestURL);
226: } else if (_application.usesBasicAuthentication()) {
227: throw new BasicAuthenticationRequiredException(_application
228: .getAuthenticationRealm());
229: } else if (!_application.usesFormAuthentication()) {
230: throw new IllegalStateException(
231: "Authorization required but no authentication method defined");
232: } else {
233: ((ServletUnitHttpSession) request.getSession())
234: .setOriginalURL(requestURL);
235: return _application.getLoginURL();
236: }
237: }
238:
239: private boolean userIsAuthorized(HttpServletRequest request,
240: URL requestURL) {
241: String[] roles = _application.getPermittedRoles(requestURL);
242: for (int i = 0; i < roles.length; i++) {
243: if (request.isUserInRole(roles[i]))
244: return true;
245: }
246: return false;
247: }
248:
249: static class ExecutionContext {
250:
251: private HttpServletResponse _response;
252: private HttpServletRequest _request;
253: private ServletMetaData _metaData;
254:
255: private Stack _filterStack = new Stack();
256:
257: ExecutionContext(HttpServletRequest request,
258: HttpServletResponse response, ServletMetaData metaData) {
259: _request = request;
260: _response = response;
261: _metaData = metaData;
262: }
263:
264: boolean isFilterActive() {
265: return getFilterIndex() < _metaData.getFilters().length;
266: }
267:
268: Servlet getServlet() throws ServletException {
269: if (isFilterActive())
270: throw new IllegalStateException(
271: "Current context is a filter - may not request servlet.");
272: return _metaData.getServlet();
273: }
274:
275: HttpServletResponse getResponse() {
276: return _filterStack.isEmpty() ? _response
277: : ((FilterContext) _filterStack.lastElement())
278: .getResponse();
279: }
280:
281: HttpServletRequest getRequest() {
282: return _filterStack.isEmpty() ? _request
283: : ((FilterContext) _filterStack.lastElement())
284: .getRequest();
285: }
286:
287: public Filter getFilter() throws ServletException {
288: if (!isFilterActive())
289: throw new IllegalStateException(
290: "Current context is a servlet - may not request filter.");
291: return _metaData.getFilters()[getFilterIndex()].getFilter();
292: }
293:
294: public void pushFilter(ServletRequest request,
295: ServletResponse response) {
296: if (!isFilterActive())
297: throw new IllegalStateException(
298: "Current context is a servlet - may not push filter.");
299: if (!(request instanceof HttpServletRequest))
300: throw new IllegalArgumentException(
301: "HttpUnit does not support non-HTTP request wrappers");
302: if (!(response instanceof HttpServletResponse))
303: throw new IllegalArgumentException(
304: "HttpUnit does not support non-HTTP response wrappers");
305:
306: _filterStack.push(new FilterContext(
307: (HttpServletRequest) request,
308: (HttpServletResponse) response));
309: }
310:
311: public boolean mayPopFilter() {
312: return getFilterIndex() > 0;
313: }
314:
315: public void popFilter() {
316: _filterStack.pop();
317: }
318:
319: private int getFilterIndex() {
320: return _filterStack.size();
321: }
322: }
323:
324: static class FilterContext {
325: HttpServletRequest _request;
326: HttpServletResponse _response;
327:
328: public FilterContext(HttpServletRequest request,
329: HttpServletResponse response) {
330: _request = request;
331: _response = response;
332: }
333:
334: public HttpServletResponse getResponse() {
335: return _response;
336: }
337:
338: public HttpServletRequest getRequest() {
339: return _request;
340: }
341: }
342:
343: private ExecutionContext getContext() {
344: return (ExecutionContext) _contextStack.lastElement();
345: }
346: }
|