[java-opensaml] branch main updated: Migrate some general metrics support classes out of IdP.

Scott Cantor cantor.2 at osu.edu
Mon Oct 10 20:04:17 UTC 2022


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch main
in repository java-opensaml.

View the commit online:
http://git.shibboleth.net/view/?p=java-opensaml.git;a=commit;h=bc5bb05d9b1b0162330239bfc942dbefa29b70e5

The following commit(s) were added to refs/heads/main by this push:
     new bc5bb05d9 Migrate some general metrics support classes out of IdP.
bc5bb05d9 is described below

commit bc5bb05d9b1b0162330239bfc942dbefa29b70e5
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Oct 10 16:04:14 2022 -0400

    Migrate some general metrics support classes out of IdP.
---
 opensaml-core-impl/pom.xml                         |  35 ++-
 .../opensaml/core/metrics/impl/CoreGaugeSet.java   | 203 ++++++++++++++
 .../opensaml/core/metrics/impl/HTTPReporter.java   | 305 +++++++++++++++++++++
 3 files changed, 537 insertions(+), 6 deletions(-)

diff --git a/opensaml-core-impl/pom.xml b/opensaml-core-impl/pom.xml
index 779d8e207..76878c4f7 100644
--- a/opensaml-core-impl/pom.xml
+++ b/opensaml-core-impl/pom.xml
@@ -27,20 +27,37 @@
             <artifactId>opensaml-core-api</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>opensaml-security-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
 
         <dependency>
-            <groupId>commons-codec</groupId>
-            <artifactId>commons-codec</artifactId>
+            <groupId>io.dropwizard.metrics</groupId>
+            <artifactId>metrics-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.dropwizard.metrics</groupId>
+            <artifactId>metrics-json</artifactId>
         </dependency>
 
         <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+        </dependency>    
 
         <dependency>
-            <groupId>io.dropwizard.metrics</groupId>
-            <artifactId>metrics-core</artifactId>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpcore</artifactId>
         </dependency>
 
         <!-- Provided Dependencies -->
@@ -55,6 +72,12 @@
             <scope>test</scope>
         </dependency>
 
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <scope>test</scope>
+        </dependency>
+
         <dependency>
             <groupId>org.xmlunit</groupId>
             <artifactId>xmlunit-core</artifactId>
diff --git a/opensaml-core-impl/src/main/java/org/opensaml/core/metrics/impl/CoreGaugeSet.java b/opensaml-core-impl/src/main/java/org/opensaml/core/metrics/impl/CoreGaugeSet.java
new file mode 100644
index 000000000..10dd5dd08
--- /dev/null
+++ b/opensaml-core-impl/src/main/java/org/opensaml/core/metrics/impl/CoreGaugeSet.java
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opensaml.core.metrics.impl;
+
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Metric;
+import com.codahale.metrics.MetricFilter;
+import com.codahale.metrics.MetricSet;
+import com.codahale.metrics.RatioGauge;
+
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A set of gauges for core system information.
+ */
+public class CoreGaugeSet implements MetricSet, MetricFilter {
+
+    /** The map of gauges. */
+    @Nonnull @NonnullElements private final Map<String,Metric> gauges;
+    
+// Checkstyle: MethodLength OFF    
+    /** Constructor. */
+    public CoreGaugeSet() {
+        gauges = new HashMap<>();
+        
+        gauges.put(
+                "host.name",
+                new Gauge<String>() {
+                    public String getValue() {
+                        try {
+                            return InetAddress.getLocalHost().getHostName();
+                        } catch (final UnknownHostException e) {
+                            return null;
+                        }
+                    }
+                });
+
+        gauges.put(
+                "os.name",
+                new Gauge<String>() {
+                    public String getValue() {
+                        return System.getProperty("os.name");
+                    }
+                });
+        
+        gauges.put(
+                "os.version",
+                new Gauge<String>() {
+                    public String getValue() {
+                        return System.getProperty("os.version");
+                    }
+                });
+
+        gauges.put(
+                "os.arch",
+                new Gauge<String>() {
+                    public String getValue() {
+                        return System.getProperty("os.arch");
+                    }
+                });
+
+        gauges.put(
+                "java.class.path",
+                new Gauge<String>() {
+                    public String getValue() {
+                        return System.getProperty("java.class.path");
+                    }
+                });
+
+        gauges.put(
+                "java.home",
+                new Gauge<String>() {
+                    public String getValue() {
+                        return System.getProperty("java.home");
+                    }
+                });
+
+        gauges.put(
+                "java.vendor",
+                new Gauge<String>() {
+                    public String getValue() {
+                        return System.getProperty("java.vendor");
+                    }
+                });
+
+        gauges.put(
+                "java.vendor.url",
+                new Gauge<String>() {
+                    public String getValue() {
+                        return System.getProperty("java.vendor.url");
+                    }
+                });
+                
+        gauges.put(
+                "java.version",
+                new Gauge<String>() {
+                    public String getValue() {
+                        return System.getProperty("java.version");
+                    }
+                });
+
+        gauges.put(
+                "cores.available",
+                new Gauge<Integer>() {
+                    public Integer getValue() {
+                        return Runtime.getRuntime().availableProcessors();
+                    }
+                });
+
+        gauges.put(
+                "memory.free.bytes",
+                new Gauge<Long>() {
+                    public Long getValue() {
+                        return Runtime.getRuntime().freeMemory();
+                    }
+                });
+
+        gauges.put(
+                "memory.free.megs",
+                new Gauge<Long>() {
+                    public Long getValue() {
+                        return Runtime.getRuntime().freeMemory() / (1024 * 1024);
+                    }
+                });
+
+        gauges.put(
+                "memory.used.bytes",
+                new Gauge<Long>() {
+                    public Long getValue() {
+                        final Runtime runtime = Runtime.getRuntime();
+                        return runtime.totalMemory() - runtime.freeMemory();
+                    }
+                });
+
+        gauges.put(
+                "memory.used.megs",
+                new Gauge<Long>() {
+                    public Long getValue() {
+                        final Runtime runtime = Runtime.getRuntime();
+                        return (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024);
+                    }
+                });
+
+        gauges.put(
+                "memory.max.bytes",
+                new Gauge<Long>() {
+                    public Long getValue() {
+                        return Runtime.getRuntime().maxMemory();
+                    }
+                });
+
+        gauges.put(
+                "memory.max.megs",
+                new Gauge<Long>() {
+                    public Long getValue() {
+                        return Runtime.getRuntime().maxMemory() / (1024 * 1024);
+                    }
+                });
+
+        gauges.put(
+                "memory.usage",
+                new RatioGauge() {
+                    protected Ratio getRatio() {
+                        final Runtime runtime = Runtime.getRuntime();
+                        return Ratio.of(runtime.totalMemory() - runtime.freeMemory(), runtime.totalMemory());
+                    }
+                });
+    }
+// Checkstyle: MethodLength ON
+    
+    /** {@inheritDoc} */
+    public Map<String,Metric> getMetrics() {
+        return Collections.unmodifiableMap(gauges);
+    }
+
+    /** {@inheritDoc} */
+    public boolean matches(final String name, final Metric metric) {
+        return gauges.containsKey(name);
+    }
+    
+}
\ No newline at end of file
diff --git a/opensaml-core-impl/src/main/java/org/opensaml/core/metrics/impl/HTTPReporter.java b/opensaml-core-impl/src/main/java/org/opensaml/core/metrics/impl/HTTPReporter.java
new file mode 100644
index 000000000..85d937f2f
--- /dev/null
+++ b/opensaml-core-impl/src/main/java/org/opensaml/core/metrics/impl/HTTPReporter.java
@@ -0,0 +1,305 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opensaml.core.metrics.impl;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.SortedMap;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.EntityBuilder;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.entity.ContentType;
+import org.opensaml.security.httpclient.HttpClientSecurityParameters;
+import org.opensaml.security.httpclient.HttpClientSecuritySupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Histogram;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.MetricFilter;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.ScheduledReporter;
+import com.codahale.metrics.Timer;
+import com.codahale.metrics.json.MetricsModule;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import net.shibboleth.shared.annotation.ParameterName;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.component.InitializableComponent;
+import net.shibboleth.shared.component.UninitializedComponentException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.StringSupport;
+
+/**
+ * A metrics reporter that runs at scheduled times and posts a JSON feed of metrics to a designated endpoint.
+ */
+public class HTTPReporter extends ScheduledReporter implements InitializableComponent {
+
+    /** Default date/time format string. */
+    @Nonnull @NotEmpty public static final String DEFAULT_DT_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZZ";
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(HTTPReporter.class);
+
+    /** Registry of metrics. */
+    @Nonnull private final MetricRegistry metricRegistry;
+    
+    /** Filter to apply. */
+    @Nonnull private final MetricFilter metricFilter;
+
+    /** Rate unit. */
+    @Nonnull private final TimeUnit rateUnit;
+
+    /** Duration unit. */
+    @Nonnull private final TimeUnit durationUnit;
+
+    /** HTTP Client used to post the data. */
+    @NonnullAfterInit private HttpClient httpClient;
+    
+    /** URL to the collection point. */
+    @NonnullAfterInit @NotEmpty private String collectorURL;
+
+    /** HTTP client security parameters. */
+    @Nullable private HttpClientSecurityParameters httpClientSecurityParameters;
+    
+    /** JSON object mapper to produce output. */
+    @NonnullAfterInit private ObjectMapper jsonMapper;
+    
+    /** Formatting string for {@link DateFormat} fields. */
+    @Nullable private String dateTimeFormat;
+
+    /** Whether this component has been initialized. */
+    private boolean isInitialized;
+
+    /**
+     * Constructor.
+     *
+     * @param registry the registry of metrics to report
+     * @param name the reporter name
+     * @param filter filter to apply
+     */
+    public HTTPReporter(@Nonnull @ParameterName(name="registry") final MetricRegistry registry,
+            @Nonnull @NotEmpty @ParameterName(name="name") final String name,
+            @Nullable @ParameterName(name="filter") final MetricFilter filter) {
+        super(registry, name, filter, TimeUnit.SECONDS, TimeUnit.SECONDS);
+        metricRegistry = registry;
+        metricFilter = filter != null ? filter : MetricFilter.ALL;
+        rateUnit = TimeUnit.SECONDS;
+        durationUnit = TimeUnit.SECONDS;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param registry the registry of metrics to report
+     * @param name the reporter name
+     * @param filter filter to apply
+     * @param rUnit unit to apply to rate information
+     * @param dUnit unit to apply to duration information
+     */
+    public HTTPReporter(@Nonnull @ParameterName(name="registry") final MetricRegistry registry,
+            @Nonnull @NotEmpty @ParameterName(name="name") final String name,
+            @Nullable @ParameterName(name="filter") final MetricFilter filter,
+            @Nonnull @ParameterName(name="rUnit") final TimeUnit rUnit,
+            @Nonnull @ParameterName(name="dUnit") final TimeUnit dUnit) {
+        super(registry, name, filter, rUnit, dUnit);
+        metricRegistry = registry;
+        metricFilter = filter != null ? filter : MetricFilter.ALL;
+        rateUnit = rUnit;
+        durationUnit = dUnit;
+    }
+
+// Checkstyle: ParameterNumber OFF
+    /**
+     * Constructor.
+     *
+     * @param registry the registry of metrics to report
+     * @param name the reporter name
+     * @param filter filter to apply
+     * @param rUnit unit to apply to rate information
+     * @param dUnit unit to apply to duration information
+     * @param executor task scheduler
+     */
+    public HTTPReporter(@Nonnull @ParameterName(name="registry") final MetricRegistry registry,
+            @Nonnull @NotEmpty @ParameterName(name="name") final String name,
+            @Nullable @ParameterName(name="filter") final MetricFilter filter,
+            @Nonnull @ParameterName(name="rUnit") final TimeUnit rUnit,
+            @Nonnull @ParameterName(name="dUnit") final TimeUnit dUnit,
+            @Nonnull @ParameterName(name="executor") final ScheduledExecutorService executor) {
+        super(registry, name, filter, rUnit, dUnit, executor);
+        metricRegistry = registry;
+        metricFilter = filter != null ? filter : MetricFilter.ALL;
+        rateUnit = rUnit;
+        durationUnit = dUnit;
+    }
+// Checkstyle: ParameterNumber ON
+
+    /**
+     * Set the {@link HttpClient} to use.
+     * 
+     * @param client client to use
+     */
+    public void setHttpClient(@Nonnull final HttpClient client) {
+        doSetterPreconditions();
+        httpClient = Constraint.isNotNull(client, "HttpClient cannot be null");
+    }
+    
+    /**
+     * Set the collection point to supply the data to.
+     * 
+     * @param url URL to post data to
+     */
+    public void setCollectorURL(@Nonnull @NotEmpty final String url) {
+        doSetterPreconditions();
+        collectorURL = Constraint.isNotNull(StringSupport.trimOrNull(url), "Collector URL cannot be null or empty");
+    }
+
+    /**
+     * Set the optional client security parameters.
+     * 
+     * @param params the new client security parameters
+     */
+    public void setHttpClientSecurityParameters(@Nullable final HttpClientSecurityParameters params) {
+        doSetterPreconditions();
+        httpClientSecurityParameters = params;
+    }
+    
+    /**
+     * Set the {@link DateFormat} formatting string to apply when writing {@link DateFormat}-valued fields.
+     * 
+     * @param format formatting string
+     */
+    public void setDateTimeFormat(@Nullable @NotEmpty final String format) {
+        doSetterPreconditions();
+        dateTimeFormat = StringSupport.trimOrNull(format);
+    }
+    
+    /**
+     * Helper for a setter method to check the standard preconditions.
+     */
+    private final void doSetterPreconditions() {
+        if (!isInitialized()) {
+            throw new UninitializedComponentException(
+                    "HTTPReported has not yet been initialized and cannot be used.");
+        }
+    }
+    
+    /** {@inheritDoc} */
+    public boolean isInitialized() {
+        return isInitialized;
+    }
+
+    /** {@inheritDoc} */
+    public void initialize() throws ComponentInitializationException {
+        if (!isInitialized) {
+            if (httpClient == null || collectorURL == null) {
+                throw new ComponentInitializationException("HttpClient and collection URL cannot be null");
+            }
+
+            jsonMapper = new ObjectMapper().registerModule(
+                    new MetricsModule(rateUnit, durationUnit, true, metricFilter));
+            jsonMapper.registerModule(new JavaTimeModule());
+            jsonMapper.setDateFormat(new SimpleDateFormat(dateTimeFormat != null ? dateTimeFormat : DEFAULT_DT_FORMAT));
+            jsonMapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
+            
+            isInitialized = true;
+        }
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public void stop() {
+        super.stop();
+        httpClient = null;
+        httpClientSecurityParameters = null;
+    }
+        
+    /** {@inheritDoc} */
+    @Override
+    public void report() {
+        synchronized (this) {
+            try {
+                final HttpPost httpRequest = new HttpPost(collectorURL);
+                final HttpClientContext httpContext = buildHttpContext(httpRequest);
+
+                final ByteArrayOutputStream output = new ByteArrayOutputStream(4096);
+                final ObjectWriter writer = jsonMapper.writer();
+                writer.writeValue(output, metricRegistry);
+
+                // Construct streamed request body.
+                final EntityBuilder entityBuilder = EntityBuilder.create();
+                entityBuilder.setContentType(ContentType.APPLICATION_JSON);
+                entityBuilder.setBinary(output.toByteArray());
+                httpRequest.setEntity(entityBuilder.build());
+
+                final HttpResponse response = httpClient.execute(httpRequest, httpContext);
+                HttpClientSecuritySupport.checkTLSCredentialEvaluated(httpContext, httpRequest.getURI().getScheme());
+                
+                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+                    log.debug("Metrics delivered successfully to collector");
+                } else {
+                    log.error("Collector responded with HTTP status {}", response.getStatusLine().getStatusCode());
+                }
+            } catch (final IOException e) {
+                log.error("Error sending metric registry to collection point {}", collectorURL, e);
+            }
+        }
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public void report(final SortedMap<String, Gauge> gauges, final SortedMap<String, Counter> counters,
+            final SortedMap<String, Histogram> histograms, final SortedMap<String, Meter> meters,
+            final SortedMap<String, Timer> timers) {
+        throw new UnsupportedOperationException("The per-metric report method should never be called.");
+    }
+
+
+    /**
+     * Build the {@link HttpClientContext} instance to be used by the HttpClient.
+     * 
+     * @param request the HTTP client request
+     * @return the client context instance
+     */
+    @Nonnull private HttpClientContext buildHttpContext(@Nonnull final HttpUriRequest request) {
+        final HttpClientContext clientContext = HttpClientContext.create();
+        HttpClientSecuritySupport.marshalSecurityParameters(clientContext, httpClientSecurityParameters, false);
+        HttpClientSecuritySupport.addDefaultTLSTrustEngineCriteria(clientContext, request);
+        return clientContext;
+    }
+
+}
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list