mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Jean-Noel Rouvignac
14.13.2013 7fc00840744292c3c138a7dffa187b073960e5a2
OPENDJ-858 (CR-1680) Add stats tracking to HTTP client connections



Now monitoring total etime for each supported HTTP method.
While implementing this, I noticed that HTTPRequestInfo was duplicating fields in HTTPClientConnection and adding little more. I moved the missing data in HTTPRequestInfo to HTTPClientConnection and transformed the HTTPRequestInfo class into an interface to keep zero coupling between logging and HTTPClientConnection.



HTTPRequestInfo.java:
Transformed this class into an interface.
Renamed getRemoteHost() to getClientHost() and getRemoteAddress() to getClientAddress().

HTTPClientConnection.java
Now implements HTTPRequestInfo.
Moved several fields from HTTPRequestInfo here + added missing getters and setters.
In sendResponse(), calling HTTPStatistics.updateRequestMonitoringData().
Added log().

TextHTTPAccessLogPublisher.java:
Consequence of the changes to HTTPRequestInfo.
Also added support for the "c-port", "s-ip", "s-computername", and "s-port" fields.

CollectClientConnectionsFilter.java, SdkConnectionAdapter.java:
Consequence of the changes to HTTPRequestInfo.

HTTPStatistics.java:
Renamed instance members nbRequests to requestMethodsTotalCount and nbRequestsTotalCount to requestsTotalCount.
Added requestMethodsTotalTime instance member.
Completed the constructor, clearStatistics(), getMonitorData().
Fixed clearStatistics().
Added addAll(), updateRequestMonitoringData().

02-config.ldif:
Added attributes for monitoring the total etime of HTTP requests.
Renumbered some previously added attributes to follow what is done for LDAP operations etime.
7 files modified
436 ■■■■■ changed files
opends/resource/schema/02-config.ldif 52 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/loggers/HTTPRequestInfo.java 137 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/loggers/TextHTTPAccessLogPublisher.java 8 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/http/CollectClientConnectionsFilter.java 20 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/http/HTTPClientConnection.java 103 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/http/HTTPStatistics.java 104 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/http/SdkConnectionAdapter.java 12 ●●●● patch | view | raw | blame | history
opends/resource/schema/02-config.ldif
@@ -3684,35 +3684,71 @@
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.121
  NAME 'ds-mon-http-delete-requests-total-count'
  NAME 'ds-mon-resident-time-http-requests-total-time'
  EQUALITY integerMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.122
  NAME 'ds-mon-http-get-requests-total-count'
  NAME 'ds-mon-http-delete-requests-total-count'
  EQUALITY integerMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.123
  NAME 'ds-mon-http-patch-requests-total-count'
  NAME 'ds-mon-resident-time-http-delete-requests-total-time'
  EQUALITY integerMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.124
  NAME 'ds-mon-http-post-requests-total-count'
  NAME 'ds-mon-http-get-requests-total-count'
  EQUALITY integerMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.125
  NAME 'ds-mon-resident-time-http-get-requests-total-time'
  EQUALITY integerMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.126
  NAME 'ds-mon-http-patch-requests-total-count'
  EQUALITY integerMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.127
  NAME 'ds-mon-resident-time-http-patch-requests-total-time'
  EQUALITY integerMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.128
  NAME 'ds-mon-http-post-requests-total-count'
  EQUALITY integerMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.129
  NAME 'ds-mon-resident-time-http-post-requests-total-time'
  EQUALITY integerMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.130
  NAME 'ds-mon-http-put-requests-total-count'
  EQUALITY integerMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.131
  NAME 'ds-mon-resident-time-http-put-requests-total-time'
  EQUALITY integerMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE
  X-ORIGIN 'OpenDJ Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
  NAME 'ds-cfg-access-control-handler'
  SUP top
@@ -5629,11 +5665,17 @@
  ds-mon-extended-operations-total-count $
  ds-mon-resident-time-extended-operations-total-time $
  ds-mon-http-requests-total-count $
  ds-mon-resident-time-http-requests-total-time $
  ds-mon-http-delete-requests-total-count $
  ds-mon-resident-time-http-delete-requests-total-time $
  ds-mon-http-get-requests-total-count $
  ds-mon-resident-time-http-get-requests-total-time $
  ds-mon-http-patch-requests-total-count $
  ds-mon-resident-time-http-patch-requests-total-time $
  ds-mon-http-post-requests-total-count $
  ds-mon-http-put-requests-total-count )
  ds-mon-resident-time-http-post-requests-total-time $
  ds-mon-http-put-requests-total-count $
  ds-mon-resident-time-http-put-requests-total-time )
  X-ORIGIN 'OpenDJ Directory Server' )
objectClasses: ( 1.3.6.1.4.1.36733.2.1.2.14
  NAME 'ds-cfg-pbkdf2-password-storage-scheme'
opends/src/server/org/opends/server/loggers/HTTPRequestInfo.java
@@ -26,131 +26,88 @@
 */
package org.opends.server.loggers;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.http.HttpServletRequest;
/**
 * Contains the information required for logging the HTTP request.
 */
public class HTTPRequestInfo
public interface HTTPRequestInfo
{
  /** The client's host. */
  private final String remoteHost;
  /** The client's address. */
  private final String remoteAddress;
  /** The protocol used for this request. */
  private final String protocol;
  /** The HTTP method/verb used for this request. */
  private final String method;
  /** The query issued by the client. */
  private final String query;
  /** The user agent used by the client. */
  private final String userAgent;
  /** The username that was used to authenticate. */
  private String authUser;
  /**
   * The HTTP status code returned to the client. Using 0 to say no status code
   * was set since it is not .
   */
  private AtomicInteger statusCode = new AtomicInteger(0);
  /**
   * The unique identifier that has been assigned to the client connection for
   * this HTTP request.
   */
  private long connectionID;
  /**
   * Constructor for this class.
   * Returns the server's host.
   *
   * @param request
   *          The {@link HttpServletRequest} for which to log the information
   * @param connectionID
   *          The unique identifier that has been assigned to the client
   *          connection for this HTTP request
   * @return the serverAddress
   */
  public HTTPRequestInfo(HttpServletRequest request, long connectionID)
  {
    this.remoteHost = request.getRemoteHost();
    this.remoteAddress = request.getRemoteAddr();
    this.method = request.getMethod();
    this.query = request.getRequestURI() + "/" + request.getQueryString();
    this.protocol = request.getProtocol();
    this.userAgent = request.getHeader("User-Agent");
    this.connectionID = connectionID;
  }
  String getServerAddress();
  /**
   * Returns the client's host.
   * Returns the server's host.
   *
   * @return the remoteHost
   * @return the serverHost
   */
  public String getRemoteHost()
  {
    return remoteHost;
  }
  String getServerHost();
  /**
   * Returns the server's port.
   *
   * @return the serverPort
   */
  int getServerPort();
  /**
   * Returns the client's address.
   *
   * @return the remoteAddress
   * @return the clientAddress
   */
  public String getRemoteAddress()
  {
    return remoteAddress;
  }
  String getClientAddress();
  /**
   * Returns the client's host.
   *
   * @return the clientHost
   */
  String getClientHost();
  /**
   * Returns the client's port.
   *
   * @return the clientPort
   */
  int getClientPort();
  /**
   * Returns the protocol used for this request.
   *
   * @return the protocol
   */
  public String getProtocol()
  {
    return protocol;
  }
  String getProtocol();
  /**
   * Returns the HTTP method/verb used for this request.
   *
   * @return the method
   */
  public String getMethod()
  {
    return method;
  }
  String getMethod();
  /**
   * Returns the query issued by the client.
   *
   * @return the query
   */
  public String getQuery()
  {
    return query;
  }
  String getQuery();
  /**
   * Returns the user agent used by the client.
   *
   * @return the userAgent
   */
  public String getUserAgent()
  {
    return userAgent;
  }
  String getUserAgent();
  /**
   * Returns the username that was used to authenticate.
   *
   * @return the authUser
   */
  public String getAuthUser()
  {
    return authUser;
  }
  String getAuthUser();
  /**
   * Sets the username that was used to authenticate.
@@ -158,21 +115,14 @@
   * @param authUser
   *          the authUser to set
   */
  public void setAuthUser(String authUser)
  {
    this.authUser = authUser;
  }
  void setAuthUser(String authUser);
  /**
   * Returns the HTTP status code returned to the client.
   *
   * @return the statusCode
   */
  public int getStatusCode()
  {
    int sc = statusCode.get();
    return sc != 0 ? sc : 200;
  }
  int getStatusCode();
  /**
   * Returns the unique identifier that has been assigned to the client
@@ -181,10 +131,7 @@
   * @return The unique identifier that has been assigned to the client
   *         connection for this HTTP request
   */
  public long getConnectionID()
  {
    return this.connectionID;
  }
  long getConnectionID();
  /**
   * Logs the current request info in the HTTP access log.
@@ -192,12 +139,6 @@
   * @param statusCode
   *          the HTTP status code that was returned to the client.
   */
  public void log(int statusCode)
  {
    if (this.statusCode.compareAndSet(0, statusCode))
    { // this request was not logged before
      HTTPAccessLogger.logRequestInfo(this);
    }
  }
  void log(int statusCode);
}
opends/src/server/org/opends/server/loggers/TextHTTPAccessLogPublisher.java
@@ -379,8 +379,12 @@
  public void logRequestInfo(HTTPRequestInfo ri)
  {
    final Map<String, Object> fields = new HashMap<String, Object>();
    fields.put("cs-host", ri.getRemoteHost());
    fields.put("c-ip", ri.getRemoteAddress());
    fields.put("c-ip", ri.getClientAddress());
    fields.put("cs-host", ri.getClientHost());
    fields.put("c-port", ri.getClientPort());
    fields.put("s-ip", ri.getServerAddress());
    fields.put("s-computername", ri.getServerHost());
    fields.put("s-port", ri.getServerPort());
    fields.put("cs-username", ri.getAuthUser());
    fields.put("datetime", TimeThread.getUserDefinedTime(timeStampFormat));
    fields.put("cs-method", ri.getMethod());
opends/src/server/org/opends/server/protocols/http/CollectClientConnectionsFilter.java
@@ -66,7 +66,6 @@
import org.forgerock.opendj.rest2ldap.servlet.Rest2LDAPContextFactory;
import org.opends.messages.Message;
import org.opends.server.admin.std.server.ConnectionHandlerCfg;
import org.opends.server.loggers.HTTPRequestInfo;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.schema.SchemaConstants;
import org.opends.server.types.AddressMask;
@@ -99,8 +98,6 @@
    private String userName;
    /** Used for the bind request when credentials are specified. */
    private String password;
    /** Request information for logging. */
    private HTTPRequestInfo requestInfo;
  }
  /**
@@ -181,7 +178,7 @@
    @Override
    public void handleResult(BindResult result)
    {
      ctx.requestInfo.setAuthUser(ctx.userName);
      ctx.clientConnection.setAuthUser(ctx.userName);
      final AuthenticationInfo authInfo =
          new AuthenticationInfo(to(resultEntry), to(resultEntry.getName()),
@@ -254,7 +251,7 @@
      @Override
      public void setStatus(int sc)
      {
        ctx.requestInfo.log(sc);
        ctx.clientConnection.log(sc);
        super.setStatus(sc);
      }
@@ -263,7 +260,7 @@
      @Override
      public void setStatus(int sc, String sm)
      {
        ctx.requestInfo.log(sc);
        ctx.clientConnection.log(sc);
        super.setStatus(sc, sm);
      }
    };
@@ -276,12 +273,10 @@
    this.connectionHandler.addClientConnection(clientConnection);
    ctx.clientConnection = clientConnection;
    ctx.requestInfo =
        new HTTPRequestInfo(ctx.request, clientConnection.getConnectionID());
    if (this.connectionHandler.keepStats()) {
      this.connectionHandler.getStatTracker().addRequest(
          ctx.request.getMethod());
          ctx.clientConnection.getMethod());
    }
    try
@@ -294,8 +289,7 @@
      // checked.
      logConnect(clientConnection);
      ctx.connection =
          new SdkConnectionAdapter(clientConnection, ctx.requestInfo);
      ctx.connection = new SdkConnectionAdapter(clientConnection);
      final String[] userPassword = extractUsernamePassword(request);
      if (userPassword != null && userPassword.length == 2)
@@ -358,7 +352,7 @@
    }
    finally
    {
      ctx.requestInfo.log(statusCode);
      ctx.clientConnection.log(statusCode);
      if (ctx.asyncContext != null)
      {
@@ -390,7 +384,7 @@
    }
    finally
    {
      ctx.requestInfo.log(ex.getCode());
      ctx.clientConnection.log(ex.getCode());
      if (ctx.asyncContext != null)
      {
opends/src/server/org/opends/server/protocols/http/HTTPClientConnection.java
@@ -37,9 +37,10 @@
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.ResultHandler;
@@ -57,6 +58,8 @@
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.loggers.HTTPAccessLogger;
import org.opends.server.loggers.HTTPRequestInfo;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.protocols.ldap.AddResponseProtocolOp;
import org.opends.server.protocols.ldap.BindResponseProtocolOp;
@@ -90,7 +93,8 @@
 * connection that will be accepted by an instance of the HTTP connection
 * handler.
 */
final class HTTPClientConnection extends ClientConnection
final class HTTPClientConnection extends ClientConnection implements
    HTTPRequestInfo
{
  // TODO JNR Confirm with Matt that persistent searches are inapplicable to
@@ -185,9 +189,27 @@
  /** The protocol in use for this client connection. */
  private String protocol;
  /** The HTTP method/verb used for this request. */
  private final String method;
  /** The query issued by the client. */
  private final String query;
  /** The user agent used by the client. */
  private final String userAgent;
  /** The username that was used to authenticate. */
  private String authUser;
  /**
   * The HTTP status code returned to the client. Using 0 to say no status code
   * was set since it is not .
   */
  private AtomicInteger statusCode = new AtomicInteger(0);
  /** The client (remote) address. */
  private String clientAddress;
  /** The client (remote) host name. */
  private String clientHost;
  /** The client (remote) port. */
  private int clientPort;
@@ -197,6 +219,9 @@
  /** The server (local) address. */
  private String serverAddress;
  /** The server (local) host name. */
  private String serverHost;
  /** The server (local) port. */
  private int serverPort;
@@ -218,13 +243,12 @@
   *          represents this client connection.
   */
  public HTTPClientConnection(HTTPConnectionHandler connectionHandler,
      ServletRequest request)
      HttpServletRequest request)
  {
    this.connectionHandler = connectionHandler;
    // memoize all the fields we need from the request before Grizzly decides to
    // recycle it
    this.protocol = request.getProtocol();
    this.clientAddress = request.getRemoteAddr();
    this.clientPort = request.getRemotePort();
    this.serverAddress = request.getLocalAddr();
@@ -234,6 +258,10 @@
    this.isSecure = request.isSecure();
    this.securityStrengthFactor =
        calcSSF(request.getAttribute(SERVLET_SSF_CONSTANT));
    this.method = request.getMethod();
    this.query = request.getRequestURI() + "/" + request.getQueryString();
    this.protocol = request.getProtocol();
    this.userAgent = request.getHeader("User-Agent");
    this.statTracker = this.connectionHandler.getStatTracker();
@@ -249,6 +277,13 @@
  /** {@inheritDoc} */
  @Override
  public String getAuthUser()
  {
    return this.authUser;
  }
  /** {@inheritDoc} */
  @Override
  public long getConnectionID()
  {
    return connectionID;
@@ -277,6 +312,13 @@
  /** {@inheritDoc} */
  @Override
  public String getClientHost()
  {
    return clientHost;
  }
  /** {@inheritDoc} */
  @Override
  public int getClientPort()
  {
    return clientPort;
@@ -291,6 +333,13 @@
  /** {@inheritDoc} */
  @Override
  public String getServerHost()
  {
    return serverHost;
  }
  /** {@inheritDoc} */
  @Override
  public int getServerPort()
  {
    return serverPort;
@@ -332,6 +381,7 @@
      {
        time = operation.getProcessingTime();
      }
      this.statTracker.updateRequestMonitoringData(getMethod(), time);
      this.statTracker.updateOperationMonitoringData(operation
          .getOperationType(), time);
    }
@@ -451,6 +501,13 @@
    throw new RuntimeException("Not implemented");
  }
  /** {@inheritDoc} */
  @Override
  public void setAuthUser(String authUser)
  {
    this.authUser = authUser;
  }
  /**
   * {@inheritDoc}
   *
@@ -511,6 +568,34 @@
  /** {@inheritDoc} */
  @Override
  public String getMethod()
  {
    return this.method;
  }
  /** {@inheritDoc} */
  @Override
  public String getQuery()
  {
    return this.query;
  }
  /** {@inheritDoc} */
  @Override
  public int getStatusCode()
  {
    return this.statusCode.get();
  }
  /** {@inheritDoc} */
  @Override
  public String getUserAgent()
  {
    return this.userAgent;
  }
  /** {@inheritDoc} */
  @Override
  public Collection<Operation> getOperationsInProgress()
  {
    Collection<OperationWithFutureResult> values =
@@ -772,4 +857,14 @@
  {
    return true;
  }
  /** {@inheritDoc} */
  @Override
  public void log(int statusCode)
  {
    if (this.statusCode.compareAndSet(0, statusCode))
    { // this request was not logged before
      HTTPAccessLogger.logRequestInfo(this);
    }
  }
}
opends/src/server/org/opends/server/protocols/http/HTTPStatistics.java
@@ -32,6 +32,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.opends.server.protocols.ldap.LDAPStatistics;
import org.opends.server.types.Attribute;
@@ -45,22 +46,34 @@
{
  /**
   * Map containing the total number of requests.
   * Map containing the total number of requests per HTTP methods.
   * <p>
   * key: HTTP verb => value: number of requests for that verb.
   * key: HTTP method => value: number of requests for that method.
   * </p>
   * Not using a ConcurrentMap implementation because the keys are static. The
   * keys are static because they need to be listed in the schema which is
   * Not using a ConcurrentMap implementation here because the keys are static.
   * The keys are static because they need to be listed in the schema which is
   * static.
   */
  private Map<String, AtomicInteger> nbRequests =
  private Map<String, AtomicInteger> requestMethodsTotalCount =
      new HashMap<String, AtomicInteger>();
  /**
   * Map containing the total execution time for the requests per HTTP methods.
   * <p>
   * key: HTTP method => value: total execution time for requests using that
   * method.
   * </p>
   * Not using a ConcurrentMap implementation here because the keys are static.
   * The keys are static because they need to be listed in the schema which is
   * static.
   */
  private Map<String, AtomicLong> requestMethodsTotalTime =
      new HashMap<String, AtomicLong>();
  /**
   * Total number of requests. The total number may be different than the sum of
   * the supported HTTP methods above because clients could use unsupported HTTP
   * verbs.
   * methods.
   */
  private AtomicInteger nbRequestsTotalCount = new AtomicInteger(0);
  private AtomicInteger requestsTotalCount = new AtomicInteger(0);
  /**
   * Constructor for this class.
@@ -77,7 +90,8 @@
        Arrays.asList("delete", "get", "patch", "post", "put");
    for (String method : supportedHttpMethods)
    {
      nbRequests.put(method, new AtomicInteger(0));
      requestMethodsTotalCount.put(method, new AtomicInteger(0));
      requestMethodsTotalTime.put(method, new AtomicLong(0));
    }
  }
@@ -85,7 +99,9 @@
  @Override
  public void clearStatistics()
  {
    this.nbRequests.clear();
    this.requestMethodsTotalCount.clear();
    this.requestMethodsTotalTime.clear();
    this.requestsTotalCount.set(0);
    super.clearStatistics();
  }
@@ -95,34 +111,45 @@
  public List<Attribute> getMonitorData()
  {
    // first take a snapshot of all the data as fast as possible
    final Map<String, Integer> snapshot = new HashMap<String, Integer>();
    for (Entry<String, AtomicInteger> entry : this.nbRequests.entrySet())
    final int totalCount = this.requestsTotalCount.get();
    final Map<String, Integer> totalCountsSnapshot =
        new HashMap<String, Integer>();
    for (Entry<String, AtomicInteger> entry : this.requestMethodsTotalCount
        .entrySet())
    {
      snapshot.put(entry.getKey(), entry.getValue().get());
      totalCountsSnapshot.put(entry.getKey(), entry.getValue().get());
    }
    final Map<String, Long> totalTimesSnapshot = new HashMap<String, Long>();
    for (Entry<String, AtomicLong> entry1 : this.requestMethodsTotalTime
        .entrySet())
    {
      totalTimesSnapshot.put(entry1.getKey(), entry1.getValue().get());
    }
    // do the same with the underlying data
    final List<Attribute> results = super.getMonitorData();
    // then add the snapshot data to the monitoring data
    int total = 0;
    for (Entry<String, Integer> entry : snapshot.entrySet())
    {
      final String httpMethod = entry.getKey();
      final Integer nb = entry.getValue();
      final String number = nb.toString();
      // nb should never be null since we only allow supported HTTP methods
      total += nb;
      results.add(createAttribute("ds-mon-http-" + httpMethod
          + "-requests-total-count", number));
    }
    addAll(results, totalCountsSnapshot, "ds-mon-http-",
        "-requests-total-count");
    addAll(results, totalTimesSnapshot, "ds-mon-resident-time-http-",
        "-requests-total-time");
    results.add(createAttribute("ds-mon-http-requests-total-count", Integer
        .toString(total)));
        .toString(totalCount)));
    return results;
  }
  private void addAll(final List<Attribute> results,
      final Map<String, ?> toOutput, String prefix, String suffix)
  {
    for (Entry<String, ?> entry : toOutput.entrySet())
    {
      final String httpMethod = entry.getKey();
      final String nb = entry.getValue().toString();
      results.add(createAttribute(prefix + httpMethod + suffix, nb));
    }
  }
  /**
   * Adds a request to the stats using the provided HTTP method.
   *
@@ -133,12 +160,33 @@
   */
  public void addRequest(String httpMethod) throws NullPointerException
  {
    AtomicInteger nb = this.nbRequests.get(httpMethod.toLowerCase());
    AtomicInteger nb =
        this.requestMethodsTotalCount.get(httpMethod.toLowerCase());
    if (nb != null)
    {
      nb.incrementAndGet();
    } // else this is an unsupported HTTP method
    // always count any requests regardless of whether the method is supported
    this.nbRequestsTotalCount.incrementAndGet();
    this.requestsTotalCount.incrementAndGet();
  }
  /**
   * Adds to the total time of an HTTP request method.
   *
   * @param httpMethod
   *          the method of the HTTP request to add to the stats
   * @param time
   *          the time to add to the total
   * @throws NullPointerException
   *           if the httpMethod is null
   */
  public void updateRequestMonitoringData(String httpMethod, long time)
      throws NullPointerException
  {
    AtomicLong nb = this.requestMethodsTotalTime.get(httpMethod.toLowerCase());
    if (nb != null)
    {
      nb.addAndGet(time);
    } // else this is an unsupported HTTP method
  }
}
opends/src/server/org/opends/server/protocols/http/SdkConnectionAdapter.java
@@ -80,7 +80,6 @@
import org.opends.server.core.SearchOperationBasis;
import org.opends.server.core.UnbindOperation;
import org.opends.server.core.UnbindOperationBasis;
import org.opends.server.loggers.HTTPRequestInfo;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.protocols.ldap.AbandonRequestProtocolOp;
import org.opends.server.protocols.ldap.AddRequestProtocolOp;
@@ -116,9 +115,6 @@
  /** The HTTP client connection being "adapted". */
  private final HTTPClientConnection clientConnection;
  /** The HTTP request information to log. */
  private final HTTPRequestInfo requestInfo;
  /**
   * The next message ID (and operation ID) that should be used for this
   * connection.
@@ -139,14 +135,10 @@
   *
   * @param clientConnection
   *          the HTTP client connection being "adapted"
   * @param requestInfo
   *          the HTTP request information to log
   */
  public SdkConnectionAdapter(HTTPClientConnection clientConnection,
      HTTPRequestInfo requestInfo)
  public SdkConnectionAdapter(HTTPClientConnection clientConnection)
  {
    this.clientConnection = clientConnection;
    this.requestInfo = requestInfo;
    this.queueingStrategy =
        new BoundedWorkQueueStrategy(clientConnection.getConnectionHandler()
            .getCurrentConfig().getMaxConcurrentOpsPerConnection());
@@ -330,7 +322,7 @@
    // At this point, we try to log the request with OK status code.
    // If it was already logged, it will be a no op.
    this.requestInfo.log(HttpServletResponse.SC_OK);
    this.clientConnection.log(HttpServletResponse.SC_OK);
    isClosed = true;
  }