[Httpunit-commit] CVS: httpunit/src/com/meterware/servletunit FilterConfigImpl.java,NONE,1.1 FilterM
Brought to you by:
russgold
|
From: Russell G. <rus...@us...> - 2004-02-26 17:52:42
|
Update of /cvsroot/httpunit/httpunit/src/com/meterware/servletunit In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12499/src/com/meterware/servletunit Modified Files: InvocationContext.java InvocationContextImpl.java ServletMetaData.java ServletUnitClient.java ServletUnitHttpRequest.java WebApplication.java Added Files: FilterConfigImpl.java FilterMetaData.java FilterUrlMap.java WebResourceConfiguration.java XMLUtils.java Log Message: Added support for Servlet 2.3 filters ***** Error reading new file[Errno 2] No such file or directory: 'FilterConfigImpl.java' ***** Error reading new file[Errno 2] No such file or directory: 'FilterMetaData.java' ***** Error reading new file[Errno 2] No such file or directory: 'FilterUrlMap.java' ***** Error reading new file[Errno 2] No such file or directory: 'WebResourceConfiguration.java' ***** Error reading new file[Errno 2] No such file or directory: 'XMLUtils.java' Index: InvocationContext.java =================================================================== RCS file: /cvsroot/httpunit/httpunit/src/com/meterware/servletunit/InvocationContext.java,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- InvocationContext.java 13 Feb 2003 20:25:55 -0000 1.7 +++ InvocationContext.java 26 Feb 2004 17:36:47 -0000 1.8 @@ -2,7 +2,7 @@ /******************************************************************************************************************** * $Id$ * - * Copyright (c) 2001-2003, Russell Gold + * Copyright (c) 2001-2004, Russell Gold * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the Software without restriction, including without limitation @@ -23,9 +23,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.servlet.Servlet; -import javax.servlet.ServletException; -import javax.servlet.RequestDispatcher; +import javax.servlet.*; import java.io.IOException; @@ -39,20 +37,26 @@ /** - * Returns the request to be processed by the servlet. + * Returns the request to be processed by the servlet or filter. **/ HttpServletRequest getRequest(); /** - * Returns the response which the servlet should modify during its operation. + * Returns the response which the servlet or filter should modify during its operation. **/ HttpServletResponse getResponse(); /** + * Invokes the current servlet or filter. + */ + void service() throws ServletException, IOException; + + + /** * Returns the selected servlet, initialized to provide access to sessions - * and servlet context information. + * and servlet context information. Only valid to call if {@link #isFilterActive} returns false. **/ Servlet getServlet() throws ServletException; @@ -83,8 +87,40 @@ /** - * Removes the top request dispatcher from this context. + * Removes the top request dispatcher or filter from this context. */ void popRequest(); + + /** + * Returns true if the current context is a filter, rather than a servlet. + */ + boolean isFilterActive(); + + + /** + * Returns the current active filter object. Only valid to call if {@link #isFilterActive} returns true. + */ + Filter getFilter() throws ServletException; + + + /** + * Returns the current filter chain. Only valid to call if {@link #isFilterActive} returns true. + */ + FilterChain getFilterChain(); + + + /** + * Pushes the current filter onto the execution stack and switches to the next filter or the selected servlet. + * This can be used to simulate the effect of the {@link javax.servlet.FilterChain#doFilter doFilter} call. + * <br><b>Note:</b> this method specifies {@link ServletRequest} and {@link ServletResponse} because those are the + * types passed to {@link Filter#doFilter}; however, HttpUnit requires the objects to implement + * {@link HttpServletRequest} and {@link HttpServletResponse} because they will eventually be passed to an + * {@link javax.servlet.http.HttpServlet}. + * + * @param request the request to pass to the next filter. May be a wrapper. + * @param response the response object to pass to the next filter. May be a wrapper. + */ + void pushFilter( ServletRequest request, ServletResponse response ); + } Index: InvocationContextImpl.java =================================================================== RCS file: /cvsroot/httpunit/httpunit/src/com/meterware/servletunit/InvocationContextImpl.java,v retrieving revision 1.14 retrieving revision 1.15 diff -u -r1.14 -r1.15 --- InvocationContextImpl.java 17 Jun 2003 23:23:02 -0000 1.14 +++ InvocationContextImpl.java 26 Feb 2004 17:36:47 -0000 1.15 @@ -2,7 +2,7 @@ /******************************************************************************************************************** * $Id$ * -* Copyright (c) 2001-2003, Russell Gold +* Copyright (c) 2001-2004, Russell Gold * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the Software without restriction, including without limitation @@ -31,9 +31,7 @@ import java.util.Stack; import java.util.StringTokenizer; -import javax.servlet.RequestDispatcher; -import javax.servlet.Servlet; -import javax.servlet.ServletException; +import javax.servlet.*; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; @@ -46,20 +44,35 @@ **/ class InvocationContextImpl implements InvocationContext { + private Stack _contextStack = new Stack(); + private URL _effectiveURL; + /** - * Returns the request to be processed by the servlet. + * Returns the request to be processed by the servlet or filter. **/ public HttpServletRequest getRequest() { - return _request; + return getContext().getRequest(); } /** - * Returns the response which the servlet should modify during its operation. + * Returns the response which the servlet or filter should modify during its operation. **/ public HttpServletResponse getResponse() { - return _response; + return getContext().getResponse(); + } + + + /** + * Invokes the current servlet or filter. + */ + public void service() throws ServletException, IOException { + if (isFilterActive()) { + getFilter().doFilter( getRequest(), getResponse(), getFilterChain() ); + } else { + getServlet().service( getRequest(), getResponse() ); + } } @@ -68,21 +81,7 @@ * and servlet context information. **/ public Servlet getServlet() throws ServletException { - if (_servlet == null) { - if (!_application.requiresAuthorization( _requestURL ) || userIsAuthorized() ) { - _servlet = _application.getServletRequest( _requestURL ).getServlet(); - } else if (_request.getRemoteUser() != null) { - throw new AccessDeniedException( _requestURL ); - } else if (_application.usesBasicAuthentication()) { - throw new BasicAuthenticationRequiredException( _application.getAuthenticationRealm() ); - } else if (_application.usesFormAuthentication()) { - _servlet = _application.getServletRequest( _application.getLoginURL() ).getServlet(); - ((ServletUnitHttpRequest) getRequest()).setOriginalURL( _requestURL ); - } else { - throw new IllegalStateException( "Authorization required but no authentication method defined" ); - } - } - return _servlet; + return getContext().getServlet(); } @@ -91,14 +90,15 @@ * only be invoked after all processing has been done to the servlet response. **/ public WebResponse getServletResponse() throws IOException { + if (_contextStack.size() != 1) throw new IllegalStateException( "Have not returned from all request dispatchers" ); if (_webResponse == null) { - HttpSession session = _request.getSession( /* create */ false ); + HttpSession session = getRequest().getSession( /* create */ false ); if (session != null && session.isNew()) { Cookie cookie = new Cookie( ServletUnitHttpSession.SESSION_COOKIE_NAME, session.getId() ); cookie.setPath( _application.getContextPath() ); - _response.addCookie( cookie ); + getResponse().addCookie( cookie ); } - _webResponse = new ServletUnitWebResponse( _client, _target, _requestURL, _response, _client.getExceptionsThrownOnErrorStatus() ); + _webResponse = new ServletUnitWebResponse( _client, _target, _effectiveURL, getResponse(), _client.getExceptionsThrownOnErrorStatus() ); } return _webResponse; } @@ -113,34 +113,53 @@ public void pushIncludeRequest( RequestDispatcher rd, HttpServletRequest request, HttpServletResponse response ) throws ServletException { - if (_servlet == null) throw new IllegalStateException( "Get the servlet for this context before pushing a request" ); - _request = DispatchedRequestWrapper.createIncludeRequestWrapper( request, rd ); - _servletStack.push( _servlet ); - _servlet = ((RequestDispatcherImpl) rd).getServletMetaData().getServlet(); + if (isFilterActive()) throw new IllegalStateException( "May not push an include request when no servlet is active" ); + _contextStack.push( new ExecutionContext( DispatchedRequestWrapper.createIncludeRequestWrapper( request, rd ), + response, ((RequestDispatcherImpl) rd).getServletMetaData() ) ); } public void pushForwardRequest( RequestDispatcher rd, HttpServletRequest request, HttpServletResponse response ) throws ServletException { - if (_servlet == null) throw new IllegalStateException( "Get the servlet for this context before pushing a request" ); - _request = DispatchedRequestWrapper.createForwardRequestWrapper( request, rd ); - _servletStack.push( _servlet ); - _servlet = ((RequestDispatcherImpl) rd).getServletMetaData().getServlet(); + if (isFilterActive()) throw new IllegalStateException( "May not push a forward request when no servlet is active" ); + _contextStack.push( new ExecutionContext( DispatchedRequestWrapper.createForwardRequestWrapper( request, rd ), + response, ((RequestDispatcherImpl) rd).getServletMetaData() ) ); } public void popRequest() { - if (!( _request instanceof DispatchedRequestWrapper)) throw new IllegalStateException( "May not pop the initial request" ); - _request = ((DispatchedRequestWrapper) _request).getBaseRequest(); - _servlet = (Servlet) _servletStack.pop(); + if (getContext().mayPopFilter()) { + getContext().popFilter(); + } else if (_contextStack.size() == 1) { + throw new IllegalStateException( "May not pop the initial request" ); + } else { + _contextStack.pop(); + } } - private boolean userIsAuthorized() { - String[] roles = _application.getPermittedRoles( _requestURL ); - for (int i = 0; i < roles.length; i++) { - if (_request.isUserInRole( roles[i] )) return true; - } - return false; + public boolean isFilterActive() { + return getContext().isFilterActive(); + } + + + public Filter getFilter() throws ServletException { + return getContext().getFilter(); + } + + + public FilterChain getFilterChain() { + return new FilterChain() { + public void doFilter( ServletRequest servletRequest, ServletResponse servletResponse ) throws IOException, ServletException { + pushFilter( servletRequest, servletResponse ); + service(); + popRequest(); + } + }; + } + + + public void pushFilter( ServletRequest request, ServletResponse response ) { + getContext().pushFilter( request, response ); } @@ -161,20 +180,140 @@ InvocationContextImpl( ServletUnitClient client, ServletRunner runner, String target, WebRequest request, Dictionary clientHeaders, byte[] messageBody ) throws IOException, MalformedURLException { _client = client; _application = runner.getApplication(); - _requestURL = request.getURL(); _target = target; - final ServletUnitHttpRequest suhr = new ServletUnitHttpRequest( _application.getServletRequest( _requestURL ), request, runner.getContext(), - clientHeaders, messageBody ); - _request = suhr; + URL requestURL = request.getURL(); + final ServletUnitHttpRequest suhr = new ServletUnitHttpRequest( _application.getServletRequest( requestURL ), request, + runner.getContext(), clientHeaders, messageBody ); Cookie[] cookies = getCookies( clientHeaders ); for (int i = 0; i < cookies.length; i++) suhr.addCookie( cookies[i] ); if (_application.usesBasicAuthentication()) suhr.readBasicAuthentication(); else if (_application.usesFormAuthentication()) suhr.readFormAuthentication(); - HttpSession session = _request.getSession( /* create */ false ); + HttpSession session = suhr.getSession( /* create */ false ); if (session != null) ((ServletUnitHttpSession) session).access(); + + _effectiveURL = computeEffectiveUrl( suhr, requestURL ); + _contextStack.push( new ExecutionContext( suhr, new ServletUnitHttpResponse(), + _application.getServletRequest( _effectiveURL ) ) ); + } + + + + private URL computeEffectiveUrl( HttpServletRequest request, URL requestURL ) { + if (!_application.requiresAuthorization( requestURL ) || userIsAuthorized( request, requestURL ) ) { + return requestURL; + } else if (request.getRemoteUser() != null) { + throw new AccessDeniedException( requestURL ); + } else if (_application.usesBasicAuthentication()) { + throw new BasicAuthenticationRequiredException( _application.getAuthenticationRealm() ); + } else if (!_application.usesFormAuthentication()) { + throw new IllegalStateException( "Authorization required but no authentication method defined" ); + } else { + ((ServletUnitHttpSession) request.getSession()).setOriginalURL( requestURL ); + return _application.getLoginURL(); + } + } + + + private boolean userIsAuthorized( HttpServletRequest request, URL requestURL ) { + String[] roles = _application.getPermittedRoles( requestURL ); + for (int i = 0; i < roles.length; i++) { + if (request.isUserInRole( roles[i] )) return true; + } + return false; + } + + + + static class ExecutionContext { + + private HttpServletResponse _response; + private HttpServletRequest _request; + private ServletMetaData _metaData; + + private Stack _filterStack = new Stack(); + + + ExecutionContext( HttpServletRequest request, HttpServletResponse response, ServletMetaData metaData ) { + _request = request; + _response = response; + _metaData = metaData; + } + + + boolean isFilterActive() { + return getFilterIndex() < _metaData.getFilters().length; + } + + + Servlet getServlet() throws ServletException { + if (isFilterActive()) throw new IllegalStateException( "Current context is a filter - may not request servlet."); + return _metaData.getServlet(); + } + + + HttpServletResponse getResponse() { + return _filterStack.isEmpty() ? _response : ((FilterContext) _filterStack.lastElement()).getResponse(); + } + + + HttpServletRequest getRequest() { + return _filterStack.isEmpty() ? _request : ((FilterContext) _filterStack.lastElement()).getRequest(); + } + + + public Filter getFilter() throws ServletException { + if (!isFilterActive()) throw new IllegalStateException( "Current context is a servlet - may not request filter."); + return _metaData.getFilters()[ getFilterIndex() ].getFilter(); + } + + + public void pushFilter( ServletRequest request, ServletResponse response ) { + if (!isFilterActive()) throw new IllegalStateException( "Current context is a servlet - may not push filter."); + if (!(request instanceof HttpServletRequest)) throw new IllegalArgumentException( "HttpUnit does not support non-HTTP request wrappers" ); + if (!(response instanceof HttpServletResponse)) throw new IllegalArgumentException( "HttpUnit does not support non-HTTP response wrappers" ); + + _filterStack.push( new FilterContext( (HttpServletRequest) request, (HttpServletResponse) response ) ); + } + + + public boolean mayPopFilter() { + return getFilterIndex() > 0; + } + + + public void popFilter() { + _filterStack.pop(); + } + + + private int getFilterIndex() { + return _filterStack.size(); + } + } + + + static class FilterContext { + HttpServletRequest _request; + HttpServletResponse _response; + + + public FilterContext( HttpServletRequest request, HttpServletResponse response ) { + _request = request; + _response = response; + } + + + public HttpServletResponse getResponse() { + return _response; + } + + + public HttpServletRequest getRequest() { + return _request; + } } @@ -184,16 +323,10 @@ final private static Cookie[] NO_COOKIES = new Cookie[0]; - private ServletUnitClient _client; - + private ServletUnitClient _client; private WebApplication _application; - private HttpServletRequest _request; - private ServletUnitHttpResponse _response = new ServletUnitHttpResponse(); - private URL _requestURL; - private Stack _servletStack = new Stack(); private String _target; - private Servlet _servlet; private WebResponse _webResponse; @@ -212,4 +345,9 @@ } return (Cookie[]) cookies.toArray( new Cookie[ cookies.size() ] ); } + + + private ExecutionContext getContext() { + return (ExecutionContext) _contextStack.lastElement(); + } } \ No newline at end of file Index: ServletMetaData.java =================================================================== RCS file: /cvsroot/httpunit/httpunit/src/com/meterware/servletunit/ServletMetaData.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- ServletMetaData.java 13 Feb 2003 20:25:55 -0000 1.1 +++ ServletMetaData.java 26 Feb 2004 17:36:47 -0000 1.2 @@ -21,6 +21,7 @@ *******************************************************************************************************************/ import javax.servlet.Servlet; import javax.servlet.ServletException; +import java.util.List; /** @@ -45,4 +46,10 @@ */ String getPathInfo(); + + /** + * Returns an ordered list of the filters associated with this servlet. + */ + FilterMetaData[] getFilters(); + } Index: ServletUnitClient.java =================================================================== RCS file: /cvsroot/httpunit/httpunit/src/com/meterware/servletunit/ServletUnitClient.java,v retrieving revision 1.11 retrieving revision 1.12 diff -u -r1.11 -r1.12 --- ServletUnitClient.java 27 Feb 2003 23:36:03 -0000 1.11 +++ ServletUnitClient.java 26 Feb 2004 17:36:47 -0000 1.12 @@ -91,7 +91,7 @@ try { InvocationContext invocation = newInvocation( request ); - invocation.getServlet().service( invocation.getRequest(), invocation.getResponse() ); + invocation.service(); return invocation.getServletResponse(); } catch (ServletException e) { throw new HttpInternalErrorException( request.getURL(), e ); Index: ServletUnitHttpRequest.java =================================================================== RCS file: /cvsroot/httpunit/httpunit/src/com/meterware/servletunit/ServletUnitHttpRequest.java,v retrieving revision 1.29 retrieving revision 1.30 diff -u -r1.29 -r1.30 --- ServletUnitHttpRequest.java 24 Mar 2003 03:22:10 -0000 1.29 +++ ServletUnitHttpRequest.java 26 Feb 2004 17:36:47 -0000 1.30 @@ -2,7 +2,7 @@ /******************************************************************************************************************** * $Id$ * -* Copyright (c) 2000-2003, Russell Gold +* Copyright (c) 2000-2004, Russell Gold * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated * documentation files (the "Software"), to deal in the Software without restriction, including without limitation @@ -27,7 +27,6 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; -import java.net.URL; import java.util.*; import javax.servlet.http.HttpServletRequest; @@ -692,11 +691,6 @@ } - void writeFormAuthentication( String userName, String password ) { - getServletSession().setUserInformation( userName, toArray( password ) ); - } - - private ServletUnitHttpSession getServletSession() { return (ServletUnitHttpSession) getSession(); } @@ -720,7 +714,7 @@ } } - private String[] toArray( String roleList ) { + static String[] toArray( String roleList ) { StringTokenizer st = new StringTokenizer( roleList, "," ); String[] result = new String[ st.countTokens() ]; for (int i = 0; i < result.length; i++) { @@ -730,16 +724,6 @@ } - void setOriginalURL( URL originalURL ) { - getServletSession().setOriginalURL( originalURL ); - } - - - URL getOriginalURL() { - return getServletSession().getOriginalURL(); - } - - void recordAuthenticationInfo( String userName, String[] roles ) { _userName = userName; _roles = roles; Index: WebApplication.java =================================================================== RCS file: /cvsroot/httpunit/httpunit/src/com/meterware/servletunit/WebApplication.java,v retrieving revision 1.22 retrieving revision 1.23 diff -u -r1.22 -r1.23 --- WebApplication.java 15 Feb 2004 06:42:01 -0000 1.22 +++ WebApplication.java 26 Feb 2004 17:36:47 -0000 1.23 @@ -33,7 +33,6 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; -import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; @@ -52,10 +51,16 @@ private final ServletConfiguration SECURITY_CHECK_CONFIGURATION = new ServletConfiguration( SecurityCheckServlet.class.getName() ); - private final ServletMapping SECURITY_CHECK_MAPPING = new ServletMapping( SECURITY_CHECK_CONFIGURATION ); + private final WebResourceMapping SECURITY_CHECK_MAPPING = new WebResourceMapping( SECURITY_CHECK_CONFIGURATION ); - /** A mapping of resource names to servlet class names. **/ - private ServletMap _servletMapping = new ServletMap(); + /** A mapping of resource names to servlet configurations. **/ + private WebResourceMap _servletMapping = new WebResourceMap(); + + /** A mapping of resource names to filter configurations. **/ + private FilterUrlMap _filterUrlMapping = new FilterUrlMap(); + + /** A mapping of servlet names to filter configurations. **/ + private Hashtable _filterMapping = new Hashtable(); private ArrayList _securityConstraints = new ArrayList(); @@ -118,6 +123,7 @@ _contextDir = file; _contextPath = contextPath == null ? "" : contextPath; registerServlets( document ); + registerFilters( document ); extractSecurityConstraints( document ); extractContextParameters( document ); extractLoginConfiguration( document ); @@ -130,7 +136,7 @@ private void extractListeners( Document document ) throws SAXException { NodeList nl = document.getElementsByTagName( "listener" ); for (int i = 0; i < nl.getLength(); i++) { - String listenerName = getChildNodeValue((Element) nl.item(i), "listener-class").trim(); + String listenerName = XMLUtils.getChildNodeValue((Element) nl.item(i), "listener-class").trim(); try { Object listener = Class.forName( listenerName ).newInstance(); @@ -245,7 +251,7 @@ * Calls the destroy method for every active servlet. */ void destroyServlets() { - _servletMapping.destroyServlets(); + _servletMapping.destroyWebResources(); } @@ -398,20 +404,62 @@ //--------------------------------------------------- private members -------------------------------------------------- + private void registerFilters( Document document ) throws SAXException { + Hashtable nameToClass = new Hashtable(); + NodeList nl = document.getElementsByTagName( "filter" ); + for (int i = 0; i < nl.getLength(); i++) registerFilterClass( nameToClass, (Element) nl.item( i ) ); + nl = document.getElementsByTagName( "filter-mapping" ); + for (int i = 0; i < nl.getLength(); i++) registerFilter( nameToClass, (Element) nl.item( i ) ); + } + + + private void registerFilterClass( Dictionary mapping, Element filterElement ) throws SAXException { + String filterName = XMLUtils.getChildNodeValue( filterElement, "filter-name" ); + mapping.put( filterName, new FilterConfiguration( filterName, filterElement ) ); + } + + + private void registerFilter( Dictionary mapping, Element filterElement ) throws SAXException { + if (XMLUtils.hasChildNode( filterElement, "servlet-name" )) { + registerFilterForServlet( XMLUtils.getChildNodeValue( filterElement, "servlet-name" ), + (FilterConfiguration) mapping.get( XMLUtils.getChildNodeValue( filterElement, "filter-name" ) ) ); + } + if (XMLUtils.hasChildNode( filterElement, "url-pattern" )) { + registerFilterForUrl( XMLUtils.getChildNodeValue( filterElement, "url-pattern" ), + (FilterConfiguration) mapping.get( XMLUtils.getChildNodeValue( filterElement, "filter-name" ) ) ); + } + } + + + private void registerFilterForUrl( String resourceName, FilterConfiguration filterConfiguration ) { + _filterUrlMapping.put( resourceName, filterConfiguration ); + } + + + private void registerFilterForServlet( String servletName, FilterConfiguration filterConfiguration ) { + List list = (List) _filterMapping.get( servletName ); + if (list == null) { + list = new ArrayList(); + _filterMapping.put( servletName, list ); + } + list.add( filterConfiguration ); + } + + private void extractLoginConfiguration( Document document ) throws MalformedURLException, SAXException { NodeList nl = document.getElementsByTagName( "login-config" ); if (nl.getLength() == 1) { final Element loginConfigElement = (Element) nl.item( 0 ); - String authenticationMethod = getChildNodeValue( loginConfigElement, "auth-method", "BASIC" ); - _authenticationRealm = getChildNodeValue( loginConfigElement, "realm-name", "" ); + String authenticationMethod = XMLUtils.getChildNodeValue( loginConfigElement, "auth-method", "BASIC" ); + _authenticationRealm = XMLUtils.getChildNodeValue( loginConfigElement, "realm-name", "" ); if (authenticationMethod.equalsIgnoreCase( "BASIC" )) { _useBasicAuthentication = true; if (_authenticationRealm.length() == 0) throw new SAXException( "No realm specified for BASIC Authorization" ); } else if (authenticationMethod.equalsIgnoreCase( "FORM" )) { _useFormAuthentication = true; if (_authenticationRealm.length() == 0) throw new SAXException( "No realm specified for FORM Authorization" ); - _loginURL = new URL( "http", "localhost", getChildNodeValue( loginConfigElement, "form-login-page" ) ); - _errorURL = new URL( "http", "localhost", getChildNodeValue( loginConfigElement, "form-error-page" ) ); + _loginURL = new URL( "http", "localhost", XMLUtils.getChildNodeValue( loginConfigElement, "form-login-page" ) ); + _errorURL = new URL( "http", "localhost", XMLUtils.getChildNodeValue( loginConfigElement, "form-error-page" ) ); } } } @@ -427,14 +475,14 @@ private void registerServletClass( Dictionary mapping, Element servletElement ) throws SAXException { - mapping.put( getChildNodeValue( servletElement, "servlet-name" ), + mapping.put( XMLUtils.getChildNodeValue( servletElement, "servlet-name" ), new ServletConfiguration( servletElement ) ); } private void registerServlet( Dictionary mapping, Element servletElement ) throws SAXException { - registerServlet( getChildNodeValue( servletElement, "url-pattern" ), - (ServletConfiguration) mapping.get( getChildNodeValue( servletElement, "servlet-name" ) ) ); + registerServlet( XMLUtils.getChildNodeValue( servletElement, "url-pattern" ), + (ServletConfiguration) mapping.get( XMLUtils.getChildNodeValue( servletElement, "servlet-name" ) ) ); } @@ -442,38 +490,13 @@ NodeList nl = document.getElementsByTagName( "context-param" ); for (int i = 0; i < nl.getLength(); i++) { Element param = (Element) nl.item( i ); - String name = getChildNodeValue( param, "param-name" ); - String value = getChildNodeValue( param, "param-value" ); + String name = XMLUtils.getChildNodeValue( param, "param-name" ); + String value = XMLUtils.getChildNodeValue( param, "param-value" ); _contextParameters.put( name, value ); } } - private static String getChildNodeValue( Element root, String childNodeName ) throws SAXException { - return getChildNodeValue( root, childNodeName, null ); - } - - - private static String getChildNodeValue( Element root, String childNodeName, String defaultValue ) throws SAXException { - NodeList nl = root.getElementsByTagName( childNodeName ); - if (nl.getLength() == 1) { - return getTextValue( nl.item( 0 ) ).trim(); - } else if (defaultValue == null) { - throw new SAXException( "Node <" + root.getNodeName() + "> has no child named <" + childNodeName + ">" ); - } else { - return defaultValue; - } - } - - - private static String getTextValue( Node node ) throws SAXException { - Node textNode = node.getFirstChild(); - if (textNode == null) return ""; - if (textNode.getNodeType() != Node.TEXT_NODE) throw new SAXException( "No text value found for <" + node.getNodeName() + "> node" ); - return textNode.getNodeValue(); - } - - private static boolean patternMatches( String urlPattern, String urlPath ) { return urlPattern.equals( urlPath ); } @@ -485,57 +508,58 @@ static class SecurityCheckServlet extends HttpServlet { protected void doGet( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException { - handleLogin( (ServletUnitHttpRequest) req, resp ); + handleLogin( req, resp ); } protected void doPost( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException { - handleLogin( (ServletUnitHttpRequest) req, resp ); + handleLogin( req, resp ); } - private void handleLogin( ServletUnitHttpRequest req, HttpServletResponse resp ) throws IOException { + private void handleLogin( HttpServletRequest req, HttpServletResponse resp ) throws IOException { final String username = req.getParameter( "j_username" ); - final String password = req.getParameter( "j_password" ); - req.writeFormAuthentication( username, password ); - resp.sendRedirect( req.getOriginalURL().toExternalForm() ); + final String roleList = req.getParameter( "j_password" ); + getServletSession( req ).setUserInformation( username, ServletUnitHttpRequest.toArray( roleList ) ); + resp.sendRedirect( getServletSession( req ).getOriginalURL().toExternalForm() ); + } + + + private ServletUnitHttpSession getServletSession( HttpServletRequest req ) { + return (ServletUnitHttpSession) req.getSession(); } } + + //============================================= ServletConfiguration class ============================================= final static int DONT_AUTOLOAD = Integer.MIN_VALUE; final static int ANY_LOAD_ORDER = Integer.MAX_VALUE; - class ServletConfiguration { + class ServletConfiguration extends WebResourceConfiguration { private Servlet _servlet; - private String _className; - private Hashtable _initParams = new Hashtable(); + private String _servletName; private int _loadOrder = DONT_AUTOLOAD; ServletConfiguration( String className ) { - _className = className; + super( className ); } ServletConfiguration( String className, Hashtable initParams ) { - _className = className; - if (initParams != null) _initParams = initParams; + super( className, initParams ); } ServletConfiguration( Element servletElement ) throws SAXException { - this( getChildNodeValue( servletElement, "servlet-class" ) ); - final NodeList initParams = servletElement.getElementsByTagName( "init-param" ); - for (int i = initParams.getLength() - 1; i >= 0; i--) { - _initParams.put( getChildNodeValue( (Element) initParams.item( i ), "param-name" ), - getChildNodeValue( (Element) initParams.item( i ), "param-value" ) ); - } + super( servletElement, "servlet-class" ); + _servletName = XMLUtils.getChildNodeValue( servletElement, "servlet-name" ); final NodeList loadOrder = servletElement.getElementsByTagName( "load-on-startup" ); for (int i = 0; i < loadOrder.getLength(); i++) { - String order = getTextValue( loadOrder.item(i) ); + String order = XMLUtils.getTextValue( loadOrder.item(i) ); try { _loadOrder = Integer.parseInt( order ); } catch (NumberFormatException e) { @@ -556,18 +580,13 @@ } - synchronized void destroyServlet() { + synchronized void destroyResource() { if (_servlet != null) _servlet.destroy(); } - String getClassName() { - return _className; - } - - - Hashtable getInitParams() { - return _initParams; + String getServletName() { + return _servletName; } @@ -582,6 +601,53 @@ } +//============================================= FilterConfiguration class ============================================= + + + class FilterConfiguration extends WebResourceConfiguration implements FilterMetaData { + + private Filter _filter; + private String _name; + + + FilterConfiguration( String name, Element filterElement ) throws SAXException { + super( filterElement, "filter-class" ); + _name = name; + } + + + public synchronized Filter getFilter() throws ServletException { + try { + if (_filter == null) { + Class filterClass = Class.forName( getClassName() ); + _filter = (Filter) filterClass.newInstance(); + _filter.init( new FilterConfigImpl( _name, getServletContext(), getInitParams() ) ); + } + + return _filter; + } catch (ClassNotFoundException e) { + throw new ServletException( "Did not find filter class: " + getClassName() ); + } catch (IllegalAccessException e) { + throw new ServletException( "Filter class " + getClassName() + " lacks a public no-arg constructor" ); + } catch (InstantiationException e) { + throw new ServletException( "Filter class " + getClassName() + " instantiation threw" + e.getCause() ); + } catch (ClassCastException e) { + throw new ServletException( "Filter class " + getClassName() + " does not implement" + Filter.class.getName() ); + } + } + + + boolean isLoadOnStartup() { + return false; + } + + + synchronized void destroyResource() { + if (_filter != null) _filter.destroy(); + } + } + + //=================================== SecurityConstract interface and implementations ================================== @@ -614,7 +680,7 @@ SecurityConstraintImpl( Element root ) throws SAXException { final NodeList roleNames = root.getElementsByTagName( "role-name" ); - for (int i = 0; i < roleNames.getLength(); i++) _roleList.add( getTextValue( roleNames.item( i ) ) ); + for (int i = 0; i < roleNames.getLength(); i++) _roleList.add( XMLUtils.getTextValue( roleNames.item( i ) ) ); final NodeList resources = root.getElementsByTagName( "web-resource-collection" ); for (int i = 0; i < resources.getLength(); i++) _resources.add( new WebResourceCollection( (Element) resources.item( i ) ) ); @@ -652,7 +718,7 @@ WebResourceCollection( Element root ) throws SAXException { final NodeList urlPatterns = root.getElementsByTagName( "url-pattern" ); - for (int i = 0; i < urlPatterns.getLength(); i++) _urlPatterns.add( getTextValue( urlPatterns.item( i ) ) ); + for (int i = 0; i < urlPatterns.getLength(); i++) _urlPatterns.add( XMLUtils.getTextValue( urlPatterns.item( i ) ) ); } @@ -670,17 +736,24 @@ } + static final FilterMetaData[] NO_FILTERS = new FilterMetaData[0]; + + static class ServletRequestImpl implements ServletMetaData { - private URL _url; - private String _servletName; - private ServletMapping _mapping; + private URL _url; + private String _fullServletPath; + private WebResourceMapping _mapping; + private Hashtable _filtersPerName; + private FilterUrlMap _filtersPerUrl; - ServletRequestImpl( URL url, String servletName, ServletMapping mapping ) { - _url = url; - _servletName = servletName; - _mapping = mapping; + ServletRequestImpl( URL url, String servletPath, WebResourceMapping mapping, Hashtable filtersPerName, FilterUrlMap filtersPerUrl ) { + _url = url; + _fullServletPath = servletPath; + _mapping = mapping; + _filtersPerName = filtersPerName; + _filtersPerUrl = filtersPerUrl; } @@ -702,102 +775,138 @@ public String getServletPath() { - return _mapping == null ? null : _mapping.getServletPath( _servletName ); + return _mapping == null ? null : _mapping.getServletPath( _fullServletPath ); } public String getPathInfo() { - return _mapping == null ? null : _mapping.getPathInfo( _servletName ); + return _mapping == null ? null : _mapping.getPathInfo( _fullServletPath ); + } + + + public FilterMetaData[] getFilters() { + if (getConfiguration() == null) return NO_FILTERS; + + List filters = new ArrayList(); + addFiltersForPath( filters, _fullServletPath ); + addFiltersForServletWithName( filters, getConfiguration().getServletName() ); + + return (FilterMetaData[]) filters.toArray( new FilterMetaData[ filters.size() ]); + } + + + private void addFiltersForPath( List filters, String fullServletPath ) { + FilterMetaData[] matches = _filtersPerUrl.getMatchingFilters( fullServletPath ); + for (int i = 0; i < matches.length; i++) { + filters.add( matches[i] ); + } + } + + + private void addFiltersForServletWithName( List filters, String servletName ) { + if (servletName == null) return; + List matches = (List) _filtersPerName.get( servletName ); + if (matches != null) filters.addAll( matches ); } private ServletConfiguration getConfiguration() { - return _mapping == null ? null : _mapping.getConfiguration(); + return _mapping == null ? null : (ServletConfiguration) _mapping.getConfiguration(); } } - static class ServletMapping { + static class WebResourceMapping { - private ServletConfiguration _configuration; + private WebResourceConfiguration _configuration; - ServletConfiguration getConfiguration() { + WebResourceConfiguration getConfiguration() { return _configuration; } - ServletMapping( ServletConfiguration configuration ) { + WebResourceMapping( WebResourceConfiguration configuration ) { _configuration = configuration; } - String getServletPath( String servletName ) { - return servletName; + /** + * Returns the portion of the request path which was actually used to select the servlet. This default + * implementation returns the full specified path. + * @param requestPath the full path of the request, relative to the application root. + */ + String getServletPath( String requestPath ) { + return requestPath; } - String getPathInfo( String servletName ) { + /** + * Returns the portion of the request path which was not used to select the servlet, and can be + * used as data by the servlet. This default implementation returns null. + * @param requestPath the full path of the request, relative to the application root. + */ + String getPathInfo( String requestPath ) { return null; } - public void destroyServlet() { - getConfiguration().destroyServlet(); + public void destroyResource() { + getConfiguration().destroyResource(); } } - static class PartialMatchServletMapping extends ServletMapping { + static class PartialMatchWebResourceMapping extends WebResourceMapping { private String _prefix; - public PartialMatchServletMapping( ServletConfiguration configuration, String prefix ) { + public PartialMatchWebResourceMapping( WebResourceConfiguration configuration, String prefix ) { super( configuration ); if (!prefix.endsWith( "/*" )) throw new IllegalArgumentException( prefix + " does not end with '/*'" ); _prefix = prefix.substring( 0, prefix.length()-2 ); } - String getServletPath( String servletName ) { + String getServletPath( String requestPath ) { return _prefix; } - String getPathInfo( String servletName ) { - return servletName.length() > _prefix.length() - ? servletName.substring( _prefix.length() ) + String getPathInfo( String requestPath ) { + return requestPath.length() > _prefix.length() + ? requestPath.substring( _prefix.length() ) : null; } } /** - * A utility class for mapping servlets to url patterns. This implements the + * A utility class for mapping web resources to url patterns. This implements the * matching algorithm documented in section 10 of the JSDK-2.2 reference. */ - class ServletMap { + class WebResourceMap { private final Map _exactMatches = new HashMap(); private final Map _extensions = new HashMap(); private final Map _urlTree = new HashMap(); - private ServletMapping _defaultMapping; + private WebResourceMapping _defaultMapping; - void put( String mapping, ServletConfiguration servletConfiguration ) { + void put( String mapping, WebResourceConfiguration configuration ) { if (mapping.equals( "/" )) { - _defaultMapping = new ServletMapping( servletConfiguration ); + _defaultMapping = new WebResourceMapping( configuration ); } else if (mapping.startsWith( "*." )) { - _extensions.put( mapping.substring( 2 ), new ServletMapping( servletConfiguration ) ); + _extensions.put( mapping.substring( 2 ), new WebResourceMapping( configuration ) ); } else if (!mapping.startsWith( "/" ) || !mapping.endsWith( "/*" )) { - _exactMatches.put( mapping, new ServletMapping( servletConfiguration ) ); + _exactMatches.put( mapping, new WebResourceMapping( configuration ) ); } else { ParsedPath path = new ParsedPath( mapping ); Map context = _urlTree; while (path.hasNext()) { String part = path.next(); if (part.equals( "*" )) { - context.put( "*", new PartialMatchServletMapping( servletConfiguration, mapping ) ); + context.put( "*", new PartialMatchWebResourceMapping( configuration, mapping ) ); return; } if (!context.containsKey( part )) { @@ -813,17 +922,17 @@ String file = url.getFile(); if (!file.startsWith( _contextPath )) throw new HttpNotFoundException( "File path does not begin with '" + _contextPath + "'", url ); - String servletName = getServletName( file.substring( _contextPath.length() ) ); + String servletPath = getServletPath( file.substring( _contextPath.length() ) ); - if (servletName.endsWith( "j_security_check" )) { - return new ServletRequestImpl( url, servletName, SECURITY_CHECK_MAPPING ); + if (servletPath.endsWith( "j_security_check" )) { + return new ServletRequestImpl( url, servletPath, SECURITY_CHECK_MAPPING, _filterMapping, _filterUrlMapping ); } else { - return new ServletRequestImpl( url, servletName, getMapping( servletName ) ); + return new ServletRequestImpl( url, servletPath, getMapping( servletPath ), _filterMapping, _filterUrlMapping ); } } - private String getServletName( String urlFile ) { + private String getServletPath( String urlFile ) { if (urlFile.indexOf( '?' ) < 0) { return urlFile; } else { @@ -832,22 +941,22 @@ } - public void destroyServlets() { - if (_defaultMapping != null) _defaultMapping.destroyServlet(); - destroyServlets( _exactMatches ); - destroyServlets( _extensions ); - destroyServlets( _urlTree ); + public void destroyWebResources() { + if (_defaultMapping != null) _defaultMapping.destroyResource(); + destroyWebResources( _exactMatches ); + destroyWebResources( _extensions ); + destroyWebResources( _urlTree ); } - private void destroyServlets( Map map ) { + private void destroyWebResources( Map map ) { for (Iterator iterator = map.values().iterator(); iterator.hasNext();) { Object o = iterator.next(); - if (o instanceof ServletMapping) { - ServletMapping servletMapping = (ServletMapping) o; - servletMapping.destroyServlet(); + if (o instanceof WebResourceMapping) { + WebResourceMapping webResourceMapping = (WebResourceMapping) o; + webResourceMapping.destroyResource(); } else { - destroyServlets( (Map) o ); + destroyWebResources( (Map) o ); } } } @@ -883,8 +992,8 @@ private void collectAutoLoadableServlets( Collection collection, Map map ) { for (Iterator iterator = map.values().iterator(); iterator.hasNext();) { Object o = iterator.next(); - if (o instanceof ServletMapping) { - ServletMapping servletMapping = (ServletMapping) o; + if (o instanceof WebResourceMapping) { + WebResourceMapping servletMapping = (WebResourceMapping) o; if (servletMapping.getConfiguration().isLoadOnStartup()) collection.add( servletMapping.getConfiguration() ); } else { collectAutoLoadableServlets( collection, (Map) o ); @@ -893,15 +1002,15 @@ } - private ServletMapping getMapping( String url ) { - if (_exactMatches.containsKey( url )) return (ServletMapping) _exactMatches.get( url ); + private WebResourceMapping getMapping( String url ) { + if (_exactMatches.containsKey( url )) return (WebResourceMapping) _exactMatches.get( url ); Map context = getContextForLongestPathPrefix( url ); - if (context.containsKey( "*" )) return (ServletMapping) context.get( "*" ); + if (context.containsKey( "*" )) return (WebResourceMapping) context.get( "*" ); - if (_extensions.containsKey( getExtension( url ))) return (ServletMapping) _extensions.get( getExtension( url ) ); + if (_extensions.containsKey( getExtension( url ))) return (WebResourceMapping) _extensions.get( getExtension( url ) ); - if (_urlTree.containsKey( "/" )) return (ServletMapping) _urlTree.get( "/" ); + if (_urlTree.containsKey( "/" )) return (WebResourceMapping) _urlTree.get( "/" ); if (_defaultMapping != null) return _defaultMapping; @@ -911,7 +1020,7 @@ String className = url.substring( prefix.length() ); try { Class.forName( className ); - return new ServletMapping( new ServletConfiguration( className ) ); + return new WebResourceMapping( new ServletConfiguration( className ) ); } catch (ClassNotFoundException e) { return null; } |