From 5811a9c579a11aa8fc8358851df33d0456a5e10c Mon Sep 17 00:00:00 2001 From: Timea Barna Date: Wed, 5 Apr 2023 14:42:47 +0200 Subject: [PATCH] NIFI-11385 Added JMX Metrics REST Resource for Diagnostics This closes #7124 Co-authored-by: David Handermann Signed-off-by: David Handermann --- .../org/apache/nifi/util/NiFiProperties.java | 1 + .../main/asciidoc/administration-guide.adoc | 36 ++++ .../nifi/web/api/dto/JmxMetricsResultDTO.java | 49 ++++++ .../api/entity/JmxMetricsResultsEntity.java | 43 +++++ .../nifi-framework/nifi-resources/pom.xml | 1 + .../src/main/resources/conf/nifi.properties | 5 +- .../web/api/SystemDiagnosticsResource.java | 60 ++++++- .../api/metrics/jmx/JmxMetricsCollector.java | 82 +++++++++ .../web/api/metrics/jmx/JmxMetricsFilter.java | 59 +++++++ .../jmx/JmxMetricsResultConverter.java | 66 ++++++++ .../api/metrics/jmx/JmxMetricsService.java | 30 ++++ .../jmx/StandardJmxMetricsService.java | 42 +++++ .../main/resources/nifi-web-api-context.xml | 10 ++ .../api/metrics/jmx/JmxMetricsFilterTest.java | 127 ++++++++++++++ .../jmx/JmxMetricsResultConverterTest.java | 158 ++++++++++++++++++ 15 files changed, 766 insertions(+), 3 deletions(-) create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/JmxMetricsResultDTO.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/JmxMetricsResultsEntity.java create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 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 jmxMetricsResults; + + /** + * A collection of JmxMetricsResultDTO objects that is being serialized. + * + * @return The collection of JmxMetricsResultDTO objects + */ + public Collection getJmxMetricsResults() { + return jmxMetricsResults; + } + + public void setJmxMetricsResults(final Collection 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 @@ true %{client}a - %u %t "%r" %s %O "%{Referer}i" "%{User-Agent}i" + false 10 secs 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 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 getBeanMetrics() { + final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + final Set 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 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 filter(final Collection 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 values = new LinkedHashMap<>(); + + for (int i = 0; i < valueArray.length; i++) { + final Map 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 values = new LinkedHashMap<>(); + convertCompositeData(((CompositeData) attributeValue), values); + return values; + } else if (attributeValue instanceof TabularData) { + final Map values = new LinkedHashMap<>(); + convertTabularData((TabularData) attributeValue, values); + return values; + } else { + return attributeValue; + } + } + + private void convertCompositeData(CompositeData attributeValue, Map values) { + for (String key : attributeValue.getCompositeType().keySet()) { + values.put(key, convert(attributeValue.get(key))); + } + } + + private void convertTabularData(TabularData attributeValue, Map values) { + final Set> keys = (Set>) 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 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 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 @@ + @@ -655,6 +656,15 @@ + + + + + + + + +