001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.catalina.core;
019:
020: import javax.servlet.Servlet;
021: import javax.servlet.ServletRequest;
022: import javax.servlet.http.HttpServletRequest;
023:
024: import org.apache.catalina.CometFilter;
025: import org.apache.catalina.Globals;
026: import org.apache.catalina.Wrapper;
027: import org.apache.catalina.connector.Request;
028: import org.apache.catalina.deploy.FilterMap;
029:
030: /**
031: * Factory for the creation and caching of Filters and creationg
032: * of Filter Chains.
033: *
034: * @author Greg Murray
035: * @author Remy Maucherat
036: * @version $Revision: 1.0
037: */
038:
039: public final class ApplicationFilterFactory {
040:
041: // -------------------------------------------------------------- Constants
042:
043: public static final int ERROR = 1;
044: public static final Integer ERROR_INTEGER = new Integer(ERROR);
045: public static final int FORWARD = 2;
046: public static final Integer FORWARD_INTEGER = new Integer(FORWARD);
047: public static final int INCLUDE = 4;
048: public static final Integer INCLUDE_INTEGER = new Integer(INCLUDE);
049: public static final int REQUEST = 8;
050: public static final Integer REQUEST_INTEGER = new Integer(REQUEST);
051:
052: public static final String DISPATCHER_TYPE_ATTR = Globals.DISPATCHER_TYPE_ATTR;
053: public static final String DISPATCHER_REQUEST_PATH_ATTR = Globals.DISPATCHER_REQUEST_PATH_ATTR;
054:
055: private static ApplicationFilterFactory factory = null;;
056:
057: // ----------------------------------------------------------- Constructors
058:
059: /*
060: * Prevent instanciation outside of the getInstanceMethod().
061: */
062: private ApplicationFilterFactory() {
063: }
064:
065: // --------------------------------------------------------- Public Methods
066:
067: /**
068: * Return the fqctory instance.
069: */
070: public static ApplicationFilterFactory getInstance() {
071: if (factory == null) {
072: factory = new ApplicationFilterFactory();
073: }
074: return factory;
075: }
076:
077: /**
078: * Construct and return a FilterChain implementation that will wrap the
079: * execution of the specified servlet instance. If we should not execute
080: * a filter chain at all, return <code>null</code>.
081: *
082: * @param request The servlet request we are processing
083: * @param servlet The servlet instance to be wrapped
084: */
085: public ApplicationFilterChain createFilterChain(
086: ServletRequest request, Wrapper wrapper, Servlet servlet) {
087:
088: // get the dispatcher type
089: int dispatcher = -1;
090: if (request.getAttribute(DISPATCHER_TYPE_ATTR) != null) {
091: Integer dispatcherInt = (Integer) request
092: .getAttribute(DISPATCHER_TYPE_ATTR);
093: dispatcher = dispatcherInt.intValue();
094: }
095: String requestPath = null;
096: Object attribute = request
097: .getAttribute(DISPATCHER_REQUEST_PATH_ATTR);
098:
099: if (attribute != null) {
100: requestPath = attribute.toString();
101: }
102:
103: HttpServletRequest hreq = null;
104: if (request instanceof HttpServletRequest)
105: hreq = (HttpServletRequest) request;
106: // If there is no servlet to execute, return null
107: if (servlet == null)
108: return (null);
109:
110: boolean comet = false;
111:
112: // Create and initialize a filter chain object
113: ApplicationFilterChain filterChain = null;
114: if (request instanceof Request) {
115: Request req = (Request) request;
116: comet = req.isComet();
117: if (Globals.IS_SECURITY_ENABLED) {
118: // Security: Do not recycle
119: filterChain = new ApplicationFilterChain();
120: if (comet) {
121: req.setFilterChain(filterChain);
122: }
123: } else {
124: filterChain = (ApplicationFilterChain) req
125: .getFilterChain();
126: if (filterChain == null) {
127: filterChain = new ApplicationFilterChain();
128: req.setFilterChain(filterChain);
129: }
130: }
131: } else {
132: // Request dispatcher in use
133: filterChain = new ApplicationFilterChain();
134: }
135:
136: filterChain.setServlet(servlet);
137:
138: filterChain.setSupport(((StandardWrapper) wrapper)
139: .getInstanceSupport());
140:
141: // Acquire the filter mappings for this Context
142: StandardContext context = (StandardContext) wrapper.getParent();
143: FilterMap filterMaps[] = context.findFilterMaps();
144:
145: // If there are no filter mappings, we are done
146: if ((filterMaps == null) || (filterMaps.length == 0))
147: return (filterChain);
148:
149: // Acquire the information we will need to match filter mappings
150: String servletName = wrapper.getName();
151:
152: // Add the relevant path-mapped filters to this filter chain
153: for (int i = 0; i < filterMaps.length; i++) {
154: if (!matchDispatcher(filterMaps[i], dispatcher)) {
155: continue;
156: }
157: if (!matchFiltersURL(filterMaps[i], requestPath))
158: continue;
159: ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context
160: .findFilterConfig(filterMaps[i].getFilterName());
161: if (filterConfig == null) {
162: ; // FIXME - log configuration problem
163: continue;
164: }
165: boolean isCometFilter = false;
166: if (comet) {
167: try {
168: isCometFilter = filterConfig.getFilter() instanceof CometFilter;
169: } catch (Exception e) {
170: // Note: The try catch is there because getFilter has a lot of
171: // declared exceptions. However, the filter is allocated much
172: // earlier
173: }
174: if (isCometFilter) {
175: filterChain.addFilter(filterConfig);
176: }
177: } else {
178: filterChain.addFilter(filterConfig);
179: }
180: }
181:
182: // Add filters that match on servlet name second
183: for (int i = 0; i < filterMaps.length; i++) {
184: if (!matchDispatcher(filterMaps[i], dispatcher)) {
185: continue;
186: }
187: if (!matchFiltersServlet(filterMaps[i], servletName))
188: continue;
189: ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context
190: .findFilterConfig(filterMaps[i].getFilterName());
191: if (filterConfig == null) {
192: ; // FIXME - log configuration problem
193: continue;
194: }
195: boolean isCometFilter = false;
196: if (comet) {
197: try {
198: isCometFilter = filterConfig.getFilter() instanceof CometFilter;
199: } catch (Exception e) {
200: // Note: The try catch is there because getFilter has a lot of
201: // declared exceptions. However, the filter is allocated much
202: // earlier
203: }
204: if (isCometFilter) {
205: filterChain.addFilter(filterConfig);
206: }
207: } else {
208: filterChain.addFilter(filterConfig);
209: }
210: }
211:
212: // Return the completed filter chain
213: return (filterChain);
214:
215: }
216:
217: // -------------------------------------------------------- Private Methods
218:
219: /**
220: * Return <code>true</code> if the context-relative request path
221: * matches the requirements of the specified filter mapping;
222: * otherwise, return <code>false</code>.
223: *
224: * @param filterMap Filter mapping being checked
225: * @param requestPath Context-relative request path of this request
226: */
227: private boolean matchFiltersURL(FilterMap filterMap,
228: String requestPath) {
229:
230: // Check the specific "*" special URL pattern, which also matches
231: // named dispatches
232: if (filterMap.getMatchAllUrlPatterns())
233: return (true);
234:
235: if (requestPath == null)
236: return (false);
237:
238: // Match on context relative request path
239: String[] testPaths = filterMap.getURLPatterns();
240:
241: for (int i = 0; i < testPaths.length; i++) {
242: if (matchFiltersURL(testPaths[i], requestPath)) {
243: return (true);
244: }
245: }
246:
247: // No match
248: return (false);
249:
250: }
251:
252: /**
253: * Return <code>true</code> if the context-relative request path
254: * matches the requirements of the specified filter mapping;
255: * otherwise, return <code>false</code>.
256: *
257: * @param testPath URL mapping being checked
258: * @param requestPath Context-relative request path of this request
259: */
260: private boolean matchFiltersURL(String testPath, String requestPath) {
261:
262: if (testPath == null)
263: return (false);
264:
265: // Case 1 - Exact Match
266: if (testPath.equals(requestPath))
267: return (true);
268:
269: // Case 2 - Path Match ("/.../*")
270: if (testPath.equals("/*"))
271: return (true);
272: if (testPath.endsWith("/*")) {
273: if (testPath.regionMatches(0, requestPath, 0, testPath
274: .length() - 2)) {
275: if (requestPath.length() == (testPath.length() - 2)) {
276: return (true);
277: } else if ('/' == requestPath
278: .charAt(testPath.length() - 2)) {
279: return (true);
280: }
281: }
282: return (false);
283: }
284:
285: // Case 3 - Extension Match
286: if (testPath.startsWith("*.")) {
287: int slash = requestPath.lastIndexOf('/');
288: int period = requestPath.lastIndexOf('.');
289: if ((slash >= 0)
290: && (period > slash)
291: && (period != requestPath.length() - 1)
292: && ((requestPath.length() - period) == (testPath
293: .length() - 1))) {
294: return (testPath.regionMatches(2, requestPath,
295: period + 1, testPath.length() - 2));
296: }
297: }
298:
299: // Case 4 - "Default" Match
300: return (false); // NOTE - Not relevant for selecting filters
301:
302: }
303:
304: /**
305: * Return <code>true</code> if the specified servlet name matches
306: * the requirements of the specified filter mapping; otherwise
307: * return <code>false</code>.
308: *
309: * @param filterMap Filter mapping being checked
310: * @param servletName Servlet name being checked
311: */
312: private boolean matchFiltersServlet(FilterMap filterMap,
313: String servletName) {
314:
315: if (servletName == null) {
316: return (false);
317: }
318: // Check the specific "*" special servlet name
319: else if (filterMap.getMatchAllServletNames()) {
320: return (true);
321: } else {
322: String[] servletNames = filterMap.getServletNames();
323: for (int i = 0; i < servletNames.length; i++) {
324: if (servletName.equals(servletNames[i])) {
325: return (true);
326: }
327: }
328: return false;
329: }
330:
331: }
332:
333: /**
334: * Convienience method which returns true if the dispatcher type
335: * matches the dispatcher types specified in the FilterMap
336: */
337: private boolean matchDispatcher(FilterMap filterMap, int dispatcher) {
338: switch (dispatcher) {
339: case FORWARD: {
340: if (filterMap.getDispatcherMapping() == FilterMap.FORWARD
341: || filterMap.getDispatcherMapping() == FilterMap.FORWARD_ERROR
342: || filterMap.getDispatcherMapping() == FilterMap.INCLUDE_FORWARD
343: || filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR_FORWARD
344: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD
345: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD
346: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE
347: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD_INCLUDE) {
348: return true;
349: }
350: break;
351: }
352: case INCLUDE: {
353: if (filterMap.getDispatcherMapping() == FilterMap.INCLUDE
354: || filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR
355: || filterMap.getDispatcherMapping() == FilterMap.INCLUDE_FORWARD
356: || filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR_FORWARD
357: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_INCLUDE
358: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_INCLUDE
359: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE
360: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD_INCLUDE) {
361: return true;
362: }
363: break;
364: }
365: case REQUEST: {
366: if (filterMap.getDispatcherMapping() == FilterMap.REQUEST
367: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR
368: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_INCLUDE
369: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_INCLUDE
370: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD
371: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD
372: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_FORWARD_INCLUDE
373: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE) {
374: return true;
375: }
376: break;
377: }
378: case ERROR: {
379: if (filterMap.getDispatcherMapping() == FilterMap.ERROR
380: || filterMap.getDispatcherMapping() == FilterMap.FORWARD_ERROR
381: || filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR
382: || filterMap.getDispatcherMapping() == FilterMap.INCLUDE_ERROR_FORWARD
383: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR
384: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD
385: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_FORWARD_INCLUDE
386: || filterMap.getDispatcherMapping() == FilterMap.REQUEST_ERROR_INCLUDE) {
387: return true;
388: }
389: break;
390: }
391: }
392: return false;
393: }
394:
395: }
|