001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2013-2016 ForgeRock AS.
015 */
016package org.opends.server.protocols.http;
017
018import java.util.Arrays;
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022import java.util.Map.Entry;
023import java.util.concurrent.atomic.AtomicInteger;
024import java.util.concurrent.atomic.AtomicLong;
025
026import org.opends.server.api.MonitorData;
027import org.opends.server.protocols.ldap.LDAPStatistics;
028
029/**
030 * Collects statistics for HTTP. This class inherits from {@link LDAPStatistics}
031 * to show the administrator how the underlying LDAP internal operations are
032 * performing.
033 */
034public class HTTPStatistics extends LDAPStatistics
035{
036  /**
037   * Map containing the total number of requests per HTTP methods.
038   * <p>
039   * key: HTTP method => value: number of requests for that method.
040   * </p>
041   * Not using a ConcurrentMap implementation here because the keys are static.
042   * The keys are static because they need to be listed in the schema which is
043   * static.
044   */
045  private final Map<String, AtomicInteger> requestMethodsTotalCount = new HashMap<>();
046  /**
047   * Map containing the total execution time for the requests per HTTP methods.
048   * <p>
049   * key: HTTP method => value: total execution time for requests using that
050   * method.
051   * </p>
052   * Not using a ConcurrentMap implementation here because the keys are static.
053   * The keys are static because they need to be listed in the schema which is
054   * static.
055   */
056  private final Map<String, AtomicLong> requestMethodsTotalTime = new HashMap<>();
057  /**
058   * Total number of requests. The total number may be different than the sum of
059   * the supported HTTP methods above because clients could use unsupported HTTP
060   * methods.
061   */
062  private final AtomicInteger requestsTotalCount = new AtomicInteger(0);
063
064  /**
065   * Constructor for this class.
066   *
067   * @param instanceName
068   *          The name for this monitor provider instance.
069   */
070  public HTTPStatistics(String instanceName)
071  {
072    super(instanceName);
073
074    // List the HTTP methods supported by Rest2LDAP
075    final List<String> supportedHttpMethods =
076        Arrays.asList("delete", "get", "patch", "post", "put");
077    for (String method : supportedHttpMethods)
078    {
079      requestMethodsTotalCount.put(method, new AtomicInteger(0));
080      requestMethodsTotalTime.put(method, new AtomicLong(0));
081    }
082  }
083
084  /** {@inheritDoc} */
085  @Override
086  public void clearStatistics()
087  {
088    this.requestMethodsTotalCount.clear();
089    this.requestMethodsTotalTime.clear();
090    this.requestsTotalCount.set(0);
091
092    super.clearStatistics();
093  }
094
095  @Override
096  public MonitorData getMonitorData()
097  {
098    final MonitorData results = super.getMonitorData();
099    addAll(results, requestMethodsTotalCount, "ds-mon-http-", "-requests-total-count");
100    addAll(results, requestMethodsTotalTime, "ds-mon-resident-time-http-", "-requests-total-time");
101    results.add("ds-mon-http-requests-total-count", requestsTotalCount.get());
102    return results;
103  }
104
105  private void addAll(final MonitorData results,
106      final Map<String, ?> toOutput, String prefix, String suffix)
107  {
108    for (Entry<String, ?> entry : toOutput.entrySet())
109    {
110      final String httpMethod = entry.getKey();
111      final String nb = entry.getValue().toString();
112      results.add(prefix + httpMethod + suffix, nb);
113    }
114  }
115
116  /**
117   * Adds to the total time of an HTTP request method.
118   *
119   * @param httpMethod
120   *          the method of the HTTP request to add to the stats
121   * @param time
122   *          the time to add to the total
123   * @throws NullPointerException
124   *           if the httpMethod is null
125   */
126  public void updateRequestMonitoringData(String httpMethod, long time)
127      throws NullPointerException
128  {
129    AtomicLong nb = this.requestMethodsTotalTime.get(httpMethod.toLowerCase());
130    if (nb != null)
131    {
132      nb.addAndGet(time);
133    } // else this is an unsupported HTTP method
134  }
135}