com.meterware.servletunit.WebApplication Maven / Gradle / Ivy
The newest version!
package com.meterware.servletunit;
/********************************************************************************************************************
* $Id: WebApplication.java 858 2008-03-31 09:58:35Z wolfgang_fahl $
*
* Copyright (c) 2001-2004, 2006,2008 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
* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
* to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
* THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*******************************************************************************************************************/
import com.meterware.httpunit.HttpInternalErrorException;
import com.meterware.httpunit.HttpNotFoundException;
import com.meterware.httpunit.HttpUnitUtils;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* This class represents the information recorded about a single web
* application. It is usually extracted from web.xml.
*
* @author Russell Gold
* @author Donald Ball
* @author Jay Dunning
**/
class WebApplication implements SessionListenerDispatcher {
private final static SecurityConstraint NULL_SECURITY_CONSTRAINT = new NullSecurityConstraint();
private final ServletConfiguration SECURITY_CHECK_CONFIGURATION = new ServletConfiguration( SecurityCheckServlet.class.getName() );
private final WebResourceMapping SECURITY_CHECK_MAPPING = new WebResourceMapping( SECURITY_CHECK_CONFIGURATION );
/** 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();
private ArrayList _contextListeners = new ArrayList();
private ArrayList _contextAttributeListeners = new ArrayList();
private ArrayList _sessionListeners = new ArrayList();
private ArrayList _sessionAttributeListeners = new ArrayList();
private boolean _useBasicAuthentication;
private boolean _useFormAuthentication;
private String _authenticationRealm = "";
private URL _loginURL;
private URL _errorURL;
private Hashtable _contextParameters = new Hashtable();
private File _contextDir = null;
private String _contextPath = null;
private ServletUnitServletContext _servletContext;
private String _displayName;
/**
* Constructs a default application spec with no information.
*/
WebApplication() {
_contextPath = "";
}
/**
* Constructs an application spec from an XML document.
*/
WebApplication( Document document ) throws MalformedURLException, SAXException {
this( document, null, "" );
}
/**
* Constructs an application spec from an XML document.
*/
WebApplication( Document document, String contextPath ) throws MalformedURLException, SAXException {
this( document, null, contextPath );
}
/**
* Constructs an application spec from an XML document.
*/
WebApplication( Document document, File file, String contextPath ) throws MalformedURLException, SAXException {
if (contextPath != null && contextPath.length() > 0 && !contextPath.startsWith( "/" )) throw new IllegalArgumentException( "Context path " + contextPath + " must start with '/'" );
_contextDir = file;
_contextPath = contextPath == null ? "" : contextPath;
NodeList nl = document.getElementsByTagName( "display-name" );
if (nl.getLength() > 0) _displayName = XMLUtils.getTextValue( nl.item(0) ).trim();
registerServlets( document );
registerFilters( document );
extractSecurityConstraints( document );
extractContextParameters( document );
extractLoginConfiguration( document );
extractListeners( document );
notifyContextInitialized();
_servletMapping.autoLoadServlets();
}
private void extractListeners( Document document ) throws SAXException {
NodeList nl = document.getElementsByTagName( "listener" );
for (int i = 0; i < nl.getLength(); i++) {
String listenerName = XMLUtils.getChildNodeValue((Element) nl.item(i), "listener-class").trim();
try {
Object listener = Class.forName( listenerName ).newInstance();
if (listener instanceof ServletContextListener) _contextListeners.add( listener );
if (listener instanceof ServletContextAttributeListener) _contextAttributeListeners.add( listener );
if (listener instanceof HttpSessionListener) _sessionListeners.add( listener );
if (listener instanceof HttpSessionAttributeListener) _sessionAttributeListeners.add( listener );
} catch (Throwable e) {
throw new RuntimeException("Unable to load context listener " + listenerName + ": " + e.toString() );
}
}
}
private void notifyContextInitialized() {
ServletContextEvent event = new ServletContextEvent( getServletContext() );
for (Iterator i = _contextListeners.iterator(); i.hasNext();) {
ServletContextListener listener = (ServletContextListener) i.next();
listener.contextInitialized( event );
}
}
void shutDown() {
destroyServlets();
notifyContextDestroyed();
}
private void notifyContextDestroyed() {
ServletContextEvent event = new ServletContextEvent( getServletContext() );
for (ListIterator i = _contextListeners.listIterator( _contextListeners.size() ); i.hasPrevious();) {
ServletContextListener listener = (ServletContextListener) i.previous();
listener.contextDestroyed( event );
}
}
void sendAttributeAdded( String name, Object value ) {
ServletContextAttributeEvent event = new ServletContextAttributeEvent( getServletContext(), name, value );
for (Iterator i = _contextAttributeListeners.iterator(); i.hasNext();) {
ServletContextAttributeListener listener = (ServletContextAttributeListener) i.next();
listener.attributeAdded( event );
}
}
void sendAttributeReplaced( String name, Object value ) {
ServletContextAttributeEvent event = new ServletContextAttributeEvent( getServletContext(), name, value );
for (Iterator i = _contextAttributeListeners.iterator(); i.hasNext();) {
ServletContextAttributeListener listener = (ServletContextAttributeListener) i.next();
listener.attributeReplaced( event );
}
}
void sendAttributeRemoved( String name, Object value ) {
ServletContextAttributeEvent event = new ServletContextAttributeEvent( getServletContext(), name, value );
for (Iterator i = _contextAttributeListeners.iterator(); i.hasNext();) {
ServletContextAttributeListener listener = (ServletContextAttributeListener) i.next();
listener.attributeRemoved( event );
}
}
private void extractSecurityConstraints( Document document ) throws SAXException {
NodeList nl = document.getElementsByTagName( "security-constraint" );
for (int i = 0; i < nl.getLength(); i++) {
_securityConstraints.add( new SecurityConstraintImpl( (Element) nl.item( i ) ) );
}
}
String getContextPath() {
return _contextPath;
}
ServletContext getServletContext() {
if (_servletContext == null) {
_servletContext = new ServletUnitServletContext( this );
}
return _servletContext;
}
/**
* Registers a servlet class to be run.
**/
void registerServlet( String resourceName, String servletClassName, Hashtable initParams ) {
registerServlet( resourceName, new ServletConfiguration( servletClassName, initParams ) );
}
/**
* Registers a servlet to be run.
**/
void registerServlet( String resourceName, ServletConfiguration servletConfiguration ) {
// FIXME - shouldn't everything start with one or the other?
if (!resourceName.startsWith( "/" ) && !resourceName.startsWith( "*" )) {
resourceName = "/" + resourceName;
}
_servletMapping.put( resourceName, servletConfiguration );
}
/**
* Calls the destroy method for every active servlet.
*/
void destroyServlets() {
_servletMapping.destroyWebResources();
}
ServletMetaData getServletRequest( URL url ) {
return _servletMapping.get( url );
}
/**
* Returns true if this application uses Basic Authentication.
*/
boolean usesBasicAuthentication() {
return _useBasicAuthentication;
}
/**
* Returns true if this application uses form-based authentication.
*/
boolean usesFormAuthentication() {
return _useFormAuthentication;
}
String getAuthenticationRealm() {
return _authenticationRealm;
}
URL getLoginURL() {
return _loginURL;
}
URL getErrorURL() {
return _errorURL;
}
/**
* Returns true if the specified path may only be accesses by an authorized user.
* @param url the application-relative path of the URL
*/
boolean requiresAuthorization( URL url ) {
String result;
String file = url.getFile();
if (_contextPath.equals( "" )) {
result = file;
} else if (file.startsWith( _contextPath )) {
result = file.substring( _contextPath.length() );
} else {
result = null;
}
return getControllingConstraint( result ) != NULL_SECURITY_CONSTRAINT;
}
/**
* Returns an array containing the roles permitted to access the specified URL.
*/
String[] getPermittedRoles( URL url ) {
String result;
String file = url.getFile();
if (_contextPath.equals( "" )) {
result = file;
} else if (file.startsWith( _contextPath )) {
result = file.substring( _contextPath.length() );
} else {
result = null;
}
return getControllingConstraint( result ).getPermittedRoles();
}
private SecurityConstraint getControllingConstraint( String urlPath ) {
for (Iterator i = _securityConstraints.iterator(); i.hasNext();) {
SecurityConstraint sc = (SecurityConstraint) i.next();
if (sc.controlsPath( urlPath )) return sc;
}
return NULL_SECURITY_CONSTRAINT;
}
File getResourceFile( String path ) {
String relativePath = path.startsWith( "/" ) ? path.substring(1) : path;
if (_contextDir == null) {
return new File( relativePath );
} else {
return new File( _contextDir, relativePath );
}
}
Hashtable getContextParameters() {
return _contextParameters;
}
//---------------------------------------- SessionListenerDispatcher methods -------------------------------------------
public void sendSessionCreated( HttpSession session ) {
HttpSessionEvent event = new HttpSessionEvent( session );
for (Iterator i = _sessionListeners.iterator(); i.hasNext();) {
HttpSessionListener listener = (HttpSessionListener) i.next();
listener.sessionCreated( event );
}
}
public void sendSessionDestroyed( HttpSession session ) {
HttpSessionEvent event = new HttpSessionEvent( session );
for (Iterator i = _sessionListeners.iterator(); i.hasNext();) {
HttpSessionListener listener = (HttpSessionListener) i.next();
listener.sessionDestroyed( event );
}
}
public void sendAttributeAdded( HttpSession session, String name, Object value ) {
HttpSessionBindingEvent event = new HttpSessionBindingEvent( session, name, value );
for (Iterator i = _sessionAttributeListeners.iterator(); i.hasNext();) {
HttpSessionAttributeListener listener = (HttpSessionAttributeListener) i.next();
listener.attributeAdded( event );
}
}
public void sendAttributeReplaced( HttpSession session, String name, Object oldValue ) {
HttpSessionBindingEvent event = new HttpSessionBindingEvent( session, name, oldValue );
for (Iterator i = _sessionAttributeListeners.iterator(); i.hasNext();) {
HttpSessionAttributeListener listener = (HttpSessionAttributeListener) i.next();
listener.attributeReplaced( event );
}
}
public void sendAttributeRemoved( HttpSession session, String name, Object oldValue ) {
HttpSessionBindingEvent event = new HttpSessionBindingEvent( session, name, oldValue );
for (Iterator i = _sessionAttributeListeners.iterator(); i.hasNext();) {
HttpSessionAttributeListener listener = (HttpSessionAttributeListener) i.next();
listener.attributeRemoved( event );
}
}
//--------------------------------------------------- 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 = 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", _contextPath + XMLUtils.getChildNodeValue( loginConfigElement, "form-login-page" ) );
_errorURL = new URL( "http", "localhost", _contextPath + XMLUtils.getChildNodeValue( loginConfigElement, "form-error-page" ) );
}
}
}
private void registerServlets( Document document ) throws SAXException {
Hashtable nameToClass = new Hashtable();
NodeList nl = document.getElementsByTagName( "servlet" );
for (int i = 0; i < nl.getLength(); i++) registerServletClass( nameToClass, (Element) nl.item( i ) );
nl = document.getElementsByTagName( "servlet-mapping" );
for (int i = 0; i < nl.getLength(); i++) registerServlet( nameToClass, (Element) nl.item( i ) );
}
private void registerServletClass( Dictionary mapping, Element servletElement ) throws SAXException {
mapping.put( XMLUtils.getChildNodeValue( servletElement, "servlet-name" ),
new ServletConfiguration( servletElement ) );
}
private void registerServlet( Dictionary mapping, Element servletElement ) throws SAXException {
registerServlet( XMLUtils.getChildNodeValue( servletElement, "url-pattern" ),
(ServletConfiguration) mapping.get( XMLUtils.getChildNodeValue( servletElement, "servlet-name" ) ) );
}
private void extractContextParameters( Document document ) throws SAXException {
NodeList nl = document.getElementsByTagName( "context-param" );
for (int i = 0; i < nl.getLength(); i++) {
Element param = (Element) nl.item( i );
String name = XMLUtils.getChildNodeValue( param, "param-name" );
String value = XMLUtils.getChildNodeValue( param, "param-value" );
_contextParameters.put( name, value );
}
}
private static boolean patternMatches( String urlPattern, String urlPath ) {
return urlPattern.equals( urlPath );
}
String getDisplayName() {
return _displayName;
}
//============================================= SecurityCheckServlet class =============================================
static class SecurityCheckServlet extends HttpServlet {
protected void doGet( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException {
handleLogin( req, resp );
}
protected void doPost( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException {
handleLogin( req, resp );
}
private void handleLogin( HttpServletRequest req, HttpServletResponse resp ) throws IOException {
final String username = req.getParameter( "j_username" );
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 extends WebResourceConfiguration {
private Servlet _servlet;
private String _servletName;
private int _loadOrder = DONT_AUTOLOAD;
ServletConfiguration( String className ) {
super( className );
}
ServletConfiguration( String className, Hashtable initParams ) {
super( className, initParams );
}
ServletConfiguration( Element servletElement ) throws SAXException {
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 = XMLUtils.getTextValue( loadOrder.item(i) );
try {
_loadOrder = Integer.parseInt( order );
} catch (NumberFormatException e) {
_loadOrder = ANY_LOAD_ORDER;
}
}
}
synchronized Servlet getServlet() throws ClassNotFoundException, InstantiationException, IllegalAccessException, ServletException {
if (_servlet == null) {
Class servletClass = Class.forName( getClassName() );
_servlet = (Servlet) servletClass.newInstance();
String servletName = _servletName != null ? _servletName : _servlet.getClass().getName();
_servlet.init( new ServletUnitServletConfig( servletName, WebApplication.this, getInitParams() ) );
}
return _servlet;
}
synchronized void destroyResource() {
if (_servlet != null) _servlet.destroy();
}
String getServletName() {
return _servletName;
}
boolean isLoadOnStartup() {
return _loadOrder != DONT_AUTOLOAD;
}
public int getLoadOrder() {
return _loadOrder;
}
}
//============================================= 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() + " could not be instantiated." );
} 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 ==================================
interface SecurityConstraint {
boolean controlsPath( String urlPath );
String[] getPermittedRoles();
}
static class NullSecurityConstraint implements SecurityConstraint {
private static final String[] NO_ROLES = new String[0];
public boolean controlsPath( String urlPath ) {
return false;
}
public String[] getPermittedRoles() {
return NO_ROLES;
}
}
static class SecurityConstraintImpl implements SecurityConstraint {
SecurityConstraintImpl( Element root ) throws SAXException {
final NodeList roleNames = root.getElementsByTagName( "role-name" );
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 ) ) );
}
public boolean controlsPath( String urlPath ) {
return getMatchingCollection( urlPath ) != null;
}
public String[] getPermittedRoles() {
if (_roles == null) {
_roles = (String[]) _roleList.toArray( new String[ _roleList.size() ] );
}
return _roles;
}
private String[] _roles;
private ArrayList _roleList = new ArrayList();
private ArrayList _resources = new ArrayList();
public WebResourceCollection getMatchingCollection( String urlPath ) {
for (Iterator i = _resources.iterator(); i.hasNext();) {
WebResourceCollection wrc = (WebResourceCollection) i.next();
if (wrc.controlsPath( urlPath )) return wrc;
}
return null;
}
class WebResourceCollection {
WebResourceCollection( Element root ) throws SAXException {
final NodeList urlPatterns = root.getElementsByTagName( "url-pattern" );
for (int i = 0; i < urlPatterns.getLength(); i++) _urlPatterns.add( XMLUtils.getTextValue( urlPatterns.item( i ) ) );
}
boolean controlsPath( String urlPath ) {
for (Iterator i = _urlPatterns.iterator(); i.hasNext();) {
String pattern = (String) i.next();
if (patternMatches( pattern, urlPath )) return true;
}
return false;
}
private ArrayList _urlPatterns = new ArrayList();
}
}
static final FilterMetaData[] NO_FILTERS = new FilterMetaData[0];
static class ServletRequestImpl implements ServletMetaData {
private URL _url;
private String _fullServletPath;
private WebResourceMapping _mapping;
private Hashtable _filtersPerName;
private FilterUrlMap _filtersPerUrl;
ServletRequestImpl( URL url, String servletPath, WebResourceMapping mapping, Hashtable filtersPerName, FilterUrlMap filtersPerUrl ) {
_url = url;
_fullServletPath = servletPath;
_mapping = mapping;
_filtersPerName = filtersPerName;
_filtersPerUrl = filtersPerUrl;
}
public Servlet getServlet() throws ServletException {
if (getConfiguration() == null) throw new HttpNotFoundException( "No servlet mapping defined", _url );
try {
return getConfiguration().getServlet();
} catch (ClassNotFoundException e) {
throw new HttpNotFoundException( _url, e );
} catch (IllegalAccessException e) {
throw new HttpInternalErrorException( _url, e );
} catch (InstantiationException e) {
throw new HttpInternalErrorException( _url, e );
} catch (ClassCastException e) {
throw new HttpInternalErrorException( _url, e );
}
}
/**
* get the ServletPath
* the decoded ServletPath
*/
public String getServletPath() {
return _mapping == null ? null : HttpUnitUtils.decode(_mapping.getServletPath( _fullServletPath ));
}
/**
* get the Path Information
* @return the decode path
*/
public String getPathInfo() {
return _mapping == null ? null : HttpUnitUtils.decode(_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 : (ServletConfiguration) _mapping.getConfiguration();
}
}
/**
* mapping for WebResources
*
*/
static class WebResourceMapping {
private WebResourceConfiguration _configuration;
WebResourceConfiguration getConfiguration() {
return _configuration;
}
WebResourceMapping( WebResourceConfiguration configuration ) {
_configuration = configuration;
}
/**
* 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;
}
/**
* 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 destroyResource() {
getConfiguration().destroyResource();
}
}
static class PartialMatchWebResourceMapping extends WebResourceMapping {
private 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 requestPath ) {
return _prefix;
}
String getPathInfo( String requestPath ) {
return requestPath.length() > _prefix.length()
? requestPath.substring( _prefix.length() )
: null;
}
}
/**
* 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 WebResourceMap {
private final Map _exactMatches = new HashMap();
private final Map _extensions = new HashMap();
private final Map _urlTree = new HashMap();
private WebResourceMapping _defaultMapping;
void put( String mapping, WebResourceConfiguration configuration ) {
if (mapping.equals( "/" )) {
_defaultMapping = new WebResourceMapping( configuration );
} else if (mapping.startsWith( "*." )) {
_extensions.put( mapping.substring( 2 ), new WebResourceMapping( configuration ) );
} else if (!mapping.startsWith( "/" ) || !mapping.endsWith( "/*" )) {
_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 PartialMatchWebResourceMapping( configuration, mapping ) );
return;
}
if (!context.containsKey( part )) {
context.put( part, new HashMap() );
}
context = (Map) context.get( part );
}
}
}
ServletMetaData get( URL url ) {
String file = url.getFile();
if (!file.startsWith( _contextPath )) throw new HttpNotFoundException( "File path does not begin with '" + _contextPath + "'", url );
String servletPath = getServletPath( file.substring( _contextPath.length() ) );
if (servletPath.endsWith( "j_security_check" )) {
return new ServletRequestImpl( url, servletPath, SECURITY_CHECK_MAPPING, _filterMapping, _filterUrlMapping );
} else {
return new ServletRequestImpl( url, servletPath, getMapping( servletPath ), _filterMapping, _filterUrlMapping );
}
}
private String getServletPath( String urlFile ) {
if (urlFile.indexOf( '?' ) < 0) {
return urlFile;
} else {
return urlFile.substring( 0, urlFile.indexOf( '?' ) );
}
}
public void destroyWebResources() {
if (_defaultMapping != null) _defaultMapping.destroyResource();
destroyWebResources( _exactMatches );
destroyWebResources( _extensions );
destroyWebResources( _urlTree );
}
private void destroyWebResources( Map map ) {
for (Iterator iterator = map.values().iterator(); iterator.hasNext();) {
Object o = iterator.next();
if (o instanceof WebResourceMapping) {
WebResourceMapping webResourceMapping = (WebResourceMapping) o;
webResourceMapping.destroyResource();
} else {
destroyWebResources( (Map) o );
}
}
}
void autoLoadServlets() {
ArrayList autoLoadable = new ArrayList();
if (_defaultMapping != null && _defaultMapping.getConfiguration().isLoadOnStartup()) autoLoadable.add( _defaultMapping.getConfiguration() );
collectAutoLoadableServlets( autoLoadable, _exactMatches );
collectAutoLoadableServlets( autoLoadable, _extensions );
collectAutoLoadableServlets( autoLoadable, _urlTree );
if (autoLoadable.isEmpty()) return;
Collections.sort( autoLoadable, new Comparator() {
public int compare( Object o1, Object o2 ) {
ServletConfiguration sc1 = (ServletConfiguration) o1;
ServletConfiguration sc2 = (ServletConfiguration) o2;
return (sc1.getLoadOrder() <= sc2.getLoadOrder()) ? -1 : +1;
}
});
for (Iterator iterator = autoLoadable.iterator(); iterator.hasNext();) {
ServletConfiguration servletConfiguration = (ServletConfiguration) iterator.next();
try {
servletConfiguration.getServlet();
} catch (Exception e) {
HttpUnitUtils.handleException(e);
throw new RuntimeException( "Unable to autoload servlet: " + servletConfiguration.getClassName() + ": " + e );
}
}
}
private void collectAutoLoadableServlets( Collection collection, Map map ) {
for (Iterator iterator = map.values().iterator(); iterator.hasNext();) {
Object o = iterator.next();
if (o instanceof WebResourceMapping) {
WebResourceMapping servletMapping = (WebResourceMapping) o;
if (servletMapping.getConfiguration().isLoadOnStartup()) collection.add( servletMapping.getConfiguration() );
} else {
collectAutoLoadableServlets( collection, (Map) o );
}
}
}
private WebResourceMapping getMapping( String url ) {
if (_exactMatches.containsKey( url )) return (WebResourceMapping) _exactMatches.get( url );
Map context = getContextForLongestPathPrefix( url );
if (context.containsKey( "*" )) return (WebResourceMapping) context.get( "*" );
if (_extensions.containsKey( getExtension( url ))) return (WebResourceMapping) _extensions.get( getExtension( url ) );
if (_urlTree.containsKey( "/" )) return (WebResourceMapping) _urlTree.get( "/" );
if (_defaultMapping != null) return _defaultMapping;
final String prefix = "/servlet/";
if (!url.startsWith( prefix )) return null;
String className = url.substring( prefix.length() );
try {
Class.forName( className );
return new WebResourceMapping( new ServletConfiguration( className ) );
} catch (ClassNotFoundException e) {
return null;
}
}
private Map getContextForLongestPathPrefix( String url ) {
Map context = _urlTree;
ParsedPath path = new ParsedPath( url );
while (path.hasNext()) {
String part = path.next();
if (!context.containsKey( part )) break;
context = (Map) context.get( part );
}
return context;
}
private String getExtension( String url ) {
int index = url.lastIndexOf( '.' );
if (index == -1 || index >= url.length() - 1) {
return "";
} else {
return url.substring( index + 1 );
}
}
}
}
/**
* A utility class for parsing URLs into paths
*
* @author Donald Ball
*/
class ParsedPath {
private final String path;
private int position = 0;
static final char seperator_char = '/';
/**
* Creates a new parsed path for the given path value
*
* @param path the path
*/
ParsedPath( String path ) {
if (path.charAt( 0 ) != seperator_char) {
throw new IllegalArgumentException( "Illegal path '" + path + "', does not begin with " + seperator_char );
}
this.path = path;
}
/**
* Returns true if there are more parts left, otherwise false
*/
public final boolean hasNext() {
return (position < path.length());
}
/**
* Returns the next part in the path
*/
public final String next() {
int offset = position + 1;
while (offset < path.length() && path.charAt( offset ) != seperator_char) {
offset++;
}
String result = path.substring( position + 1, offset );
position = offset;
return result;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy