/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cocoon.servlet; import java.io.File; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.Enumeration; import java.util.Set; import javax.servlet.RequestDispatcher; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; /** * A bootstrap servlet to allow Cocoon to run in servlet engines that aren't fully * compliant with the servlet 2.2 spec. * <p> * This servlet adds a mandatory "context-dir" parameter to those accepted by {@link CocoonServlet}, * which should point to Cocoon's context directory (e.g. "<code>/path-to-webapp/cocoon</code>"). * This directory is used to : * <ul> * <li>build a classloader with the correct class path with the contents of * <code>WEB-INF/classes</code> and <code>WEB-INF/lib</code> (see * {@link ParanoidClassLoader}),</li> * <li>resolve paths for context resources. * </ul> * * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a> * @version CVS $Id$ */ public class BootstrapServlet extends ParanoidCocoonServlet { protected File contextDir; protected File getContextDir() throws ServletException { ServletContext context = getServletContext(); ServletConfig config = getServletConfig(); log("getRealPath(\"/\") = " + context.getRealPath("/")); String contextDirParam = config.getInitParameter("context-directory"); if (contextDirParam == null) { throw new ServletException("The 'context-directory' parameter must be set to the root of the servlet context"); } // Ensure context dir doesn't end with a "/" (servlet spec says that paths for // getResource() should start by a "/") if (contextDirParam.endsWith("/")) { contextDirParam = contextDirParam.substring(0, contextDirParam.length() - 1); } // Ensure context dir exists and is a directory this.contextDir = new File(contextDirParam); if (!this.contextDir.exists()) { String msg = "Context dir '" + this.contextDir + "' doesn't exist"; log(msg); throw new ServletException(msg); } if (!this.contextDir.isDirectory()) { String msg = "Context dir '" + this.contextDir + "' should be a directory"; log(msg); throw new ServletException(msg); } context.log("Context dir set to " + this.contextDir); return this.contextDir; } protected void initServlet() throws ServletException { ServletContext newContext = new ContextWrapper(getServletContext(), this.contextDir); ServletConfig newConfig = new ConfigWrapper(getServletConfig(), newContext); this.servlet.init(newConfig); } //------------------------------------------------------------------------- /** * Implementation of <code>ServletConfig</code> passed to the actual servlet. * It wraps the original config object and returns the new context. */ public static class ConfigWrapper implements ServletConfig { ServletConfig config; ServletContext context; /** * Builds a <code>ServletConfig</code> using a servlet name and * a <code>ServletContext</code>. */ public ConfigWrapper(ServletConfig config, ServletContext context) { this.config = config; this.context = context; } public String getServletName() { return config.getServletName(); } public Enumeration getInitParameterNames() { return this.config.getInitParameterNames(); } public ServletContext getServletContext() { return this.context; } public String getInitParameter(String name) { return config.getInitParameter(name); } } //------------------------------------------------------------------------- /** * Wrapper for the <code>ServletContext</code> passed to the actual servlet. * It implements all resource-related methods using the provided context * root directory. Other calls are delegated to the wrapped context. */ public static class ContextWrapper implements ServletContext { ServletContext context; File contextRoot; /** * Builds a wrapper around an existing context, and handle all * resource resolution relatively to <code>contextRoot</code> */ public ContextWrapper(ServletContext context, File contextRoot) { this.context = context; this.contextRoot = contextRoot; } public ServletContext getContext(String param) { return this.context.getContext(param); } public int getMajorVersion() { return this.context.getMajorVersion(); } public int getMinorVersion() { return this.context.getMinorVersion(); } public String getMimeType(String param) { return this.context.getMimeType(param); } /** * Returns the resource URL by appending <code>path</code> to the context * root. If this doesn't point to an existing file, <code>null</code> is * returned. */ public URL getResource(String path) throws MalformedURLException { File file = new File(this.contextRoot, path); if (file.exists()) { URL result = file.toURL(); //this.context.log("getResource(" + path + ") = " + result); return result; } else { //this.context.log("getResource(" + path + ") = null"); return null; } } /** * Returns the stream for the result of <code>getResource()</code>, or * <code>null</code> if the resource doesn't exist. */ public InputStream getResourceAsStream(String path) { try { URL url = getResource(path); return (url == null) ? null : url.openStream(); } catch(Exception e) { this.context.log("getResourceAsStream(" + path + ") failed", e); return null; } } public RequestDispatcher getRequestDispatcher(String param) { return this.context.getRequestDispatcher(param); } public RequestDispatcher getNamedDispatcher(String param) { return this.context.getNamedDispatcher(param); } /** * @deprecated The method BootstrapServlet.ContextWrapper.getServlet(String) * overrides a deprecated method from ServletContext. * @see <a href="http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/ServletContext.html#getServlet(java.lang.String)">ServletContext#getServlet(java.lang.String)</a> */ public Servlet getServlet(String param) throws ServletException { return this.context.getServlet(param); } /** * @deprecated The method BootstrapServlet.ContextWrapper.getServlets() * overrides a deprecated method from ServletContext. * @see <a href="http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/ServletContext.html#getServlets()">ServletContext#getServlets()</a> */ public Enumeration getServlets() { return this.context.getServlets(); } /** * @deprecated The method BootstrapServlet.ContextWrapper.getServletNames() * overrides a deprecated method from ServletContext. * @see <a href="http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/ServletContext.html#getServletNames()">ServletContext#getServletNames()</a> */ public Enumeration getServletNames() { return this.context.getServletNames(); } public void log(String msg) { this.context.log(msg); } /** @deprecated use {@link #log(String message, Throwable throwable)} instead. */ public void log(Exception ex, String msg) { this.context.log(ex, msg); } public void log(String msg, Throwable thr) { this.context.log(msg, thr); } /** * Appends <code>path</code> to the context root. */ public String getRealPath(String path) { String result = this.contextRoot + path; //this.context.log("getRealPath(" + path + ") = " + result); return result; } public String getServerInfo() { return this.context.getServerInfo(); } public String getInitParameter(String param) { return this.context.getInitParameter(param); } public Enumeration getInitParameterNames() { return this.context.getInitParameterNames(); } public Object getAttribute(String param) { Object result = this.context.getAttribute(param); //this.context.log("getAttribute(" + param + ") = " + result); return result; } public Enumeration getAttributeNames() { return this.context.getAttributeNames(); } public void setAttribute(String name, Object value) { this.context.setAttribute(name, value); } public void removeAttribute(String name) { this.context.removeAttribute(name); } // Implementation of Servlet 2.3 methods. This is not absolutely required // for real usage since this servlet is targeted at 2.2, but is needed // for successful compilation public Set getResourcePaths(String param) { return null; } public String getServletContextName() { return "Cocoon context"; } } }