001: // ========================================================================
002: // $Id: Invoker.java 1344 2006-12-01 22:31:56Z gregw $
003: // Copyright 199-2004 Mort Bay Consulting Pty. Ltd.
004: // ------------------------------------------------------------------------
005: // Licensed under the Apache License, Version 2.0 (the "License");
006: // you may not use this file except in compliance with the License.
007: // You may obtain a copy of the License at
008: // http://www.apache.org/licenses/LICENSE-2.0
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014: // ========================================================================
015:
016: package org.mortbay.jetty.servlet;
017:
018: import java.io.IOException;
019: import java.util.Enumeration;
020: import java.util.HashMap;
021: import java.util.Map;
022:
023: import javax.servlet.ServletContext;
024: import javax.servlet.ServletException;
025: import javax.servlet.UnavailableException;
026: import javax.servlet.http.HttpServlet;
027: import javax.servlet.http.HttpServletRequest;
028: import javax.servlet.http.HttpServletRequestWrapper;
029: import javax.servlet.http.HttpServletResponse;
030:
031: import org.mortbay.jetty.Handler;
032: import org.mortbay.jetty.handler.ContextHandler;
033: import org.mortbay.jetty.handler.HandlerWrapper;
034: import org.mortbay.log.Log;
035: import org.mortbay.util.LazyList;
036: import org.mortbay.util.URIUtil;
037:
038: ;
039:
040: /* ------------------------------------------------------------ */
041: /** Dynamic Servlet Invoker.
042: * This servlet invokes anonymous servlets that have not been defined
043: * in the web.xml or by other means. The first element of the pathInfo
044: * of a request passed to the envoker is treated as a servlet name for
045: * an existing servlet, or as a class name of a new servlet.
046: * This servlet is normally mapped to /servlet/*
047: * This servlet support the following initParams:
048: * <PRE>
049: * nonContextServlets If false, the invoker can only load
050: * servlets from the contexts classloader.
051: * This is false by default and setting this
052: * to true may have security implications.
053: *
054: * verbose If true, log dynamic loads
055: *
056: * * All other parameters are copied to the
057: * each dynamic servlet as init parameters
058: * </PRE>
059: * @version $Id: Invoker.java 1344 2006-12-01 22:31:56Z gregw $
060: * @author Greg Wilkins (gregw)
061: */
062: public class Invoker extends HttpServlet {
063:
064: private ContextHandler _contextHandler;
065: private ServletHandler _servletHandler;
066: private Map.Entry _invokerEntry;
067: private Map _parameters;
068: private boolean _nonContextServlets;
069: private boolean _verbose;
070:
071: /* ------------------------------------------------------------ */
072: public void init() {
073: ServletContext config = getServletContext();
074: _contextHandler = ((ContextHandler.SContext) config)
075: .getContextHandler();
076:
077: Handler handler = _contextHandler.getHandler();
078: while (handler != null && !(handler instanceof ServletHandler)
079: && (handler instanceof HandlerWrapper))
080: handler = ((HandlerWrapper) handler).getHandler();
081: _servletHandler = (ServletHandler) handler;
082: Enumeration e = getInitParameterNames();
083: while (e.hasMoreElements()) {
084: String param = (String) e.nextElement();
085: String value = getInitParameter(param);
086: String lvalue = value.toLowerCase();
087: if ("nonContextServlets".equals(param)) {
088: _nonContextServlets = value.length() > 0
089: && lvalue.startsWith("t");
090: }
091: if ("verbose".equals(param)) {
092: _verbose = value.length() > 0 && lvalue.startsWith("t");
093: } else {
094: if (_parameters == null)
095: _parameters = new HashMap();
096: _parameters.put(param, value);
097: }
098: }
099: }
100:
101: /* ------------------------------------------------------------ */
102: protected void service(HttpServletRequest request,
103: HttpServletResponse response) throws ServletException,
104: IOException {
105: // Get the requested path and info
106: boolean included = false;
107: String servlet_path = (String) request
108: .getAttribute(Dispatcher.__INCLUDE_SERVLET_PATH);
109: if (servlet_path == null)
110: servlet_path = request.getServletPath();
111: else
112: included = true;
113: String path_info = (String) request
114: .getAttribute(Dispatcher.__INCLUDE_PATH_INFO);
115: if (path_info == null)
116: path_info = request.getPathInfo();
117:
118: // Get the servlet class
119: String servlet = path_info;
120: if (servlet == null || servlet.length() <= 1) {
121: response.sendError(404);
122: return;
123: }
124:
125: int i0 = servlet.charAt(0) == '/' ? 1 : 0;
126: int i1 = servlet.indexOf('/', i0);
127: servlet = i1 < 0 ? servlet.substring(i0) : servlet.substring(
128: i0, i1);
129:
130: // look for a named holder
131: ServletHolder[] holders = _servletHandler.getServlets();
132: ServletHolder holder = getHolder(holders, servlet);
133:
134: if (holder != null) {
135: // Found a named servlet (from a user's web.xml file) so
136: // now we add a mapping for it
137: Log.debug("Adding servlet mapping for named servlet:"
138: + servlet + ":"
139: + URIUtil.addPaths(servlet_path, servlet) + "/*");
140: ServletMapping mapping = new ServletMapping();
141: mapping.setServletName(servlet);
142: mapping.setPathSpec(URIUtil.addPaths(servlet_path, servlet)
143: + "/*");
144: _servletHandler
145: .setServletMappings((ServletMapping[]) LazyList
146: .addToArray(_servletHandler
147: .getServletMappings(), mapping,
148: ServletMapping.class));
149: } else {
150: // look for a class mapping
151: if (servlet.endsWith(".class"))
152: servlet = servlet.substring(0, servlet.length() - 6);
153: if (servlet == null || servlet.length() == 0) {
154: response.sendError(404);
155: return;
156: }
157:
158: synchronized (_servletHandler) {
159: // find the entry for the invoker (me)
160: _invokerEntry = _servletHandler
161: .getHolderEntry(servlet_path);
162:
163: // Check for existing mapping (avoid threaded race).
164: String path = URIUtil.addPaths(servlet_path, servlet);
165: Map.Entry entry = _servletHandler.getHolderEntry(path);
166:
167: if (entry != null && !entry.equals(_invokerEntry)) {
168: // Use the holder
169: holder = (ServletHolder) entry.getValue();
170: } else {
171: // Make a holder
172: Log.debug("Making new servlet=" + servlet
173: + " with path=" + path + "/*");
174: holder = _servletHandler.addServlet(servlet, path
175: + "/*");
176: Map.Entry eee = _servletHandler
177: .getHolderEntry(path);
178: ServletMapping mapping = new ServletMapping();
179: mapping.setServletName(servlet);
180: mapping.setPathSpec(path + ".class/*");
181: _servletHandler
182: .setServletMappings((ServletMapping[]) LazyList
183: .addToArray(_servletHandler
184: .getServletMappings(),
185: mapping,
186: ServletMapping.class));
187:
188: if (_parameters != null)
189: holder.setInitParameters(_parameters);
190:
191: try {
192: holder.start();
193: } catch (Exception e) {
194: Log.debug(e);
195: throw new UnavailableException(e.toString());
196: }
197:
198: // Check it is from an allowable classloader
199: if (!_nonContextServlets) {
200: Object s = holder.getServlet();
201:
202: if (_contextHandler.getClassLoader() != s
203: .getClass().getClassLoader()) {
204: try {
205: holder.stop();
206: } catch (Exception e) {
207: Log.ignore(e);
208: }
209:
210: Log.warn("Dynamic servlet " + s
211: + " not loaded from context "
212: + request.getContextPath());
213: throw new UnavailableException(
214: "Not in context");
215: }
216: }
217:
218: if (_verbose)
219: Log.debug("Dynamic load '" + servlet + "' at "
220: + path);
221: }
222: }
223: }
224:
225: if (holder != null)
226: holder.handle(new Request(request, included, servlet,
227: servlet_path, path_info), response);
228: else {
229: Log.info("Can't find holder for servlet: " + servlet);
230: response.sendError(404);
231: }
232:
233: }
234:
235: /* ------------------------------------------------------------ */
236: class Request extends HttpServletRequestWrapper {
237: String _servletPath;
238: String _pathInfo;
239: boolean _included;
240:
241: /* ------------------------------------------------------------ */
242: Request(HttpServletRequest request, boolean included,
243: String name, String servletPath, String pathInfo) {
244: super (request);
245: _included = included;
246: _servletPath = URIUtil.addPaths(servletPath, name);
247: _pathInfo = pathInfo.substring(name.length() + 1);
248: if (_pathInfo.length() == 0)
249: _pathInfo = null;
250: }
251:
252: /* ------------------------------------------------------------ */
253: public String getServletPath() {
254: if (_included)
255: return super .getServletPath();
256: return _servletPath;
257: }
258:
259: /* ------------------------------------------------------------ */
260: public String getPathInfo() {
261: if (_included)
262: return super .getPathInfo();
263: return _pathInfo;
264: }
265:
266: /* ------------------------------------------------------------ */
267: public Object getAttribute(String name) {
268: if (_included) {
269: if (name.equals(Dispatcher.__INCLUDE_REQUEST_URI))
270: return URIUtil.addPaths(URIUtil.addPaths(
271: getContextPath(), _servletPath), _pathInfo);
272: if (name.equals(Dispatcher.__INCLUDE_PATH_INFO))
273: return _pathInfo;
274: if (name.equals(Dispatcher.__INCLUDE_SERVLET_PATH))
275: return _servletPath;
276: }
277: return super .getAttribute(name);
278: }
279: }
280:
281: private ServletHolder getHolder(ServletHolder[] holders,
282: String servlet) {
283: if (holders == null)
284: return null;
285:
286: ServletHolder holder = null;
287: for (int i = 0; holder == null && i < holders.length; i++) {
288: if (holders[i].getName().equals(servlet)) {
289: holder = holders[i];
290: }
291: }
292: return holder;
293: }
294: }
|