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; }