Java tutorial
/* * Copyright (c) 2013, Cloudera, Inc. All Rights Reserved. * * Cloudera, Inc. 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 * * This software 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.cloudera.oryx.serving.web; import java.io.IOException; import java.util.Map; import java.util.regex.Pattern; 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 com.google.common.base.Splitter; import com.google.common.collect.Maps; import com.cloudera.oryx.serving.stats.ServletStats; /** * Superclass of {@link HttpServlet}s used in the application. All API methods return the following * HTTP statuses in certain situations: * * <ul> * <li>{@code 400 Bad Request} if the arguments are invalid</li> * <li>{@code 401 Unauthorized} if a username/password is required, but not supplied correctly * in the request via HTTP DIGEST</li> * <li>{@code 405 Method Not Allowed} if an incorrect HTTP method is used, like {@code GET} * where {@code POST} is required</li> * <li>{@code 500 Internal Server Error} if an unexpected server-side exception occurs</li> * <li>{@code 503 Service Unavailable} if not yet available to serve requests</li> * </ul> * * @author Sean Owen */ public abstract class AbstractOryxServlet extends HttpServlet { private static final String KEY_PREFIX = AbstractOryxServlet.class.getName(); public static final String TIMINGS_KEY = KEY_PREFIX + ".TIMINGS"; private static final Pattern ESCAPED_SLASH = Pattern.compile("%2F", Pattern.CASE_INSENSITIVE); protected static final Splitter SLASH = Splitter.on('/').omitEmptyStrings(); private ServletStats timing; @Override public void init(ServletConfig config) throws ServletException { super.init(config); ServletContext context = config.getServletContext(); Map<String, ServletStats> timings; synchronized (context) { @SuppressWarnings("unchecked") Map<String, ServletStats> temp = (Map<String, ServletStats>) context.getAttribute(TIMINGS_KEY); timings = temp; if (timings == null) { timings = Maps.newTreeMap(); context.setAttribute(TIMINGS_KEY, timings); } } String key = getClass().getSimpleName(); ServletStats theTiming = timings.get(key); if (theTiming == null) { theTiming = new ServletStats(); timings.put(key, theTiming); } timing = theTiming; } @Override protected final void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long start = System.nanoTime(); super.service(request, response); timing.addTimingNanosec(System.nanoTime() - start); int status = response.getStatus(); if (status >= 400) { if (status >= 500) { timing.incrementServerErrors(); } else { timing.incrementClientErrors(); } } } /** * Hack: we have to double-escape forward-slash so that Tomcat won't read it as a path delimiter, * then un-escape again on the other end. * * @param pathElement path element potentially containing %2F * @return argument, with %2F converted to / */ protected static String unescapeSlashHack(CharSequence pathElement) { return ESCAPED_SLASH.matcher(pathElement).replaceAll("/"); } protected static void unescapeSlashHack(String[] pathElements) { for (int i = 0; i < pathElements.length; i++) { pathElements[i] = unescapeSlashHack(pathElements[i]); } } }