package org.ovirt.engine.core.utils.servlet; import java.io.IOException; import java.util.Collections; import javax.servlet.RequestDispatcher; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.ovirt.engine.core.utils.EngineLocalConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This Servlet is used to forward requests from one context to another context. You can define the target context * using init parameters in web.xml. There are two init parameter keys that are important: * <ul> * <li>targetContext: This is the context path to the target context</li> * <li>uri: This is the URI relative to the target context path</li> * </ul> * So combined the targetContext and uri will define the URL of the target servlet. * <br /> * For instance: <br /> * <init-param> <br /> *   <param-name>targetContext</param-name><br /> *   <param-value>/ovirt-engine/services;</param-value><br /> * </init-param> <br /> * <init-param> <br /> *   <param-name>uri</param-name><br /> *   <param-value>/pkiresource</param-value><br /> * </init-param> <br /> * Any extra path associated with the URL (i.e. any path after the servlet context path) * will be appended to given URI relative to the target context path. * <br /> * The parameters' value can contain property expressions for expanding Engine property values in * form of %{PROP_NAME}. */ public class ForwardServlet extends HttpServlet { /** * The logger instance. */ private static final Logger log = LoggerFactory.getLogger(ForwardServlet.class); /** * serialVersionUID. */ private static final long serialVersionUID = -4938071390518075052L; /** * The key to use in web.xml to specify the target context. */ public static final String CONTEXT_PARAM = "targetContext"; //$NON-NLS-1$ /** * The key to use in web.xml to specify the target URI. */ public static final String URI_PARAM = "uri"; //$NON-NLS-1$ /** * Init param prefix for adding attributes. */ public static final String ATTR_PREF = "attr-"; /** * The target context to use when forwarding. */ private String targetContext = null; /** * The target URI to use when forwarding. */ private String uri = null; @Override public void init(final ServletConfig config) throws ServletException { // Let the parent do its work: super.init(config); targetContext = config.getInitParameter(CONTEXT_PARAM); if (targetContext == null) { throw new ServletException("Target context not defined in web.xml"); //$NON-NLS-1$ } uri = config.getInitParameter(URI_PARAM); if (uri == null) { throw new ServletException("Target URI not defined in web.xml"); //$NON-NLS-1$ } // we use %{x} convention to avoid conflict with jboss properties EngineLocalConfig engineLocalConfig = EngineLocalConfig.getInstance(); targetContext = ServletUtils.getAsAbsoluteContext( getServletContext().getContextPath(), engineLocalConfig.expandString(targetContext.replaceAll("%\\{", "\\${")) ); uri = engineLocalConfig.expandString(uri.replaceAll("%\\{", "\\${")); } /** * Forward the request to the appropriate servlet in the context specified in the init method. This works * for all verbs due to the fact that RequestDispatcher.forward calls the 'service' method in the target * servlet, which determines which verb was used based on the request object. * * If the initialization of the {@code ForwardServlet} includes special parameters with a key starts with * 'attr-' those keys and values will be added to the request attributes for use in the target servlet. The * 'attr-' prefix will be stripped from attribute key. * For instance: <br /> * <init-param> <br /> *   <param-name>attr-location</param-name><br /> *   <param-value>secret location</param-value><br /> * </init-param> <br /> * Will results in a new attribute in the request object with the key 'location' and the value 'secret location' * * @param request The {@code HttpServletRequest} object * @param response The {@code HttpServletResponse} object * @throws IOException When underlying code throws IOException. * @throws ServletException When underlying code throws ServletException. */ @Override protected void service(final HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException { try { if (!request.getRequestURI().startsWith(request.getContextPath() + request.getServletPath())) { throw new RuntimeException("Unexpected request URI " + request.getRequestURI() + " with servlet path " + request.getContextPath() + request.getServletPath()); } final ServletContext forwardContext = getServletContext().getContext(targetContext); if (forwardContext == null) { throw new RuntimeException("Unable to determine forward context for " + targetContext); } final String forwardUri = uri + request.getRequestURI().substring( request.getContextPath().length() + request.getServletPath().length()); final RequestDispatcher dispatcher = forwardContext.getRequestDispatcher(forwardUri); if (dispatcher == null) { throw new RuntimeException("Unable to determine dispatcher for " + forwardUri); } for (String initParam : Collections.list(getInitParameterNames())) { if (initParam.startsWith(ATTR_PREF)) { request.setAttribute(initParam.replaceFirst(ATTR_PREF, ""), getInitParameter(initParam)); } } dispatcher.forward(request, response); } catch(Exception e) { log.error("Forward failed", e); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } } }