From 9460e3eb5f025af1dd4f5642046ac6ba5b92e39c Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Mon, 13 May 2013 12:35:26 +0000
Subject: [PATCH] OPENDJ-858 (CR-1651) Add stats tracking to HTTP client connections
---
opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPClientConnection.java | 151 ++++++++++++
opendj-sdk/opends/src/server/org/opends/server/protocols/http/SdkConnectionAdapter.java | 94 +++++++
opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/HTTPConnectionHandlerConfiguration.xml | 26 ++
opendj-sdk/opends/resource/config/config.ldif | 2
opendj-sdk/opends/src/admin/messages/HTTPConnectionHandlerCfgDefn.properties | 2
opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPStatistics.java | 10
opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPStatistics.java | 144 ++++++++++++
opendj-sdk/opends/src/server/org/opends/server/protocols/http/CollectClientConnectionsFilter.java | 5
opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPConnectionHandler.java | 107 +++++++-
opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPStatsProbe.java | 76 ++++++
opendj-sdk/opends/resource/schema/02-config.ldif | 46 +++
11 files changed, 630 insertions(+), 33 deletions(-)
diff --git a/opendj-sdk/opends/resource/config/config.ldif b/opendj-sdk/opends/resource/config/config.ldif
index 8c659e0..4a4e40d 100644
--- a/opendj-sdk/opends/resource/config/config.ldif
+++ b/opendj-sdk/opends/resource/config/config.ldif
@@ -487,7 +487,7 @@
ds-cfg-listen-address: 0.0.0.0
ds-cfg-listen-port: 8080
ds-cfg-accept-backlog: 128
-#ds-cfg-keep-stats: true
+ds-cfg-keep-stats: true
ds-cfg-use-tcp-keep-alive: true
ds-cfg-use-tcp-no-delay: true
ds-cfg-allow-tcp-reuse-address: true
diff --git a/opendj-sdk/opends/resource/schema/02-config.ldif b/opendj-sdk/opends/resource/schema/02-config.ldif
index 76a3a62..7967342 100644
--- a/opendj-sdk/opends/resource/schema/02-config.ldif
+++ b/opendj-sdk/opends/resource/schema/02-config.ldif
@@ -3677,6 +3677,42 @@
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
SINGLE-VALUE
X-ORIGIN 'OpenDJ Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.120
+ NAME 'ds-mon-http-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.121
+ 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.122
+ 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.123
+ 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.124
+ 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.125
+ 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' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
NAME 'ds-cfg-access-control-handler'
SUP top
@@ -3838,7 +3874,7 @@
STRUCTURAL
MUST ds-cfg-listen-port
MAY ( ds-cfg-listen-address $
-# ds-cfg-keep-stats $
+ ds-cfg-keep-stats $
ds-cfg-use-tcp-keep-alive $
ds-cfg-use-tcp-no-delay $
ds-cfg-allow-tcp-reuse-address $
@@ -5591,7 +5627,13 @@
ds-mon-abandon-operations-total-count $
ds-mon-resident-time-abandon-operations-total-time $
ds-mon-extended-operations-total-count $
- ds-mon-resident-time-extended-operations-total-time )
+ ds-mon-resident-time-extended-operations-total-time $
+ ds-mon-http-requests-total-count $
+ ds-mon-http-delete-requests-total-count $
+ ds-mon-http-get-requests-total-count $
+ ds-mon-http-patch-requests-total-count $
+ ds-mon-http-post-requests-total-count $
+ ds-mon-http-put-requests-total-count )
X-ORIGIN 'OpenDJ Directory Server' )
objectClasses: ( 1.3.6.1.4.1.36733.2.1.2.14
NAME 'ds-cfg-pbkdf2-password-storage-scheme'
diff --git a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/HTTPConnectionHandlerConfiguration.xml b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/HTTPConnectionHandlerConfiguration.xml
index ae4d7fe..d8aff43 100644
--- a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/HTTPConnectionHandlerConfiguration.xml
+++ b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/HTTPConnectionHandlerConfiguration.xml
@@ -214,6 +214,32 @@
</ldap:attribute>
</adm:profile>
</adm:property>
+ <adm:property name="keep-stats">
+ <adm:synopsis>
+ Indicates whether the
+ <adm:user-friendly-name />
+ should keep statistics.
+ </adm:synopsis>
+ <adm:description>
+ If enabled, the
+ <adm:user-friendly-name />
+ maintains statistics about the number and types of operations
+ requested over HTTP and the amount of data sent and received.
+ </adm:description>
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>true</adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:boolean />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:name>ds-cfg-keep-stats</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
<adm:property name="max-request-size" advanced="true">
<adm:synopsis>
Specifies the size in bytes of the largest HTTP request message that will
diff --git a/opendj-sdk/opends/src/admin/messages/HTTPConnectionHandlerCfgDefn.properties b/opendj-sdk/opends/src/admin/messages/HTTPConnectionHandlerCfgDefn.properties
index 65f6e25..577968f 100644
--- a/opendj-sdk/opends/src/admin/messages/HTTPConnectionHandlerCfgDefn.properties
+++ b/opendj-sdk/opends/src/admin/messages/HTTPConnectionHandlerCfgDefn.properties
@@ -24,6 +24,8 @@
property.denied-client.requires-admin-action.synopsis=Changes to this property take effect immediately and do not interfere with connections that may have already been established.
property.enabled.synopsis=Indicates whether the HTTP Connection Handler is enabled.
property.java-class.synopsis=Specifies the fully-qualified name of the Java class that provides the HTTP Connection Handler implementation.
+property.keep-stats.synopsis=Indicates whether the HTTP Connection Handler should keep statistics.
+property.keep-stats.description=If enabled, the HTTP Connection Handler maintains statistics about the number and types of operations requested over HTTP and the amount of data sent and received.
property.key-manager-provider.synopsis=Specifies the name of the key manager that should be used with this HTTP Connection Handler .
property.key-manager-provider.requires-admin-action.synopsis=Changes to this property take effect immediately, but only for subsequent attempts to access the key manager provider for associated client connections.
property.key-manager-provider.syntax.aggregation.constraint-synopsis=The referenced key manager provider must be enabled when the HTTP Connection Handler is enabled and configured to use SSL.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/http/CollectClientConnectionsFilter.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/CollectClientConnectionsFilter.java
index 7d0acaa..a7738ea 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/protocols/http/CollectClientConnectionsFilter.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/CollectClientConnectionsFilter.java
@@ -279,6 +279,11 @@
ctx.requestInfo =
new HTTPRequestInfo(ctx.request, clientConnection.getConnectionID());
+ if (this.connectionHandler.keepStats()) {
+ this.connectionHandler.getStatTracker().addRequest(
+ ctx.request.getMethod());
+ }
+
try
{
if (!canProcessRequest(request, clientConnection))
diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPClientConnection.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPClientConnection.java
index 1b0244c..97d1854 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPClientConnection.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPClientConnection.java
@@ -48,9 +48,28 @@
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import org.opends.server.api.ClientConnection;
+import org.opends.server.core.AddOperation;
+import org.opends.server.core.BindOperation;
+import org.opends.server.core.CompareOperation;
+import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.ExtendedOperation;
+import org.opends.server.core.ModifyDNOperation;
+import org.opends.server.core.ModifyOperation;
import org.opends.server.core.SearchOperation;
import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.protocols.ldap.AddResponseProtocolOp;
+import org.opends.server.protocols.ldap.BindResponseProtocolOp;
+import org.opends.server.protocols.ldap.CompareResponseProtocolOp;
+import org.opends.server.protocols.ldap.DeleteResponseProtocolOp;
+import org.opends.server.protocols.ldap.ExtendedResponseProtocolOp;
+import org.opends.server.protocols.ldap.LDAPMessage;
+import org.opends.server.protocols.ldap.ModifyDNResponseProtocolOp;
+import org.opends.server.protocols.ldap.ModifyResponseProtocolOp;
+import org.opends.server.protocols.ldap.ProtocolOp;
+import org.opends.server.protocols.ldap.SearchResultDoneProtocolOp;
+import org.opends.server.protocols.ldap.SearchResultEntryProtocolOp;
+import org.opends.server.protocols.ldap.SearchResultReferenceProtocolOp;
import org.opends.server.types.CancelRequest;
import org.opends.server.types.CancelResult;
import org.opends.server.types.DN;
@@ -59,6 +78,7 @@
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.IntermediateResponse;
import org.opends.server.types.Operation;
+import org.opends.server.types.OperationType;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchResultEntry;
import org.opends.server.types.SearchResultReference;
@@ -76,7 +96,6 @@
// TODO JNR Confirm with Matt that persistent searches are inapplicable to
// Rest2LDAP.
// TODO JNR Should I override getIdleTime()?
- // TODO JNR Implement stats
/**
* Class grouping together an {@link Operation} and its associated
@@ -128,6 +147,12 @@
private boolean disconnectRequested;
/**
+ * Indicates whether the connection should keep statistics regarding the
+ * operations that it is performing.
+ */
+ private final boolean keepStats;
+
+ /**
* The Map (messageID => {@link OperationWithFutureResult}) of all operations
* currently in progress on this connection.
*/
@@ -153,6 +178,10 @@
/** The reference to the connection handler that accepted this connection. */
private final HTTPConnectionHandler connectionHandler;
+ /** The statistics tracker associated with this client connection. */
+ private final HTTPStatistics statTracker;
+ private boolean useNanoTime = false;
+
/** The protocol in use for this client connection. */
private String protocol;
@@ -206,6 +235,15 @@
this.securityStrengthFactor =
calcSSF(request.getAttribute(SERVLET_SSF_CONSTANT));
+ this.statTracker = this.connectionHandler.getStatTracker();
+
+ this.keepStats = connectionHandler.keepStats();
+ if (this.keepStats)
+ {
+ this.statTracker.updateConnect();
+ this.useNanoTime = DirectoryServer.getUseNanoTime();
+ }
+
this.connectionID = DirectoryServer.newConnectionAccepted(this);
}
@@ -283,6 +321,21 @@
@Override
public void sendResponse(Operation operation)
{
+ if (keepStats)
+ {
+ long time;
+ if (useNanoTime)
+ {
+ time = operation.getProcessingNanoTime();
+ }
+ else
+ {
+ time = operation.getProcessingTime();
+ }
+ this.statTracker.updateOperationMonitoringData(operation
+ .getOperationType(), time);
+ }
+
OperationWithFutureResult op =
this.operationsInProgress.get(operation.getMessageID());
if (op != null)
@@ -290,6 +343,12 @@
try
{
op.futureResult.handleResult(getResponseResult(operation));
+
+ if (keepStats)
+ {
+ this.statTracker.updateMessageWritten(new LDAPMessage(operation
+ .getMessageID(), toResponseProtocolOp(operation)));
+ }
}
catch (ErrorResultException e)
{
@@ -298,6 +357,44 @@
}
}
+ private ProtocolOp toResponseProtocolOp(Operation operation)
+ {
+ final int resultCode = operation.getResultCode().getIntValue();
+ if (operation instanceof AddOperation)
+ {
+ return new AddResponseProtocolOp(resultCode);
+ }
+ else if (operation instanceof BindOperation)
+ {
+ return new BindResponseProtocolOp(resultCode);
+ }
+ else if (operation instanceof CompareOperation)
+ {
+ return new CompareResponseProtocolOp(resultCode);
+ }
+ else if (operation instanceof DeleteOperation)
+ {
+ return new DeleteResponseProtocolOp(resultCode);
+ }
+ else if (operation instanceof ExtendedOperation)
+ {
+ return new ExtendedResponseProtocolOp(resultCode);
+ }
+ else if (operation instanceof ModifyDNOperation)
+ {
+ return new ModifyDNResponseProtocolOp(resultCode);
+ }
+ else if (operation instanceof ModifyOperation)
+ {
+ return new ModifyResponseProtocolOp(resultCode);
+ }
+ else if (operation instanceof SearchOperation)
+ {
+ return new SearchResultDoneProtocolOp(resultCode);
+ }
+ throw new RuntimeException("Not implemented for operation " + operation);
+ }
+
/** {@inheritDoc} */
@Override
public void sendSearchEntry(SearchOperation operation,
@@ -309,6 +406,12 @@
{
((SearchResultHandler) op.futureResult.getResultHandler())
.handleEntry(from(searchEntry));
+
+ if (keepStats)
+ {
+ this.statTracker.updateMessageWritten(new LDAPMessage(operation
+ .getMessageID(), new SearchResultEntryProtocolOp(searchEntry)));
+ }
}
}
@@ -323,6 +426,13 @@
{
((SearchResultHandler) op.futureResult.getResultHandler())
.handleReference(from(searchReference));
+
+ if (keepStats)
+ {
+ this.statTracker.updateMessageWritten(new LDAPMessage(operation
+ .getMessageID(), new SearchResultReferenceProtocolOp(
+ searchReference)));
+ }
}
return connectionValid;
}
@@ -332,6 +442,12 @@
protected boolean sendIntermediateResponseMessage(
IntermediateResponse intermediateResponse)
{
+ // if (keepStats)
+ // {
+ // this.statTracker.updateMessageWritten(new LDAPMessage(
+ // intermediateResponse.getOperation().getMessageID(),
+ // new IntermediateResponseProtocolOp(intermediateResponse.getOID())));
+ // }
throw new RuntimeException("Not implemented");
}
@@ -360,11 +476,10 @@
disconnectRequested = true;
}
- // TODO JNR
- // if (keepStats)
- // {
- // statTracker.updateDisconnect();
- // }
+ if (keepStats)
+ {
+ statTracker.updateDisconnect();
+ }
if (connectionID >= 0)
{
@@ -459,6 +574,15 @@
if (previousValue != null)
{
operationsPerformed.incrementAndGet();
+
+ final Operation operation = previousValue.operation;
+ if (operation.getOperationType() == OperationType.ABANDON)
+ {
+ if (keepStats && operation.getResultCode() == ResultCode.CANCELED)
+ {
+ statTracker.updateAbandonedOperation();
+ }
+ }
}
return previousValue != null;
}
@@ -517,6 +641,11 @@
op.futureResult.handleErrorResult(ErrorResultException
.newErrorResult(org.forgerock.opendj.ldap.ResultCode.CANCELLED));
op.operation.abort(cancelRequest);
+
+ if (keepStats)
+ {
+ statTracker.updateAbandonedOperation();
+ }
}
catch (Exception e)
{ // make sure all operations are cancelled, no matter what
@@ -613,6 +742,16 @@
buffer.append(getServerAddress()).append(":").append(getServerPort());
}
+ /**
+ * Returns the statTracker for this connection handler.
+ *
+ * @return the statTracker for this connection handler
+ */
+ public HTTPStatistics getStatTracker()
+ {
+ return statTracker;
+ }
+
/** {@inheritDoc} */
@Override
public int getSSF()
diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPConnectionHandler.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPConnectionHandler.java
index e4e4399..c44f70d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPConnectionHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPConnectionHandler.java
@@ -70,9 +70,12 @@
import org.forgerock.opendj.rest2ldap.AuthorizationPolicy;
import org.forgerock.opendj.rest2ldap.Rest2LDAP;
import org.forgerock.opendj.rest2ldap.servlet.Rest2LDAPContextFactory;
+import org.glassfish.grizzly.http.HttpProbe;
import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.HttpServerMonitoringConfig;
import org.glassfish.grizzly.http.server.NetworkListener;
import org.glassfish.grizzly.http.server.ServerConfiguration;
+import org.glassfish.grizzly.monitoring.MonitoringConfig;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
import org.glassfish.grizzly.servlet.ServletRegistration;
import org.glassfish.grizzly.servlet.WebappContext;
@@ -94,6 +97,7 @@
import org.opends.server.extensions.NullTrustManagerProvider;
import org.opends.server.loggers.HTTPAccessLogger;
import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.monitors.ClientConnectionMonitorProvider;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
@@ -144,6 +148,9 @@
/** The HTTP server embedded in OpenDJ. */
private HttpServer httpServer;
+ /** The HTTP probe that collects stats. */
+ private HTTPStatsProbe httpProbe;
+
/**
* Holds the current client connections. Using {@link ConcurrentHashMap} to
* ensure no concurrent reads/writes can happen and adds/removes are fast. We
@@ -152,6 +159,15 @@
private Map<ClientConnection, ClientConnection> clientConnections =
new ConcurrentHashMap<ClientConnection, ClientConnection>();
+ /** The set of statistics collected for this connection handler. */
+ private HTTPStatistics statTracker;
+
+ /**
+ * The client connection monitor provider associated with this connection
+ * handler.
+ */
+ private ClientConnectionMonitorProvider connMonitor;
+
/** The unique name assigned to this connection handler. */
private String handlerName;
@@ -245,6 +261,22 @@
messages);
}
+ if (config.isEnabled() && this.currentConfig.isEnabled() && isListening())
+ { // server was running and will still be running
+ // if the "enabled" was flipped, leave it to the stop / start server to
+ // handle it
+ if (!this.currentConfig.isKeepStats() && config.isKeepStats())
+ { // it must now keep stats while it was not previously
+ setHttpStatsProbe();
+ }
+ else if (this.currentConfig.isKeepStats() && !config.isKeepStats()
+ && this.httpProbe != null)
+ { // it must NOT keep stats anymore
+ getHttpConfig().removeProbes(this.httpProbe);
+ this.httpProbe = null;
+ }
+ }
+
this.initConfig = config;
this.currentConfig = config;
@@ -324,19 +356,17 @@
// Unregister this as a change listener.
currentConfig.removeHTTPChangeListener(this);
- // TODO JNR
- // if (connMonitor != null)
- // {
- // String lowerName = toLowerCase(connMonitor.getMonitorInstanceName());
- // DirectoryServer.deregisterMonitorProvider(lowerName);
- // }
- //
- // if (statTracker != null)
- // {
- // String lowerName = toLowerCase(statTracker.getMonitorInstanceName());
- // DirectoryServer.deregisterMonitorProvider(lowerName);
- // }
+ if (connMonitor != null)
+ {
+ String lowerName = toLowerCase(connMonitor.getMonitorInstanceName());
+ DirectoryServer.deregisterMonitorProvider(lowerName);
+ }
+ if (statTracker != null)
+ {
+ String lowerName = toLowerCase(statTracker.getMonitorInstanceName());
+ DirectoryServer.deregisterMonitorProvider(lowerName);
+ }
}
/** {@inheritDoc} */
@@ -451,6 +481,16 @@
return handlerName;
}
+ /**
+ * Retrieves the set of statistics maintained by this connection handler.
+ *
+ * @return The set of statistics maintained by this connection handler.
+ */
+ public HTTPStatistics getStatTracker()
+ {
+ return statTracker;
+ }
+
/** {@inheritDoc} */
@Override
public void initializeConnectionHandler(HTTPConnectionHandlerCfg config)
@@ -484,14 +524,14 @@
}
// TODO JNR
- // handle ds-cfg-keep-stats
// handle ds-cfg-num-request-handlers??
- // // Create and register monitors.
- // statTracker = new LDAPStatistics(handlerName + " Statistics");
- // DirectoryServer.registerMonitorProvider(statTracker);
- //
- // connMonitor = new ClientConnectionMonitorProvider(this);
- // DirectoryServer.registerMonitorProvider(connMonitor);
+
+ // Create and register monitors.
+ statTracker = new HTTPStatistics(handlerName + " Statistics");
+ DirectoryServer.registerMonitorProvider(statTracker);
+
+ connMonitor = new ClientConnectionMonitorProvider(this);
+ DirectoryServer.registerMonitorProvider(connMonitor);
// Register this as a change listener.
config.addHTTPChangeListener(this);
@@ -612,6 +652,17 @@
return isConfigurationAcceptable(configuration, unacceptableReasons);
}
+ /**
+ * Indicates whether this connection handler should maintain usage statistics.
+ *
+ * @return <CODE>true</CODE> if this connection handler should maintain usage
+ * statistics, or <CODE>false</CODE> if not.
+ */
+ public boolean keepStats()
+ {
+ return currentConfig.isKeepStats();
+ }
+
/** {@inheritDoc} */
@Override
public void processServerShutdown(Message reason)
@@ -715,6 +766,10 @@
this.httpServer.getServerConfiguration();
serverConfig.setMaxBufferedPostSize(requestSize);
serverConfig.setMaxFormPostSize(requestSize);
+ if (keepStats())
+ {
+ setHttpStatsProbe();
+ }
try
{
@@ -799,6 +854,19 @@
}
}
+ private void setHttpStatsProbe()
+ {
+ httpProbe = new HTTPStatsProbe(this.statTracker);
+ getHttpConfig().addProbes(httpProbe);
+ }
+
+ private MonitoringConfig<HttpProbe> getHttpConfig()
+ {
+ final HttpServerMonitoringConfig monitoringCfg =
+ this.httpServer.getServerConfiguration().getMonitoringConfig();
+ return monitoringCfg.getHttpConfig();
+ }
+
private HTTPAuthenticationConfig getAuthenticationConfig(
final JsonValue configuration)
{
@@ -867,6 +935,7 @@
TRACER.debugInfo("Stopping HTTP server...");
this.httpServer.stop();
this.httpServer = null;
+ this.httpProbe = null;
TRACER.debugInfo("HTTP server stopped");
logError(NOTE_CONNHANDLER_STOPPED_LISTENING.get(handlerName));
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPStatistics.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPStatistics.java
new file mode 100644
index 0000000..be4a8f0
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPStatistics.java
@@ -0,0 +1,144 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2013 ForgeRock AS
+ */
+package org.opends.server.protocols.http;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.opends.server.protocols.ldap.LDAPStatistics;
+import org.opends.server.types.Attribute;
+
+/**
+ * Collects statistics for HTTP. This class inherits from {@link LDAPStatistics}
+ * to show the administrator how the underlying LDAP internal operations are
+ * performing.
+ */
+public class HTTPStatistics extends LDAPStatistics
+{
+
+ /**
+ * Map containing the total number of requests.
+ * <p>
+ * key: HTTP verb => value: number of requests for that verb.
+ * </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
+ * static.
+ */
+ private Map<String, AtomicInteger> nbRequests =
+ new HashMap<String, AtomicInteger>();
+ /**
+ * 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.
+ */
+ private AtomicInteger nbRequestsTotalCount = new AtomicInteger(0);
+
+ /**
+ * Constructor for this class.
+ *
+ * @param instanceName
+ * The name for this monitor provider instance.
+ */
+ public HTTPStatistics(String instanceName)
+ {
+ super(instanceName);
+
+ // List the HTTP methods supported by Rest2LDAP
+ final List<String> supportedHttpMethods =
+ Arrays.asList("delete", "get", "patch", "post", "put");
+ for (String method : supportedHttpMethods)
+ {
+ nbRequests.put(method, new AtomicInteger(0));
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void clearStatistics()
+ {
+ this.nbRequests.clear();
+
+ super.clearStatistics();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ 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())
+ {
+ snapshot.put(entry.getKey(), entry.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));
+ }
+ results.add(createAttribute("ds-mon-http-requests-total-count", Integer
+ .toString(total)));
+
+ return results;
+ }
+
+ /**
+ * Adds a request to the stats using the provided HTTP method.
+ *
+ * @param httpMethod
+ * the method of the HTTP request to add to the stats
+ * @throws NullPointerException
+ * if the httpMethod is null
+ */
+ public void addRequest(String httpMethod) throws NullPointerException
+ {
+ AtomicInteger nb = this.nbRequests.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();
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPStatsProbe.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPStatsProbe.java
new file mode 100644
index 0000000..91a6a91
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPStatsProbe.java
@@ -0,0 +1,76 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2013 ForgeRock AS
+ */
+package org.opends.server.protocols.http;
+
+import org.glassfish.grizzly.Buffer;
+import org.glassfish.grizzly.Connection;
+import org.glassfish.grizzly.http.HttpProbe;
+
+/**
+ * Probe that collect some statistics on the HTTP server: bytes read and bytes
+ * written to the HTTP connection. We are using
+ * {@link #onDataReceivedEvent(Connection, Buffer)} and
+ * {@link #onDataSentEvent(Connection, Buffer)} because they are the only ones
+ * that really output the number of bytes sent on the wire, including data
+ * formatting for the HTTP protocol (chunk size, etc.).
+ * <p>
+ * Use
+ * <code>curl "http://localhost:8080/users?_queryFilter=true&_prettyPrint=true"
+ * --trace-ascii output.txt</code> to trace the client-server communication.
+ * </p>
+ */
+@SuppressWarnings("rawtypes")
+final class HTTPStatsProbe extends HttpProbe.Adapter
+{
+ private final HTTPStatistics statTracker;
+
+ /**
+ * Constructs and object from this class.
+ *
+ * @param statTracker
+ * the statistic tracker
+ */
+ public HTTPStatsProbe(HTTPStatistics statTracker)
+ {
+ this.statTracker = statTracker;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onDataSentEvent(Connection connection, Buffer buffer)
+ {
+ this.statTracker.updateBytesWritten(buffer.limit());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onDataReceivedEvent(Connection connection, Buffer buffer)
+ {
+ this.statTracker.updateBytesRead(buffer.limit());
+ }
+
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/http/SdkConnectionAdapter.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/SdkConnectionAdapter.java
index ff2a1d5..737b542 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/protocols/http/SdkConnectionAdapter.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/SdkConnectionAdapter.java
@@ -58,20 +58,42 @@
import org.forgerock.opendj.ldap.responses.CompareResult;
import org.forgerock.opendj.ldap.responses.ExtendedResult;
import org.forgerock.opendj.ldap.responses.Result;
+import org.opends.server.core.AbandonOperation;
import org.opends.server.core.AbandonOperationBasis;
+import org.opends.server.core.AddOperation;
import org.opends.server.core.AddOperationBasis;
+import org.opends.server.core.BindOperation;
import org.opends.server.core.BindOperationBasis;
import org.opends.server.core.BoundedWorkQueueStrategy;
+import org.opends.server.core.CompareOperation;
import org.opends.server.core.CompareOperationBasis;
+import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DeleteOperationBasis;
+import org.opends.server.core.ExtendedOperation;
import org.opends.server.core.ExtendedOperationBasis;
+import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.ModifyDNOperationBasis;
+import org.opends.server.core.ModifyOperation;
import org.opends.server.core.ModifyOperationBasis;
import org.opends.server.core.QueueingStrategy;
+import org.opends.server.core.SearchOperation;
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;
+import org.opends.server.protocols.ldap.BindRequestProtocolOp;
+import org.opends.server.protocols.ldap.CompareRequestProtocolOp;
+import org.opends.server.protocols.ldap.DeleteRequestProtocolOp;
+import org.opends.server.protocols.ldap.ExtendedRequestProtocolOp;
+import org.opends.server.protocols.ldap.LDAPMessage;
+import org.opends.server.protocols.ldap.ModifyDNRequestProtocolOp;
+import org.opends.server.protocols.ldap.ModifyRequestProtocolOp;
+import org.opends.server.protocols.ldap.ProtocolOp;
+import org.opends.server.protocols.ldap.SearchRequestProtocolOp;
+import org.opends.server.protocols.ldap.UnbindRequestProtocolOp;
import org.opends.server.types.AuthenticationInfo;
import org.opends.server.types.ByteString;
import org.opends.server.types.DebugLogLevel;
@@ -142,6 +164,15 @@
{
operation.setInnerOperation(this.clientConnection.isInnerConnection());
+ HTTPConnectionHandler connHandler =
+ this.clientConnection.getConnectionHandler();
+ if (connHandler.keepStats())
+ {
+ connHandler.getStatTracker().updateMessageRead(
+ new LDAPMessage(operation.getMessageID(),
+ toRequestProtocolOp(operation)));
+ }
+
// need this raw cast here to fool the compiler's generic type safety
// Problem here is due to the generic type R on enqueueOperation()
clientConnection.addOperationInProgress(operation,
@@ -163,6 +194,69 @@
return futureResult;
}
+ private ProtocolOp toRequestProtocolOp(Operation operation)
+ {
+ operation.getResultCode().getIntValue();
+ if (operation instanceof AbandonOperation)
+ {
+ final AbandonOperation op = (AbandonOperation) operation;
+ return new AbandonRequestProtocolOp(op.getIDToAbandon());
+ }
+ else if (operation instanceof AddOperation)
+ {
+ final AddOperation op = (AddOperation) operation;
+ return new AddRequestProtocolOp(op.getRawEntryDN(),
+ op.getRawAttributes());
+ }
+ else if (operation instanceof BindOperation)
+ {
+ final BindOperation op = (BindOperation) operation;
+ return new BindRequestProtocolOp(op.getRawBindDN(),
+ op.getSASLMechanism(), op.getSASLCredentials());
+ }
+ else if (operation instanceof CompareOperation)
+ {
+ final CompareOperation op = (CompareOperation) operation;
+ return new CompareRequestProtocolOp(op.getRawEntryDN(), op
+ .getRawAttributeType(), op.getAssertionValue());
+ }
+ else if (operation instanceof DeleteOperation)
+ {
+ final DeleteOperation op = (DeleteOperation) operation;
+ return new DeleteRequestProtocolOp(op.getRawEntryDN());
+ }
+ else if (operation instanceof ExtendedOperation)
+ {
+ final ExtendedOperation op = (ExtendedOperation) operation;
+ return new ExtendedRequestProtocolOp(op.getRequestOID(), op
+ .getRequestValue());
+ }
+ else if (operation instanceof ModifyDNOperation)
+ {
+ final ModifyDNOperation op = (ModifyDNOperation) operation;
+ return new ModifyDNRequestProtocolOp(op.getRawEntryDN(), op
+ .getRawNewRDN(), op.deleteOldRDN(), op.getRawNewSuperior());
+ }
+ else if (operation instanceof ModifyOperation)
+ {
+ final ModifyOperation op = (ModifyOperation) operation;
+ return new ModifyRequestProtocolOp(op.getRawEntryDN(), op
+ .getRawModifications());
+ }
+ else if (operation instanceof SearchOperation)
+ {
+ final SearchOperation op = (SearchOperation) operation;
+ return new SearchRequestProtocolOp(op.getRawBaseDN(), op.getScope(), op
+ .getDerefPolicy(), op.getSizeLimit(), op.getTimeLimit(), op
+ .getTypesOnly(), op.getRawFilter(), op.getAttributes());
+ }
+ else if (operation instanceof UnbindOperation)
+ {
+ return new UnbindRequestProtocolOp();
+ }
+ throw new RuntimeException("Not implemented for operation " + operation);
+ }
+
/** {@inheritDoc} */
@Override
public FutureResult<Void> abandonAsync(AbandonRequest request)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPStatistics.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPStatistics.java
index b8174f9..cac937a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPStatistics.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPStatistics.java
@@ -35,8 +35,9 @@
import static org.opends.server.util.ServerConstants.*;
import java.util.ArrayList;
-
+import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
+
import org.opends.messages.Message;
import org.opends.server.admin.std.server.MonitorProviderCfg;
import org.opends.server.api.MonitorProvider;
@@ -198,10 +199,9 @@
* is requested.
*/
@Override
- public ArrayList<Attribute> getMonitorData()
+ public List<Attribute> getMonitorData()
{
-
- ArrayList<Attribute> attrs = new ArrayList<Attribute>();
+ List<Attribute> attrs = new ArrayList<Attribute>();
long tmpAbandonRequests = abandonRequests.get();
long tmpAddRequests = addRequests.get();
@@ -662,7 +662,7 @@
* The value to use for the attribute.
* @return the constructed attribute.
*/
- private Attribute createAttribute(String name, String value)
+ protected Attribute createAttribute(String name, String value)
{
AttributeType attrType =
DirectoryServer.getAttributeType(name.toLowerCase());
--
Gitblit v1.10.0