This is an automated email from the ASF dual-hosted git repository.
exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push:
new 5811a9c579 NIFI-11385 Added JMX Metrics REST Resource for Diagnostics
5811a9c579 is described below
commit 5811a9c579a11aa8fc8358851df33d0456a5e10c
Author: Timea Barna <[email protected]>
AuthorDate: Wed Apr 5 14:42:47 2023 +0200
NIFI-11385 Added JMX Metrics REST Resource for Diagnostics
This closes #7124
Co-authored-by: David Handermann <[email protected]>
Signed-off-by: David Handermann <[email protected]>
---
.../java/org/apache/nifi/util/NiFiProperties.java | 1 +
.../src/main/asciidoc/administration-guide.adoc | 36 +++++
.../nifi/web/api/dto/JmxMetricsResultDTO.java | 49 +++++++
.../web/api/entity/JmxMetricsResultsEntity.java | 43 ++++++
.../nifi-framework/nifi-resources/pom.xml | 1 +
.../src/main/resources/conf/nifi.properties | 5 +-
.../nifi/web/api/SystemDiagnosticsResource.java | 60 +++++++-
.../web/api/metrics/jmx/JmxMetricsCollector.java | 82 +++++++++++
.../nifi/web/api/metrics/jmx/JmxMetricsFilter.java | 59 ++++++++
.../api/metrics/jmx/JmxMetricsResultConverter.java | 66 +++++++++
.../web/api/metrics/jmx/JmxMetricsService.java | 30 ++++
.../api/metrics/jmx/StandardJmxMetricsService.java | 42 ++++++
.../src/main/resources/nifi-web-api-context.xml | 10 ++
.../web/api/metrics/jmx/JmxMetricsFilterTest.java | 127 +++++++++++++++++
.../metrics/jmx/JmxMetricsResultConverterTest.java | 158 +++++++++++++++++++++
15 files changed, 766 insertions(+), 3 deletions(-)
diff --git
a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
index d4f5c04984..681ed0a5ee 100644
---
a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
+++
b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
@@ -245,6 +245,7 @@ public class NiFiProperties extends ApplicationProperties {
public static final String WEB_REQUEST_IP_WHITELIST =
"nifi.web.request.ip.whitelist";
public static final String WEB_SHOULD_SEND_SERVER_VERSION =
"nifi.web.should.send.server.version";
public static final String WEB_REQUEST_LOG_FORMAT =
"nifi.web.request.log.format";
+ public static final String WEB_JMX_METRICS_ALLOWED_FILTER_PATTERN =
"nifi.web.jmx.metrics.allowed.filter.pattern";
// ui properties
public static final String UI_BANNER_TEXT = "nifi.ui.banner.text";
diff --git a/nifi-docs/src/main/asciidoc/administration-guide.adoc
b/nifi-docs/src/main/asciidoc/administration-guide.adoc
index 0d27c8c2ec..838fe75d7d 100644
--- a/nifi-docs/src/main/asciidoc/administration-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/administration-guide.adoc
@@ -4174,6 +4174,8 @@ request headers. The default value is:
The CustomRequestLog writes formatted messages using the following SLF4J
logger:
`org.apache.nifi.web.server.RequestLog`
+|`nifi.web.jmx.metrics.allowed.filter.pattern`|The regular expression
controlling the JMX MBean names that the REST API
+is allowed to return. The default value is empty, blocking all MBeans.
Configuring `.*` allows all registered MBeans.
|====
[[security_properties]]
@@ -4844,3 +4846,37 @@ nifi.diagnostics.on.shutdown.max.directory.size=10 MB
```
In the case of a lengthy diagnostic, NiFi may terminate before the command
execution ends. In this case, the `graceful.shutdown.seconds` property should
be set to a higher value in the `bootstrap.conf` configuration file.
+
+[[jmx_metrics]]
+== JMX Metrics
+
+It is possible to get JMX metrics using the REST API with read permissions on
system diagnostics resources.
+
+The information available depends on the registered MBeans. Metrics can
contain data related to performance indicators.
+
+Listing of MBeans is controlled using a regular expression pattern in
application properties. Leaving the
+property empty means no MBeans will be returned. The default value blocks all
MBeans and must be changed to return
+information.
+
+ nifi.web.jmx.metrics.allowed.filter.pattern=.*
+
+An optionally provided query parameter using a regular expression pattern,
will display only MBeans with matching names.
+Leaving this parameter empty means listing all MBeans except those filtered
out by the blocked filter pattern.
+
+
https://localhost:8443/nifi-api/system-diagnostics/jmx-metrics?beanNameFilter=bean.name.1|bean.name.2
+
+An example output would look like this:
+
+ [
+ {
+ "beanName" : "bean.name.1,type=type1",
+ "attributeName" : “attribute-name",
+ "attributeValue" : “attribute-value”
+ },
+ {
+ "beanName" : "bean.name.2, type=type2",
+ "attributeName" : "attribute-name",
+ "attributeValue" : integer-value
+ }
+ ]
+
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/JmxMetricsResultDTO.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/JmxMetricsResultDTO.java
new file mode 100644
index 0000000000..b36932f786
--- /dev/null
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/JmxMetricsResultDTO.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.nifi.web.api.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.xml.bind.annotation.XmlType;
+
+@XmlType(name = "jmxMetricsResults")
+public class JmxMetricsResultDTO {
+ final private String beanName;
+ final private String attributeName;
+ final private Object attributeValue;
+
+ public JmxMetricsResultDTO(final String beanName, final String
attributeName, final Object attributeValue) {
+ this.beanName = beanName;
+ this.attributeName = attributeName;
+ this.attributeValue = attributeValue;
+ }
+
+ @ApiModelProperty("The bean name of the metrics bean.")
+ public String getBeanName() {
+ return beanName;
+ }
+
+ @ApiModelProperty("The attribute name of the metrics bean's attribute.")
+ public String getAttributeName() {
+ return attributeName;
+ }
+
+ @ApiModelProperty("The attribute value of the the metrics bean's
attribute")
+ public Object getAttributeValue() {
+ return attributeValue;
+ }
+}
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/JmxMetricsResultsEntity.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/JmxMetricsResultsEntity.java
new file mode 100644
index 0000000000..4a11957f3d
--- /dev/null
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/JmxMetricsResultsEntity.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.nifi.web.api.entity;
+
+import org.apache.nifi.web.api.dto.JmxMetricsResultDTO;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import java.util.Collection;
+
+/**
+ * A serialized representation of this class can be placed in the entity body
of a request or response to or from the API. This particular entity holds a
reference to a list of JmxMetricsResult.
+ */
+@XmlRootElement(name = "jmxMetricsResult")
+public class JmxMetricsResultsEntity extends Entity {
+ private Collection<JmxMetricsResultDTO> jmxMetricsResults;
+
+ /**
+ * A collection of JmxMetricsResultDTO objects that is being serialized.
+ *
+ * @return The collection of JmxMetricsResultDTO objects
+ */
+ public Collection<JmxMetricsResultDTO> getJmxMetricsResults() {
+ return jmxMetricsResults;
+ }
+
+ public void setJmxMetricsResults(final Collection<JmxMetricsResultDTO>
jmxMetricsResults) {
+ this.jmxMetricsResults = jmxMetricsResults;
+ }
+}
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/pom.xml
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/pom.xml
index 87d7ba6183..e8700a07f8 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/pom.xml
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/pom.xml
@@ -140,6 +140,7 @@
<nifi.web.request.ip.whitelist />
<nifi.web.should.send.server.version>true</nifi.web.should.send.server.version>
<nifi.web.request.log.format>%{client}a - %u %t "%r" %s %O
"%{Referer}i" "%{User-Agent}i"</nifi.web.request.log.format>
+ <nifi.web.jmx.metrics.allowed.filter.pattern />
<!-- nifi.properties: security properties -->
<nifi.security.autoreload.enabled>false</nifi.security.autoreload.enabled>
<nifi.security.autoreload.interval>10
secs</nifi.security.autoreload.interval>
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
index a6091c965b..2f6214c519 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
@@ -178,6 +178,9 @@
nifi.web.request.ip.whitelist=${nifi.web.request.ip.whitelist}
nifi.web.should.send.server.version=${nifi.web.should.send.server.version}
nifi.web.request.log.format=${nifi.web.request.log.format}
+# Filter JMX MBeans available through the System Diagnostics REST API
+nifi.web.jmx.metrics.allowed.filter.pattern=${nifi.web.jmx.metrics.allowed.filter.pattern}
+
# Include or Exclude TLS Cipher Suites for HTTPS
nifi.web.https.ciphersuites.include=
nifi.web.https.ciphersuites.exclude=
@@ -341,8 +344,6 @@
nifi.analytics.connection.model.score.threshold=${nifi.analytics.connection.mode
nifi.monitor.long.running.task.schedule=
nifi.monitor.long.running.task.threshold=
-# Create automatic diagnostics when stopping/restarting NiFi.
-
# Enable automatic diagnostic at shutdown.
nifi.diagnostics.on.shutdown.enabled=false
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SystemDiagnosticsResource.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SystemDiagnosticsResource.java
index 8b9fed401b..7238eaddbf 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SystemDiagnosticsResource.java
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SystemDiagnosticsResource.java
@@ -28,8 +28,11 @@ import org.apache.nifi.authorization.resource.Authorizable;
import org.apache.nifi.authorization.user.NiFiUserUtils;
import org.apache.nifi.cluster.manager.NodeResponse;
import org.apache.nifi.web.NiFiServiceFacade;
+import org.apache.nifi.web.api.dto.JmxMetricsResultDTO;
import org.apache.nifi.web.api.dto.SystemDiagnosticsDTO;
+import org.apache.nifi.web.api.entity.JmxMetricsResultsEntity;
import org.apache.nifi.web.api.entity.SystemDiagnosticsEntity;
+import org.apache.nifi.web.api.metrics.jmx.JmxMetricsService;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
@@ -40,6 +43,7 @@ import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import java.util.Collection;
/**
* RESTful endpoint for retrieving system diagnostics.
@@ -50,7 +54,7 @@ import javax.ws.rs.core.Response;
description = "Endpoint for accessing system diagnostics."
)
public class SystemDiagnosticsResource extends ApplicationResource {
-
+ private JmxMetricsService jmxMetricsService;
private NiFiServiceFacade serviceFacade;
private Authorizer authorizer;
@@ -138,6 +142,56 @@ public class SystemDiagnosticsResource extends
ApplicationResource {
return generateOkResponse(entity).build();
}
+ /**
+ * Retrieves the JMX metrics.
+ *
+ * @return A jmxMetricsResult list.
+ */
+ @Path("jmx-metrics")
+ @GET
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(
+ value = "Retrieve available JMX metrics",
+ notes = NON_GUARANTEED_ENDPOINT,
+ response = JmxMetricsResultsEntity.class,
+ authorizations = {
+ @Authorization(value = "Read - /system")
+ }
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = 400, message = "NiFi was unable to
complete the request because it was invalid. The request should not be retried
without modification."),
+ @ApiResponse(code = 401, message = "Client could not be
authenticated."),
+ @ApiResponse(code = 403, message = "Client is not
authorized to make this request."),
+ @ApiResponse(code = 404, message = "The specified resource
could not be found."),
+ @ApiResponse(code = 409, message = "The request was valid
but NiFi was not in the appropriate state to process it. Retrying the same
request later may be successful.")
+ }
+ )
+ public Response getJmxMetrics(
+ @ApiParam(
+ value = "Regular Expression Pattern to be applied against
the ObjectName")
+ @QueryParam("beanNameFilter") final String beanNameFilter
+
+ ) {
+ authorizeJmxMetrics();
+
+ final Collection<JmxMetricsResultDTO> results =
jmxMetricsService.getFilteredMBeanMetrics(beanNameFilter);
+ final JmxMetricsResultsEntity entity = new JmxMetricsResultsEntity();
+ entity.setJmxMetricsResults(results);
+
+ return generateOkResponse(entity)
+ .type(MediaType.APPLICATION_JSON_TYPE)
+ .build();
+ }
+
+ private void authorizeJmxMetrics() {
+ serviceFacade.authorizeAccess(lookup -> {
+ final Authorizable system = lookup.getSystem();
+ system.authorize(authorizer, RequestAction.READ,
NiFiUserUtils.getNiFiUser());
+ });
+ }
+
// setters
public void setServiceFacade(NiFiServiceFacade serviceFacade) {
@@ -147,4 +201,8 @@ public class SystemDiagnosticsResource extends
ApplicationResource {
public void setAuthorizer(Authorizer authorizer) {
this.authorizer = authorizer;
}
+
+ public void setJmxMetricsService(final JmxMetricsService
jmxMetricsService) {
+ this.jmxMetricsService = jmxMetricsService;
+ }
}
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsCollector.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsCollector.java
new file mode 100644
index 0000000000..5281e8a898
--- /dev/null
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsCollector.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.nifi.web.api.metrics.jmx;
+
+import org.apache.nifi.web.api.dto.JmxMetricsResultDTO;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.RuntimeMBeanException;
+import java.lang.management.ManagementFactory;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Set;
+
+public class JmxMetricsCollector {
+ private static final Logger LOGGER =
LoggerFactory.getLogger(JmxMetricsCollector.class);
+ private final static String PATTERN_FOR_ALL_OBJECT_NAMES = "*:*";
+ private final JmxMetricsResultConverter resultConverter;
+
+ public JmxMetricsCollector(final JmxMetricsResultConverter
metricsResultConverter) {
+ this.resultConverter = metricsResultConverter;
+ }
+
+ public Collection<JmxMetricsResultDTO> getBeanMetrics() {
+ final MBeanServer mBeanServer =
ManagementFactory.getPlatformMBeanServer();
+ final Set<ObjectInstance> instances;
+ try {
+ instances = mBeanServer.queryMBeans(new
ObjectName(PATTERN_FOR_ALL_OBJECT_NAMES), null);
+ } catch (MalformedObjectNameException e) {
+ throw new RuntimeException("Invalid ObjectName pattern", e);
+ }
+
+ final Collection<JmxMetricsResultDTO> results = new ArrayList<>();
+ for (final ObjectInstance instance : instances) {
+ final MBeanInfo info;
+ try {
+ info = mBeanServer.getMBeanInfo(instance.getObjectName());
+ } catch (InstanceNotFoundException | ReflectionException |
IntrospectionException e) {
+ continue;
+ }
+
+ for (MBeanAttributeInfo attribute : info.getAttributes()) {
+ try {
+ final String beanName =
instance.getObjectName().getCanonicalName();
+ final String attributeName = attribute.getName();
+ final Object attributeValue =
resultConverter.convert(mBeanServer.getAttribute(instance.getObjectName(),
attribute.getName()));
+
+ results.add(new JmxMetricsResultDTO(beanName,
attributeName, attributeValue));
+ } catch (final MBeanException | RuntimeMBeanException |
ReflectionException | InstanceNotFoundException | AttributeNotFoundException e)
{
+ //Empty or invalid attributes should not stop the loop.
+ LOGGER.debug("MBean Object [{}] invalid attribute [{}]
found", instance.getObjectName(), attribute.getName());
+ }
+ }
+ }
+ return results;
+ }
+}
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsFilter.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsFilter.java
new file mode 100644
index 0000000000..46c9cdf24d
--- /dev/null
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsFilter.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.nifi.web.api.metrics.jmx;
+
+import org.apache.nifi.web.api.dto.JmxMetricsResultDTO;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import java.util.stream.Collectors;
+
+public class JmxMetricsFilter {
+ private static final Logger LOGGER =
LoggerFactory.getLogger(JmxMetricsFilter.class);
+ private final static String MATCH_NOTHING = "~^";
+ private final static String MATCH_ALL = "";
+ private final Pattern allowedNameFilter;
+ private final Pattern beanNameFilter;
+
+ public JmxMetricsFilter(final String allowedNameFilter, final String
beanNameFilter) {
+ this.allowedNameFilter = createPattern(allowedNameFilter,
MATCH_NOTHING);
+ this.beanNameFilter = createPattern(beanNameFilter, MATCH_ALL);
+ }
+
+ private Pattern createPattern(final String filter, final String
defaultValue) {
+ try {
+ if (filter == null || filter.isEmpty()) {
+ return Pattern.compile(defaultValue);
+ } else {
+ return Pattern.compile(filter);
+ }
+ } catch (PatternSyntaxException e) {
+ LOGGER.warn("Invalid JMX MBean filter pattern ignored [{}]",
filter);
+ return Pattern.compile(defaultValue);
+ }
+ }
+
+ public Collection<JmxMetricsResultDTO> filter(final
Collection<JmxMetricsResultDTO> results) {
+ return results.stream()
+ .filter(result ->
allowedNameFilter.asPredicate().test(result.getBeanName()))
+ .filter(result ->
beanNameFilter.asPredicate().test(result.getBeanName()))
+ .collect(Collectors.toList());
+ }
+}
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsResultConverter.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsResultConverter.java
new file mode 100644
index 0000000000..afb9c3f4c1
--- /dev/null
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsResultConverter.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.nifi.web.api.metrics.jmx;
+
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class JmxMetricsResultConverter {
+ private static final String COMPOSITE_DATA_KEY = "CompositeData%s";
+
+ public Object convert(final Object attributeValue) {
+ if (attributeValue instanceof CompositeData[]) {
+ final CompositeData[] valueArray = (CompositeData[])
attributeValue;
+ final Map<String, Object> values = new LinkedHashMap<>();
+
+ for (int i = 0; i < valueArray.length; i++) {
+ final Map<String, Object> subValues = new LinkedHashMap<>();
+ convertCompositeData(valueArray[i], subValues);
+ values.put(String.format(COMPOSITE_DATA_KEY, i), subValues);
+ }
+ return values;
+ } else if (attributeValue instanceof CompositeData) {
+ final Map<String, Object> values = new LinkedHashMap<>();
+ convertCompositeData(((CompositeData) attributeValue), values);
+ return values;
+ } else if (attributeValue instanceof TabularData) {
+ final Map<String, Object> values = new LinkedHashMap<>();
+ convertTabularData((TabularData) attributeValue, values);
+ return values;
+ } else {
+ return attributeValue;
+ }
+ }
+
+ private void convertCompositeData(CompositeData attributeValue,
Map<String, Object> values) {
+ for (String key : attributeValue.getCompositeType().keySet()) {
+ values.put(key, convert(attributeValue.get(key)));
+ }
+ }
+
+ private void convertTabularData(TabularData attributeValue, Map<String,
Object> values) {
+ final Set<List<?>> keys = (Set<List<?>>) attributeValue.keySet();
+ for (List<?> key : keys) {
+ Object value = convert(attributeValue.get(key.toArray()));
+ values.put(key.toString(), value);
+ }
+ }
+}
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsService.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsService.java
new file mode 100644
index 0000000000..5a81ae6bf9
--- /dev/null
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsService.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.nifi.web.api.metrics.jmx;
+
+import org.apache.nifi.web.api.dto.JmxMetricsResultDTO;
+
+import java.util.Collection;
+
+public interface JmxMetricsService {
+ /**
+ * @return a filtered mBean metric collection
+ *
+ * @param beanNameFilter regular expression pattern for bean name filtering
+ */
+ Collection<JmxMetricsResultDTO> getFilteredMBeanMetrics(final String
beanNameFilter);
+}
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/metrics/jmx/StandardJmxMetricsService.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/metrics/jmx/StandardJmxMetricsService.java
new file mode 100644
index 0000000000..a6d6ed933d
--- /dev/null
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/metrics/jmx/StandardJmxMetricsService.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.nifi.web.api.metrics.jmx;
+
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.api.dto.JmxMetricsResultDTO;
+
+import java.util.Collection;
+
+public class StandardJmxMetricsService implements JmxMetricsService {
+ private NiFiProperties properties;
+ private JmxMetricsCollector metricsCollector;
+
+ @Override
+ public Collection<JmxMetricsResultDTO> getFilteredMBeanMetrics(final
String beanNameFilter) {
+ final String allowedFilterPattern =
properties.getProperty(NiFiProperties.WEB_JMX_METRICS_ALLOWED_FILTER_PATTERN);
+ final JmxMetricsFilter metricsFilter = new
JmxMetricsFilter(allowedFilterPattern, beanNameFilter);
+ return metricsFilter.filter(metricsCollector.getBeanMetrics());
+ }
+
+ public void setProperties(final NiFiProperties properties) {
+ this.properties = properties;
+ }
+
+ public void setMetricsCollector(final JmxMetricsCollector
metricsCollector) {
+ this.metricsCollector = metricsCollector;
+ }
+}
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
index 9cd20ce746..5e356da4bb 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
@@ -608,6 +608,7 @@
<property name="requestReplicator" ref="requestReplicator" />
<property name="authorizer" ref="authorizer"/>
<property name="flowController" ref="flowController" />
+ <property name="jmxMetricsService" ref="jmxMetricsService" />
</bean>
<bean id="accessResource" class="org.apache.nifi.web.api.AccessResource"
scope="singleton">
<property name="logoutRequestManager" ref="logoutRequestManager" />
@@ -655,6 +656,15 @@
<property name="clusterComponentLifecycle"
ref="clusterComponentLifecycle" />
<property name="localComponentLifecycle" ref="localComponentLifecycle"
/>
</bean>
+ <bean id="jmxMetricsService"
class="org.apache.nifi.web.api.metrics.jmx.StandardJmxMetricsService">
+ <property name="properties" ref="nifiProperties"/>
+ <property name="metricsCollector" ref="metricsCollector"/>
+ </bean>
+ <bean id="metricsResultConverter"
class="org.apache.nifi.web.api.metrics.jmx.JmxMetricsResultConverter">
+ </bean>
+ <bean id="metricsCollector"
class="org.apache.nifi.web.api.metrics.jmx.JmxMetricsCollector">
+ <constructor-arg ref="metricsResultConverter"/>
+ </bean>
<!-- enable aop -->
<!--
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsFilterTest.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsFilterTest.java
new file mode 100644
index 0000000000..f525fb0e15
--- /dev/null
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsFilterTest.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.nifi.web.api.metrics.jmx;
+
+import org.apache.nifi.web.api.dto.JmxMetricsResultDTO;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class JmxMetricsFilterTest {
+ private static final String ALLOW_ALL_PATTERN = ".*";
+ private static final String EMPTY_STRING_PATTERN = "";
+ private static final String BEAN_NAME_FILTER = "%s|%s";
+ private static final String INVALID_REGEX = "(";
+ private static final String TEST_BEAN_NAME_ONE = "testBean1";
+ private static final String TEST_BEAN_NAME_TWO = "testBean2";
+ private static final JmxMetricsResultDTO RESULT_ONE = new
JmxMetricsResultDTO(TEST_BEAN_NAME_ONE, null, null);
+ private static final JmxMetricsResultDTO RESULT_TWO = new
JmxMetricsResultDTO(TEST_BEAN_NAME_TWO, null, null);
+ private static List<JmxMetricsResultDTO> results;
+
+ @BeforeAll
+ public static void init() {
+ results = new ArrayList<>();
+ results.add(RESULT_ONE);
+ results.add(RESULT_TWO);
+ }
+
+ @Test
+ public void testNotProvidingFiltersReturnsAllMBeans() {
+ final JmxMetricsFilter metricsFilter = new
JmxMetricsFilter(ALLOW_ALL_PATTERN, EMPTY_STRING_PATTERN);
+
+ final Collection<JmxMetricsResultDTO> actual =
metricsFilter.filter(results);
+
+ assertEquals(actual.size(), 2);
+ assertTrue(actual.containsAll(results));
+ }
+
+ @Test
+ public void testAllowedNameFiltersRemovesMBeanFromResult() {
+ final JmxMetricsFilter metricsFilter = new
JmxMetricsFilter(TEST_BEAN_NAME_ONE, EMPTY_STRING_PATTERN);
+
+ final Collection<JmxMetricsResultDTO> actual =
metricsFilter.filter(results);
+
+ assertEquals(actual.size(), 1);
+ assertTrue(actual.contains(RESULT_ONE));
+ assertFalse(actual.contains(RESULT_TWO));
+ }
+
+ @Test
+ public void testBeanNameFiltersReturnsTheSpecifiedMBeansOnly() {
+ final JmxMetricsFilter metricsFilter = new
JmxMetricsFilter(ALLOW_ALL_PATTERN, String.format(BEAN_NAME_FILTER,
TEST_BEAN_NAME_ONE, TEST_BEAN_NAME_TWO));
+
+ final Collection<JmxMetricsResultDTO> actual =
metricsFilter.filter(results);
+
+ assertEquals(actual.size(), 2);
+ assertTrue(actual.containsAll(results));
+ }
+
+ @Test
+ public void testInvalidAllowedNameFilterRevertingBackToDefaultFiltering() {
+ final JmxMetricsFilter metricsFilter = new
JmxMetricsFilter(INVALID_REGEX, EMPTY_STRING_PATTERN);
+
+ final Collection<JmxMetricsResultDTO> actual =
metricsFilter.filter(results);
+
+ assertTrue(actual.isEmpty());
+ }
+
+ @Test
+ public void testInvalidBeanNameFilteringRevertingBackToDefaultFiltering() {
+ final JmxMetricsFilter metricsFilter = new
JmxMetricsFilter(ALLOW_ALL_PATTERN, INVALID_REGEX);
+
+ final Collection<JmxMetricsResultDTO> actual =
metricsFilter.filter(results);
+
+ assertEquals(actual.size(), 2);
+ assertTrue(actual.containsAll(results));
+ }
+
+ @Test
+ public void testInvalidFiltersRevertingBackToDefaultFiltering() {
+ final JmxMetricsFilter metricsFilter = new
JmxMetricsFilter(INVALID_REGEX, INVALID_REGEX);
+
+ final Collection<JmxMetricsResultDTO> actual =
metricsFilter.filter(results);
+
+ assertTrue(actual.isEmpty());
+ }
+
+ @Test
+ public void testAllowedNameFilterHasPriority() {
+ final JmxMetricsFilter metricsFilter = new
JmxMetricsFilter(TEST_BEAN_NAME_TWO, TEST_BEAN_NAME_ONE);
+
+ final Collection<JmxMetricsResultDTO> actual =
metricsFilter.filter(results);
+
+ assertTrue(actual.isEmpty());
+ }
+
+ @Test
+ public void testAllowedNameFilterHasPriorityWhenTheSameFiltersApplied() {
+ final JmxMetricsFilter metricsFilter = new
JmxMetricsFilter(TEST_BEAN_NAME_TWO, String.format(BEAN_NAME_FILTER,
TEST_BEAN_NAME_ONE, TEST_BEAN_NAME_TWO));
+
+ final Collection<JmxMetricsResultDTO> actual =
metricsFilter.filter(results);
+
+ assertEquals(actual.size(), 1);
+ assertFalse(actual.contains(RESULT_ONE));
+ assertTrue(actual.contains(RESULT_TWO));
+ }
+}
\ No newline at end of file
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsResultConverterTest.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsResultConverterTest.java
new file mode 100644
index 0000000000..6f6e64000e
--- /dev/null
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/api/metrics/jmx/JmxMetricsResultConverterTest.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.nifi.web.api.metrics.jmx;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class JmxMetricsResultConverterTest {
+ private static final String COMPOSITE_DATA_KEY = "CompositeData%s";
+ private static final String TABLE_DATA_KEY = "Test String %s";
+ private static final String CONVERTED_TABLE_DATA_KEY = "[Test String %s]";
+ private static final String ATTRIBUTE_NAME_STRING = "string";
+ private static final String ATTRIBUTE_NAME_INT = "int";
+ private static final String ATTRIBUTE_NAME_BOOLEAN = "boolean";
+ private static final String TABLE_DESCRIPTION = "Table for all tests";
+ private static final String TABLE_NAME = "Test table";
+ private static final String METRIC_TYPE_DESCRIPTION = "Metric type for
testing";
+ private static final String METRIC_TYPE_NAME = "Metric type";
+ private static JmxMetricsResultConverter metricsResultConverter;
+ private static CompositeData compositeDataOne;
+ private static CompositeData compositeDataTwo;
+ private static CompositeData compositeDataThree;
+ private static TabularType tableType;
+ @BeforeAll
+ public static void init() throws OpenDataException {
+ metricsResultConverter = new JmxMetricsResultConverter();
+
+ final CompositeType compositeType = new CompositeType(METRIC_TYPE_NAME,
+ METRIC_TYPE_DESCRIPTION,
+ new String[]{ATTRIBUTE_NAME_STRING, ATTRIBUTE_NAME_INT,
ATTRIBUTE_NAME_BOOLEAN},
+ new String[]{ATTRIBUTE_NAME_STRING, ATTRIBUTE_NAME_INT,
ATTRIBUTE_NAME_BOOLEAN},
+ new OpenType[]{SimpleType.STRING, SimpleType.INTEGER,
SimpleType.BOOLEAN});
+
+ tableType = new TabularType(TABLE_NAME,
+ TABLE_DESCRIPTION,
+ compositeType,
+ new String[] {ATTRIBUTE_NAME_STRING});
+
+ compositeDataOne = new CompositeDataSupport(compositeType,
+ new String[] {ATTRIBUTE_NAME_STRING, ATTRIBUTE_NAME_INT,
ATTRIBUTE_NAME_BOOLEAN},
+ new Object[] {"Test String 1", 1, Boolean.FALSE}
+ );
+
+ compositeDataTwo = new CompositeDataSupport(compositeType,
+ new String[] {ATTRIBUTE_NAME_STRING, ATTRIBUTE_NAME_INT,
ATTRIBUTE_NAME_BOOLEAN},
+ new Object[] {"Test String 2", 2, Boolean.TRUE}
+ );
+
+ compositeDataThree = new CompositeDataSupport(compositeType,
+ new String[] {ATTRIBUTE_NAME_STRING, ATTRIBUTE_NAME_INT,
ATTRIBUTE_NAME_BOOLEAN},
+ new Object[] {"Test String 3", 3, Boolean.FALSE}
+ );
+ }
+
+ @Test
+ public void testSimpleTypeKeptOriginalType() {
+ final String expectedString = "Test String";
+ final int expectedInt = 1;
+ final boolean expectedBoolean = Boolean.TRUE;
+
+ final Object actualString =
metricsResultConverter.convert(expectedString);
+ final Object actualInt = metricsResultConverter.convert(expectedInt);
+ final Object actualBoolean =
metricsResultConverter.convert(expectedBoolean);
+
+ assertEquals(expectedString, actualString);
+ assertEquals(expectedInt, actualInt);
+ assertEquals(expectedBoolean, actualBoolean);
+ assertEquals(SimpleType.STRING.getTypeName(),
actualString.getClass().getName());
+ assertEquals(SimpleType.INTEGER.getTypeName(),
actualInt.getClass().getName());
+ assertEquals(SimpleType.BOOLEAN.getTypeName(),
actualBoolean.getClass().getName());
+ }
+
+ @Test
+ public void testCompositeDataConvertedToMap() {
+ //CompositeData consists of a compositeType and a content which is a
Map of attribute name and value pairs.
+ //The content will be concerted to a LinkedHashMap.
+ final CompositeData expected = compositeDataOne;
+
+ final Map<String, Object> actual =
castToMap(metricsResultConverter.convert(expected));
+
+ assertEquals(expected.get(ATTRIBUTE_NAME_STRING),
actual.get(ATTRIBUTE_NAME_STRING));
+ assertEquals(expected.get(ATTRIBUTE_NAME_STRING).getClass().getName(),
actual.get(ATTRIBUTE_NAME_STRING).getClass().getName());
+ assertEquals(expected.get(ATTRIBUTE_NAME_INT),
actual.get(ATTRIBUTE_NAME_INT));
+ assertEquals(expected.get(ATTRIBUTE_NAME_INT).getClass().getName(),
actual.get(ATTRIBUTE_NAME_INT).getClass().getName());
+ assertEquals(expected.get(ATTRIBUTE_NAME_BOOLEAN),
actual.get(ATTRIBUTE_NAME_BOOLEAN));
+
assertEquals(expected.get(ATTRIBUTE_NAME_BOOLEAN).getClass().getName(),
actual.get(ATTRIBUTE_NAME_BOOLEAN).getClass().getName());
+ }
+
+ @Test
+ public void testCompositeDataListConvertedToMaps() {
+ //A CompositeData array consists of a collection of CompositeData
objects.
+ //The content will be concerted to a LinkedHashMap where the key is
'CompositeData<array_index>'
+ // and the value is a LinkedHashmap of the CompositeData content.
+ final CompositeData[] expected = new CompositeData[] {
+ compositeDataOne,
+ compositeDataTwo,
+ compositeDataThree
+ };
+
+ final Map<String, Object> actual =
castToMap(metricsResultConverter.convert(expected));
+
+
assertTrue(expected[0].values().containsAll(castToMap(actual.get(String.format(COMPOSITE_DATA_KEY,
0))).values()));
+
assertTrue(expected[1].values().containsAll(castToMap(actual.get(String.format(COMPOSITE_DATA_KEY,
1))).values()));
+
assertTrue(expected[2].values().containsAll(castToMap(actual.get(String.format(COMPOSITE_DATA_KEY,
2))).values()));
+ assertEquals(expected.length, actual.size());
+ }
+
+ @Test
+ public void testTabularDataConvertedToMaps() {
+ //A TabularData consists of a Collection where key is a String array
of attribute names and value is a CompositeData.
+ //The content will be concerted to a LinkedHashMap where the key is
String output of the Tabular key array
+ // and the value is a LinkedHashmap of the CompositeData content.
+ final TabularData expected = new TabularDataSupport(tableType);
+ expected.put(compositeDataOne);
+ expected.put(compositeDataTwo);
+ expected.put(compositeDataThree);
+
+ final Map<String, Object> actual =
castToMap(metricsResultConverter.convert(expected));
+
+ assertTrue(expected.get(new String[]{String.format(TABLE_DATA_KEY,
1)}).values().containsAll(castToMap(actual.get(String.format(CONVERTED_TABLE_DATA_KEY,
1))).values()));
+ assertTrue(expected.get(new String[]{String.format(TABLE_DATA_KEY,
2)}).values().containsAll(castToMap(actual.get(String.format(CONVERTED_TABLE_DATA_KEY,
2))).values()));
+ assertTrue(expected.get(new String[]{String.format(TABLE_DATA_KEY,
3)}).values().containsAll(castToMap(actual.get(String.format(CONVERTED_TABLE_DATA_KEY,
3))).values()));
+ assertEquals(expected.values().size(), actual.size());
+ }
+
+ private Map<String, Object> castToMap(final Object object) {
+ return (Map<String, Object>) object;
+ }
+
+}
\ No newline at end of file