mirror of https://github.com/apache/nifi.git
NIFI-11385 Added JMX Metrics REST Resource for Diagnostics
This closes #7124 Co-authored-by: David Handermann <exceptionfactory@apache.org> Signed-off-by: David Handermann <exceptionfactory@apache.org>
This commit is contained in:
parent
340b5fcb00
commit
5811a9c579
|
@ -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_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_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_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
|
// ui properties
|
||||||
public static final String UI_BANNER_TEXT = "nifi.ui.banner.text";
|
public static final String UI_BANNER_TEXT = "nifi.ui.banner.text";
|
||||||
|
|
|
@ -4174,6 +4174,8 @@ request headers. The default value is:
|
||||||
The CustomRequestLog writes formatted messages using the following SLF4J logger:
|
The CustomRequestLog writes formatted messages using the following SLF4J logger:
|
||||||
|
|
||||||
`org.apache.nifi.web.server.RequestLog`
|
`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]]
|
[[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.
|
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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -140,6 +140,7 @@
|
||||||
<nifi.web.request.ip.whitelist />
|
<nifi.web.request.ip.whitelist />
|
||||||
<nifi.web.should.send.server.version>true</nifi.web.should.send.server.version>
|
<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.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.properties: security properties -->
|
||||||
<nifi.security.autoreload.enabled>false</nifi.security.autoreload.enabled>
|
<nifi.security.autoreload.enabled>false</nifi.security.autoreload.enabled>
|
||||||
<nifi.security.autoreload.interval>10 secs</nifi.security.autoreload.interval>
|
<nifi.security.autoreload.interval>10 secs</nifi.security.autoreload.interval>
|
||||||
|
|
|
@ -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.should.send.server.version=${nifi.web.should.send.server.version}
|
||||||
nifi.web.request.log.format=${nifi.web.request.log.format}
|
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
|
# Include or Exclude TLS Cipher Suites for HTTPS
|
||||||
nifi.web.https.ciphersuites.include=
|
nifi.web.https.ciphersuites.include=
|
||||||
nifi.web.https.ciphersuites.exclude=
|
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.schedule=
|
||||||
nifi.monitor.long.running.task.threshold=
|
nifi.monitor.long.running.task.threshold=
|
||||||
|
|
||||||
# Create automatic diagnostics when stopping/restarting NiFi.
|
|
||||||
|
|
||||||
# Enable automatic diagnostic at shutdown.
|
# Enable automatic diagnostic at shutdown.
|
||||||
nifi.diagnostics.on.shutdown.enabled=false
|
nifi.diagnostics.on.shutdown.enabled=false
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,11 @@ import org.apache.nifi.authorization.resource.Authorizable;
|
||||||
import org.apache.nifi.authorization.user.NiFiUserUtils;
|
import org.apache.nifi.authorization.user.NiFiUserUtils;
|
||||||
import org.apache.nifi.cluster.manager.NodeResponse;
|
import org.apache.nifi.cluster.manager.NodeResponse;
|
||||||
import org.apache.nifi.web.NiFiServiceFacade;
|
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.dto.SystemDiagnosticsDTO;
|
||||||
|
import org.apache.nifi.web.api.entity.JmxMetricsResultsEntity;
|
||||||
import org.apache.nifi.web.api.entity.SystemDiagnosticsEntity;
|
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.Consumes;
|
||||||
import javax.ws.rs.DefaultValue;
|
import javax.ws.rs.DefaultValue;
|
||||||
|
@ -40,6 +43,7 @@ import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RESTful endpoint for retrieving system diagnostics.
|
* RESTful endpoint for retrieving system diagnostics.
|
||||||
|
@ -50,7 +54,7 @@ import javax.ws.rs.core.Response;
|
||||||
description = "Endpoint for accessing system diagnostics."
|
description = "Endpoint for accessing system diagnostics."
|
||||||
)
|
)
|
||||||
public class SystemDiagnosticsResource extends ApplicationResource {
|
public class SystemDiagnosticsResource extends ApplicationResource {
|
||||||
|
private JmxMetricsService jmxMetricsService;
|
||||||
private NiFiServiceFacade serviceFacade;
|
private NiFiServiceFacade serviceFacade;
|
||||||
private Authorizer authorizer;
|
private Authorizer authorizer;
|
||||||
|
|
||||||
|
@ -138,6 +142,56 @@ public class SystemDiagnosticsResource extends ApplicationResource {
|
||||||
return generateOkResponse(entity).build();
|
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
|
// setters
|
||||||
|
|
||||||
public void setServiceFacade(NiFiServiceFacade serviceFacade) {
|
public void setServiceFacade(NiFiServiceFacade serviceFacade) {
|
||||||
|
@ -147,4 +201,8 @@ public class SystemDiagnosticsResource extends ApplicationResource {
|
||||||
public void setAuthorizer(Authorizer authorizer) {
|
public void setAuthorizer(Authorizer authorizer) {
|
||||||
this.authorizer = authorizer;
|
this.authorizer = authorizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setJmxMetricsService(final JmxMetricsService jmxMetricsService) {
|
||||||
|
this.jmxMetricsService = jmxMetricsService;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -608,6 +608,7 @@
|
||||||
<property name="requestReplicator" ref="requestReplicator" />
|
<property name="requestReplicator" ref="requestReplicator" />
|
||||||
<property name="authorizer" ref="authorizer"/>
|
<property name="authorizer" ref="authorizer"/>
|
||||||
<property name="flowController" ref="flowController" />
|
<property name="flowController" ref="flowController" />
|
||||||
|
<property name="jmxMetricsService" ref="jmxMetricsService" />
|
||||||
</bean>
|
</bean>
|
||||||
<bean id="accessResource" class="org.apache.nifi.web.api.AccessResource" scope="singleton">
|
<bean id="accessResource" class="org.apache.nifi.web.api.AccessResource" scope="singleton">
|
||||||
<property name="logoutRequestManager" ref="logoutRequestManager" />
|
<property name="logoutRequestManager" ref="logoutRequestManager" />
|
||||||
|
@ -655,6 +656,15 @@
|
||||||
<property name="clusterComponentLifecycle" ref="clusterComponentLifecycle" />
|
<property name="clusterComponentLifecycle" ref="clusterComponentLifecycle" />
|
||||||
<property name="localComponentLifecycle" ref="localComponentLifecycle" />
|
<property name="localComponentLifecycle" ref="localComponentLifecycle" />
|
||||||
</bean>
|
</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 -->
|
<!-- enable aop -->
|
||||||
<!--
|
<!--
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue