All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.appengine.tools.development.jetty9.FixupJspServlet Maven / Gradle / Ivy

/*
 * Copyright 2021 Google LLC
 *
 * Licensed 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
 *
 *     https://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 com.google.appengine.tools.development.jetty9;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.jasper.servlet.JspServlet;
import org.apache.tomcat.InstanceManager;

/**
 * {@code FixupJspServlet} adds some logic to work around bugs in the Jasper {@link JspServlet}.
 *
 */
public class FixupJspServlet extends JspServlet {

  /**
   * The request attribute that contains the name of the JSP file, when the
   * request path doesn't refer directly to the JSP file (for example,
   * it's instead a servlet mapping).
   */
  private static final String JASPER_JSP_FILE = "org.apache.catalina.jsp_file";
  private static final String WEB31XML =
      ""
          + ""
          + "";

  @Override
  public void init(ServletConfig config) throws ServletException {
    config
        .getServletContext()
        .setAttribute(InstanceManager.class.getName(), new InstanceManagerImpl());
    config
        .getServletContext()
        .setAttribute("org.apache.tomcat.util.scan.MergedWebXml", WEB31XML);
    super.init(config);
  }

  @Override
  public void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    fixupJspFileAttribute(request);
    super.service(request, response);
  }

  private static class InstanceManagerImpl implements InstanceManager {
    @Override
    public Object newInstance(String className)
        throws IllegalAccessException, InvocationTargetException, InstantiationException,
            ClassNotFoundException {
      return newInstance(className, this.getClass().getClassLoader());
    }

    @Override
    public Object newInstance(String fqcn, ClassLoader classLoader)
        throws IllegalAccessException, InvocationTargetException, InstantiationException,
            ClassNotFoundException {
      Class cl = classLoader.loadClass(fqcn);
      return newInstance(cl);
    }

    @Override
    @SuppressWarnings("ClassNewInstance")
    // We would prefer clazz.getConstructor().newInstance() here, but that throws
    // NoSuchMethodException. It would also lead to a change in behaviour, since an exception
    // thrown by the constructor would be wrapped in InvocationTargetException rather than being
    // propagated from newInstance(). Although that's funky, and the reason for preferring
    // getConstructor().newInstance(), we don't know if something is relying on the current
    // behaviour.
    public Object newInstance(Class clazz)
        throws IllegalAccessException, InvocationTargetException, InstantiationException {
      return clazz.newInstance();
    }

    @Override
    public void newInstance(Object o) {}

    @Override
    public void destroyInstance(Object o)
        throws IllegalAccessException, InvocationTargetException {}
  }

  // NB This method is here, because there appears to be
  // a bug in either Jetty or Jasper where  entries in web.xml
  // don't get handled correctly. This interaction between Jetty and Jasper
  // appears to have always been broken, irrespective of App Engine
  // integration.
  //
  // Jetty hands the name of the JSP file to Jasper (via a request attribute)
  // without a leading slash. This seems to cause all sorts of problems.
  //   - Jasper turns around and asks Jetty to lookup that same file
  // (using ServletContext.getResourceAsStream). Jetty rejects, out-of-hand,
  // any resource requests that don't start with a leading slash.
  //   - Jasper seems to plain blow up on jsp paths that don't have a leading
  // slash.
  //
  // If we enforce a leading slash, Jetty and Jasper seem to co-operate
  // correctly.
  private void fixupJspFileAttribute(HttpServletRequest request) {
    String jspFile = (String) request.getAttribute(JASPER_JSP_FILE);

    if (jspFile != null) {
      if (jspFile.length() == 0) {
        jspFile = "/";
      } else if (jspFile.charAt(0) != '/') {
        jspFile = "/" + jspFile;
      }
      request.setAttribute(JASPER_JSP_FILE, jspFile);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy