Java tutorial
/** * Copyright 2011 meltmedia * * 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 * * 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.xchain.framework.servlet; import java.io.IOException; import java.util.HashMap; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.namespace.QName; import org.apache.commons.jxpath.JXPathContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xchain.CatalogNotFoundException; import org.xchain.CommandNotFoundException; import org.xchain.framework.factory.CatalogFactory; import org.xchain.framework.jxpath.QNameVariables; import org.xchain.framework.lifecycle.ContainerLifecycle; import org.xchain.framework.lifecycle.LifecycleException; import org.xchain.framework.lifecycle.ThreadLifecycle; import org.xchain.namespaces.servlet.Constants; /** * This servlet can be used to bind servlet requests to xchains inside of an application. To do this, this servlet tasks the following action: * <ol> * <li>The requested url is translated into the name of a catalog. This catalog is then loaded.</li> * <li>The requested method is translated into a qname. This command is loaded from the catalog.</li> * <li>The servlet request and response are used to create a JXPathContext.</li> * <li>The JXPathcContext is used to execute the command.</li> * </ol> * * @author Mike Moulton * @author Devon Tackett * @author Christian Trimble * @author John Trimble * @author Josh Kennedy */ public class CatalogServlet extends HttpServlet { public static Logger log = LoggerFactory.getLogger(CatalogServlet.class); public static final String DEFAULT_BASE_CATALOG_NAME = "resource://" + ContainerLifecycle.SERVLET_CONTEXT_ATHORITY + "/"; public static final String BASE_CATALOG_NAME_PARAM = "base-catalog-name"; protected String baseCatalogName = null; public void init(ServletConfig config) throws ServletException { super.init(config); // read the base catalog name. baseCatalogName = config.getInitParameter(BASE_CATALOG_NAME_PARAM); if (baseCatalogName == null) { baseCatalogName = DEFAULT_BASE_CATALOG_NAME; } // log the config. if (log.isDebugEnabled()) { log.debug("Base Catalog Name: " + baseCatalogName); } } protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { ServletThreadContext context = new ServletThreadContext(request, response); try { ThreadLifecycle.getInstance().startThread(context); } catch (LifecycleException le) { throw new ServletException("Could not start the servlet thread due to an exception."); } try { // All methods may be valid commands for a catalog. No need to break out the usage into different // method calls. executeCommand(request, response); } finally { try { ThreadLifecycle.getInstance().stopThread(context); } catch (LifecycleException le) { if (log.isWarnEnabled()) { log.warn("An exception was thrown while cleaning up a thread.", le); } } } } /** * Execute the proper XChain based on the incoming {@link HttpServletRequest} and sending output on the {@link HttpServletResponse}. * The path on the request determines which catalog to use while the method of the request determines which command to execute. * * @param request The incoming request. * @param response The outgoing response. */ public void executeCommand(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { // get the catalog name for the request. String catalogName = catalogNameForRequest(request); // get the command name for the request. QName commandName = commandNameForRequest(request); if (log.isDebugEnabled()) { log.debug("Executing '" + commandName + "' in catalog '" + catalogName + "'."); } // create the context for the request. JXPathContext context = jXPathContext(request, response); // execute the command. boolean result = CatalogFactory.getInstance().getCatalog(catalogName).getCommand(commandName) .execute(context); // boolean result = CommandUtil.execute(catalogName, commandName, context); if (response.isCommitted() == false && result == false) { // it looks like we didn't do anything... } } catch (CatalogNotFoundException cae) { if (log.isDebugEnabled()) { log.debug("Could not find xchain catalog.", cae); } response.sendError(HttpServletResponse.SC_NOT_FOUND); } catch (CommandNotFoundException coe) { if (log.isDebugEnabled()) { log.debug("Could not find xchain command.", coe); } response.sendError(HttpServletResponse.SC_NOT_FOUND); } catch (Exception e) { if (log.isErrorEnabled()) { log.error("Unable to execute xchain.", e); } throw new ServletException(e); } } /** * Get the catalog based on the requested path. * * @param request The incoming HttpServletRequest. * * @return The full requested catalog name. */ protected String catalogNameForRequest(HttpServletRequest request) { StringBuffer sb = new StringBuffer(); // append the base catalog name. sb.append(baseCatalogName); String servletPath = request.getServletPath(); if (servletPath.startsWith("/") && baseCatalogName.endsWith("/")) { sb.append(servletPath.replaceAll("\\A/(.*)\\Z", "$1")); } else if (!servletPath.startsWith("/") && !baseCatalogName.endsWith("/")) { sb.append("/").append(servletPath); } else { sb.append(servletPath); } // return the catalog name. return sb.toString(); } /** * Get the command requested based on the request method. * * @param request The incoming HttpServletRequest. * * @return The QName of the requested command. */ protected QName commandNameForRequest(HttpServletRequest request) { return new QName(Constants.URI, request.getMethod().toLowerCase()); } /** * Build a new JXPathContext and populate it with the given request and response. * * @param request The incoming HttpServletRequest. * @param response The outgoing HttpServletResponse. * * @return A JXPath which contains the servlet request and response. */ protected JXPathContext jXPathContext(HttpServletRequest request, HttpServletResponse response) { JXPathContext context = JXPathContext.newContext(new HashMap()); ((QNameVariables) context.getVariables()).declareVariable(new QName(Constants.URI, Constants.CONTEXT), getServletConfig().getServletContext()); ((QNameVariables) context.getVariables()).declareVariable(new QName(Constants.URI, Constants.REQUEST), request); ((QNameVariables) context.getVariables()).declareVariable(new QName(Constants.URI, Constants.RESPONSE), response); return context; } }