001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.server.dispatch;
031:
032: import com.caucho.log.Log;
033: import com.caucho.make.DependencyContainer;
034: import com.caucho.server.webapp.WebApp;
035: import com.caucho.util.L10N;
036: import com.caucho.vfs.Depend;
037: import com.caucho.vfs.Path;
038:
039: import javax.servlet.FilterChain;
040: import javax.servlet.ServletContext;
041: import javax.servlet.ServletException;
042: import java.io.InputStream;
043: import java.util.ArrayList;
044: import java.util.HashMap;
045: import java.util.logging.Level;
046: import java.util.logging.Logger;
047:
048: /**
049: * Manages dispatching: servlets and filters.
050: */
051: public class ServletMapper {
052: static final Logger log = Log.open(ServletMapper.class);
053: static final L10N L = new L10N(ServletMapper.class);
054:
055: private ServletContext _servletContext;
056:
057: private ServletManager _servletManager;
058:
059: private UrlMap<String> _servletMap = new UrlMap<String>();
060:
061: private ArrayList<String> _welcomeFileList = new ArrayList<String>();
062:
063: private HashMap<String, ServletMapping> _regexpMap = new HashMap<String, ServletMapping>();
064:
065: private ArrayList<String> _ignorePatterns = new ArrayList<String>();
066:
067: private String _defaultServlet;
068:
069: /**
070: * Sets the servlet context.
071: */
072: public void setServletContext(ServletContext servletContext) {
073: _servletContext = servletContext;
074: }
075:
076: /**
077: * Gets the servlet context.
078: */
079: public ServletContext getServletContext() {
080: return _servletContext;
081: }
082:
083: /**
084: * Returns the servlet manager.
085: */
086: public ServletManager getServletManager() {
087: return _servletManager;
088: }
089:
090: /**
091: * Sets the servlet manager.
092: */
093: public void setServletManager(ServletManager manager) {
094: _servletManager = manager;
095: }
096:
097: /**
098: * Adds a servlet mapping
099: */
100: public void addUrlRegexp(String regexp, ServletMapping mapping)
101: throws ServletException {
102: _servletMap.addRegexp(regexp, regexp);
103: _regexpMap.put(regexp, mapping);
104: }
105:
106: /**
107: * Adds a servlet mapping
108: */
109: void addUrlMapping(String urlPattern, String servletName,
110: ServletMapping mapping) throws ServletException {
111: try {
112: if (servletName == null) {
113: throw new ServletConfigException(L
114: .l("servlet needs a servlet-name."));
115: } else if (servletName.equals("invoker")) {
116: // special case
117: } else if (servletName.equals("plugin_match")
118: || servletName.equals("plugin-match")) {
119: // special case
120: } else if (servletName.equals("plugin_ignore")
121: || servletName.equals("plugin-ignore")) {
122: if (urlPattern != null)
123: _ignorePatterns.add(urlPattern);
124:
125: return;
126: } else if (_servletManager.getServlet(servletName) == null)
127: throw new ServletConfigException(
128: L
129: .l(
130: "`{0}' is an unknown servlet-name. servlet-mapping requires that the named servlet be defined in a <servlet> configuration before the <servlet-mapping>.",
131: servletName));
132:
133: if ("/".equals(urlPattern)) {
134: _defaultServlet = servletName;
135: } else if (mapping.isStrictMapping()) {
136: _servletMap.addStrictMap(urlPattern, null, servletName);
137: } else
138: _servletMap.addMap(urlPattern, servletName);
139:
140: log.config("servlet-mapping " + urlPattern + " -> "
141: + servletName);
142: } catch (ServletException e) {
143: throw e;
144: } catch (Exception e) {
145: throw new ServletException(e);
146: }
147: }
148:
149: /**
150: * Adds a servlet mapping
151: */
152: /*
153: public void addServletRegexp(ServletMapping servletRegexp)
154: throws ServletException
155: {
156: try {
157: String regexp = servletRegexp.getURLRegexp();
158:
159: _servletMap.addRegexp(regexp, regexp);
160: _regexpMap.put(regexp, servletRegexp);
161: } catch (RuntimeException e) {
162: throw e;
163: } catch (Exception e) {
164: throw new ServletException(e);
165: }
166: }
167: */
168:
169: /**
170: * Sets the default servlet.
171: */
172: public void setDefaultServlet(String servletName)
173: throws ServletException {
174: _defaultServlet = servletName;
175: }
176:
177: /**
178: * Adds a welcome-file
179: */
180: public void addWelcomeFile(String fileName) {
181: _welcomeFileList.add(fileName);
182: }
183:
184: /**
185: * Sets the welcome-file list
186: */
187: public void setWelcomeFileList(ArrayList<String> list) {
188: _welcomeFileList.clear();
189: _welcomeFileList.addAll(list);
190: }
191:
192: public FilterChain mapServlet(ServletInvocation invocation)
193: throws ServletException {
194: String contextURI = invocation.getContextURI();
195:
196: String servletName = null;
197: ArrayList<String> vars = new ArrayList<String>();
198:
199: invocation.setClassLoader(Thread.currentThread()
200: .getContextClassLoader());
201:
202: if (_servletMap != null) {
203: servletName = _servletMap.map(contextURI, vars);
204:
205: ServletMapping servletRegexp = _regexpMap.get(servletName);
206:
207: if (servletRegexp != null) {
208: servletName = servletRegexp.initRegexp(_servletContext,
209: _servletManager, vars);
210: }
211: }
212:
213: if (servletName == null) {
214: try {
215: InputStream is;
216: is = _servletContext.getResourceAsStream(contextURI);
217:
218: if (is != null) {
219: is.close();
220:
221: servletName = _defaultServlet;
222: }
223: } catch (Exception e) {
224: }
225: }
226:
227: if (servletName == null) {
228: for (int i = 0; i < _welcomeFileList.size(); i++) {
229: String file = _welcomeFileList.get(i);
230:
231: try {
232: String welcomeURI;
233:
234: if (contextURI.endsWith("/"))
235: welcomeURI = contextURI + file;
236: else
237: welcomeURI = contextURI + '/' + file;
238:
239: InputStream is;
240: is = _servletContext
241: .getResourceAsStream(welcomeURI);
242:
243: if (is != null)
244: is.close();
245:
246: if (is == null) {
247: } else if (!contextURI.endsWith("/")
248: && !(invocation instanceof SubInvocation)) {
249: String contextPath = invocation
250: .getContextPath();
251:
252: return new RedirectFilterChain(contextPath
253: + contextURI + "/");
254: } else {
255: servletName = _servletMap.map(welcomeURI, vars);
256:
257: if (servletName != null
258: || _defaultServlet != null) {
259: contextURI = welcomeURI;
260:
261: if (invocation instanceof Invocation) {
262: Invocation inv = (Invocation) invocation;
263:
264: inv.setContextURI(contextURI);
265: // server/10r9
266: // inv.setRawURI(inv.getRawURI() + file);
267: }
268: break;
269: }
270: }
271: } catch (Exception e) {
272: log.log(Level.WARNING, e.toString(), e);
273: }
274: }
275: }
276:
277: if (servletName == null) {
278: servletName = _defaultServlet;
279: vars.clear();
280: vars.add(contextURI);
281:
282: addWelcomeFileDependency(invocation);
283: }
284:
285: if (servletName == null) {
286: log.fine(L.l("'{0}' has no default servlet defined",
287: contextURI));
288:
289: return new ErrorFilterChain(404);
290: }
291:
292: String servletPath = vars.get(0);
293:
294: invocation.setServletPath(servletPath);
295:
296: if (servletPath.length() < contextURI.length())
297: invocation.setPathInfo(contextURI.substring(servletPath
298: .length()));
299: else
300: invocation.setPathInfo(null);
301:
302: ServletMapping regexp = _regexpMap.get(servletName);
303:
304: if (regexp != null)
305: servletName = regexp.initRegexp(_servletContext,
306: _servletManager, vars);
307:
308: if (servletName.equals("invoker"))
309: servletName = handleInvoker(invocation);
310:
311: invocation.setServletName(servletName);
312:
313: if (log.isLoggable(Level.FINE))
314: log.fine("invoke (uri:" + contextURI + " -> " + servletName
315: + ")");
316:
317: ServletConfigImpl config = _servletManager
318: .getServlet(servletName);
319:
320: if (config != null) {
321: invocation.setSecurityRoleMap(config.getRoleMap());
322: }
323:
324: FilterChain chain = _servletManager
325: .createServletChain(servletName);
326:
327: if (chain instanceof PageFilterChain) {
328: PageFilterChain pageChain = (PageFilterChain) chain;
329:
330: chain = PrecompilePageFilterChain.create(invocation,
331: pageChain);
332: }
333:
334: return chain;
335: }
336:
337: private void addWelcomeFileDependency(
338: ServletInvocation servletInvocation) {
339: if (!(servletInvocation instanceof Invocation))
340: return;
341:
342: Invocation invocation = (Invocation) servletInvocation;
343:
344: String contextURI = invocation.getContextURI();
345:
346: DependencyContainer dependencyList = new DependencyContainer();
347:
348: WebApp app = (WebApp) _servletContext;
349:
350: Path contextPath = app.getAppDir().lookup(
351: app.getRealPath(contextURI));
352:
353: if (!contextPath.isDirectory())
354: return;
355:
356: for (int i = 0; i < _welcomeFileList.size(); i++) {
357: String file = _welcomeFileList.get(i);
358:
359: String realPath = app.getRealPath(contextURI + "/" + file);
360:
361: Path path = app.getAppDir().lookup(realPath);
362:
363: dependencyList.add(new Depend(path));
364: }
365:
366: dependencyList.clearModified();
367:
368: invocation.setDependency(dependencyList);
369: }
370:
371: private String handleInvoker(ServletInvocation invocation)
372: throws ServletException {
373: String tail;
374:
375: if (invocation.getPathInfo() != null)
376: tail = invocation.getPathInfo();
377: else
378: tail = invocation.getServletPath();
379:
380: // XXX: this is really an unexpected, internal error that should never
381: // happen
382: if (!tail.startsWith("/")) {
383: throw new ServletException("expected '/' starting "
384: + " sp:" + invocation.getServletPath() + " pi:"
385: + invocation.getPathInfo() + " sn:invocation"
386: + invocation);
387: }
388:
389: int next = tail.indexOf('/', 1);
390: String servletName;
391:
392: if (next < 0)
393: servletName = tail.substring(1);
394: else
395: servletName = tail.substring(1, next);
396:
397: // XXX: This should be generalized, possibly with invoker configuration
398: if (servletName.startsWith("com.caucho")) {
399: throw new ServletConfigException(
400: L
401: .l(
402: "servlet `{0}' forbidden from invoker. com.caucho.* classes must be defined explicitly in a <servlet> declaration.",
403: servletName));
404: } else if (servletName.equals("")) {
405: throw new ServletConfigException(L.l(
406: "invoker needs a servlet name in URL `{0}'.",
407: invocation.getContextURI()));
408: }
409:
410: addServlet(servletName);
411:
412: String servletPath = invocation.getServletPath();
413: if (invocation.getPathInfo() == null) {
414: } else if (next < 0) {
415: invocation.setServletPath(servletPath + tail);
416: invocation.setPathInfo(null);
417: } else if (next < tail.length()) {
418:
419: invocation.setServletPath(servletPath
420: + tail.substring(0, next));
421: invocation.setPathInfo(tail.substring(next));
422: } else {
423: invocation.setServletPath(servletPath + tail);
424: invocation.setPathInfo(null);
425: }
426:
427: return servletName;
428: }
429:
430: public String getServletPattern(String uri) {
431: ArrayList<String> vars = new ArrayList<String>();
432:
433: Object value = null;
434:
435: if (_servletMap != null)
436: value = _servletMap.map(uri, vars);
437:
438: if (value == null)
439: return null;
440: else
441: return uri;
442: }
443:
444: /**
445: * Returns the servlet matching patterns.
446: */
447: public ArrayList<String> getURLPatterns() {
448: ArrayList<String> patterns = _servletMap.getURLPatterns();
449:
450: return patterns;
451: }
452:
453: /**
454: * Returns the servlet plugin_ignore patterns.
455: */
456: public ArrayList<String> getIgnorePatterns() {
457: return _ignorePatterns;
458: }
459:
460: private void addServlet(String servletName) throws ServletException {
461: if (_servletManager.getServlet(servletName) != null)
462: return;
463:
464: ServletConfigImpl config = new ServletConfigImpl();
465: config.setServletContext(_servletContext);
466: config.setServletName(servletName);
467:
468: try {
469: config.setServletClass(servletName);
470: } catch (Exception e) {
471: throw new ServletException(e);
472: }
473:
474: config.init();
475:
476: _servletManager.addServlet(config);
477: }
478:
479: public void destroy() {
480: _servletManager.destroy();
481: }
482: }
|