content: package alien4cloud.webconfiguration;
  
  import static com.codahale.metrics.MetricRegistry.name;
  
  import java.io.IOException;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.concurrent.ConcurrentHashMap;
  import java.util.concurrent.ConcurrentMap;
  
  import javax.servlet.Filter;
  import javax.servlet.FilterChain;
  import javax.servlet.FilterConfig;
  import javax.servlet.ServletException;
  import javax.servlet.ServletRequest;
  import javax.servlet.ServletResponse;
  import javax.servlet.http.HttpServletResponse;
  import javax.servlet.http.HttpServletResponseWrapper;
  
  import com.codahale.metrics.Counter;
  import com.codahale.metrics.Meter;
  import com.codahale.metrics.MetricRegistry;
  import com.codahale.metrics.Timer;
  import com.codahale.metrics.servlet.AbstractInstrumentedFilter;
  import lombok.extern.slf4j.Slf4j;
  
  /**
   * {@link Filter} implementation which captures request information and a breakdown of the response
   * codes being returned.
   */
  @Slf4j
  public class MetricsFilter implements Filter {
      private static final String NAME_PREFIX = "responseCodes.";
      private static final int OK = 200;
      private static final int CREATED = 201;
      private static final int NO_CONTENT = 204;
      private static final int BAD_REQUEST = 400;
      private static final int NOT_FOUND = 404;
      private static final int SERVER_ERROR = 500;
  
      private final String otherMetricName;
      private final Map<Integer, String> meterNamesByStatusCode;
  
      // initialized after call of init method
      private ConcurrentMap<Integer, Meter> metersByStatusCode;
      private Meter otherMeter;
      private Counter activeRequests;
      private Timer requestTimer;
  
      /**
       * Creates a new instance of the filter.
       */
      public MetricsFilter(MetricRegistry metricsRegistry) {
          this.otherMetricName = NAME_PREFIX + "other";
          this.meterNamesByStatusCode = createMeterNamesByStatusCode();
  
          this.metersByStatusCode = new ConcurrentHashMap<Integer, Meter>(meterNamesByStatusCode.size());
          for (Entry<Integer, String> entry : meterNamesByStatusCode.entrySet()) {
              metersByStatusCode.put(entry.getKey(), metricsRegistry.meter(name(AbstractInstrumentedFilter.class, entry.getValue())));
          }
          this.otherMeter = metricsRegistry.meter(name(AbstractInstrumentedFilter.class, otherMetricName));
          this.activeRequests = metricsRegistry.counter(name(AbstractInstrumentedFilter.class, "activeRequests"));
          this.requestTimer = metricsRegistry.timer(name(AbstractInstrumentedFilter.class, "requests"));
          log.info("Metrics filter initialized.");
      }
  
      private static Map<Integer, String> createMeterNamesByStatusCode() {
          final Map<Integer, String> meterNamesByStatusCode = new HashMap<Integer, String>(6);
          meterNamesByStatusCode.put(OK, NAME_PREFIX + "ok");
          meterNamesByStatusCode.put(CREATED, NAME_PREFIX + "created");
          meterNamesByStatusCode.put(NO_CONTENT, NAME_PREFIX + "noContent");
          meterNamesByStatusCode.put(BAD_REQUEST, NAME_PREFIX + "badRequest");
          meterNamesByStatusCode.put(NOT_FOUND, NAME_PREFIX + "notFound");
          meterNamesByStatusCode.put(SERVER_ERROR, NAME_PREFIX + "serverError");
          return meterNamesByStatusCode;
      }
  
      @Override
      public void init(FilterConfig filterConfig) throws ServletException {
      }
  
      @Override
      public void destroy() {
  
      }
  
      @Override
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
          final StatusExposingServletResponse wrappedResponse = new StatusExposingServletResponse((HttpServletResponse) response);
          activeRequests.inc();
          final Timer.Context context = requestTimer.time();
          try {
              chain.doFilter(request, wrappedResponse);
          } finally {
              context.stop();
              activeRequests.dec();
              markMeterForStatusCode(wrappedResponse.getStatus());
          }
      }
  
      private void markMeterForStatusCode(int status) {
          final Meter metric = metersByStatusCode.get(status);
          if (metric != null) {
              metric.mark();
          } else {
              otherMeter.mark();
          }
      }
  
      private static class StatusExposingServletResponse extends HttpServletResponseWrapper {
          // The Servlet spec says: calling setStatus is optional, if no status is set, the default is 200.
          private int httpStatus = 200;
  
          public StatusExposingServletResponse(HttpServletResponse response) {
              super(response);
          }
  
          @Override
          public void sendError(int sc) throws IOException {
              httpStatus = sc;
              super.sendError(sc);
          }
  
          @Override
          public void sendError(int sc, String msg) throws IOException {
              httpStatus = sc;
              super.sendError(sc, msg);
          }
  
          @Override
          public void setStatus(int sc) {
              httpStatus = sc;
              super.setStatus(sc);
          }
  
          public int getStatus() {
              return httpStatus;
          }
      }
  }
  