Skip to content

Commit c111e1b

Browse files
garyrussellartembilan
authored andcommitted
JMX: Quote object name values if not identifiers
https://www.oracle.com/technetwork/java/javase/tech/best-practices-jsp-136021.html The set of characters in a value is also limited. If special characters may occur, it is recommended that the value be quoted, using ObjectName.quote. If the value for a given key is sometimes quoted, then it should always be quoted. By default, if a value is a string (rather than a number, say), then it should be quoted unless you are sure that it will never contain special characters. Practically, some special characters are allowed, but we will standardize on allowed characters in java identifiers. In 5.0.x, this is enabled using `spring.integration.properties`.
1 parent 7403e36 commit c111e1b

File tree

6 files changed

+49
-12
lines changed

6 files changed

+49
-12
lines changed

spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.concurrent.atomic.AtomicLong;
2929
import java.util.concurrent.atomic.AtomicReference;
3030

31+
import javax.lang.model.SourceVersion;
3132
import javax.management.DynamicMBean;
3233
import javax.management.JMException;
3334
import javax.management.ObjectName;
@@ -157,6 +158,8 @@ public class IntegrationMBeanExporter extends MBeanExporter implements Applicati
157158

158159
private final AtomicBoolean shuttingDown = new AtomicBoolean();
159160

161+
private boolean quoteNames;
162+
160163

161164
public IntegrationMBeanExporter() {
162165
super();
@@ -216,6 +219,14 @@ public void setEmbeddedValueResolver(StringValueResolver resolver) {
216219

217220
@Override
218221
public void afterSingletonsInstantiated() {
222+
if (this.applicationContext.containsBean(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME)) {
223+
Properties props = this.applicationContext
224+
.getBean(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME, Properties.class);
225+
String prop = props.getProperty("spring.integration.jmx.quote.names");
226+
if (prop != null) {
227+
this.quoteNames = Boolean.parseBoolean(prop);
228+
}
229+
}
219230
Map<String, MessageHandlerMetrics> messageHandlers =
220231
this.applicationContext.getBeansOfType(MessageHandlerMetrics.class);
221232
for (Entry<String, MessageHandlerMetrics> entry : messageHandlers.entrySet()) {
@@ -744,28 +755,43 @@ private Object extractTarget(Object bean) {
744755
}
745756

746757
private String getChannelBeanKey(String channel) {
747-
String name = "" + channel;
748-
if (name.startsWith("org.springframework.integration")) {
749-
name = name + ",source=anonymous";
758+
String extra = "";
759+
if (channel.startsWith("org.springframework.integration")) {
760+
extra = ",source=anonymous";
750761
}
751-
return String.format(this.domain + ":type=MessageChannel,name=%s" + getStaticNames(), name);
762+
return String.format(this.domain + ":type=MessageChannel,name=%s%s" + getStaticNames(),
763+
quoteIfNecessary(channel), extra);
752764
}
753765

754766
private String getHandlerBeanKey(MessageHandlerMetrics handler) {
755767
// This ordering of keys seems to work with default settings of JConsole
756768
return String.format(this.domain + ":type=MessageHandler,name=%s,bean=%s" + getStaticNames(),
757-
handler.getManagedName(), handler.getManagedType());
769+
quoteIfNecessary(handler.getManagedName()), quoteIfNecessary(handler.getManagedType()));
758770
}
759771

760772
private String getSourceBeanKey(MessageSourceMetrics source) {
761773
// This ordering of keys seems to work with default settings of JConsole
762774
return String.format(this.domain + ":type=MessageSource,name=%s,bean=%s" + getStaticNames(),
763-
source.getManagedName(), source.getManagedType());
775+
quoteIfNecessary(source.getManagedName()), quoteIfNecessary(source.getManagedType()));
764776
}
765777

766778
private String getEndpointBeanKey(AbstractEndpoint endpoint, String name, String source) {
767779
// This ordering of keys seems to work with default settings of JConsole
768-
return String.format(this.domain + ":type=ManagedEndpoint,name=%s,bean=%s" + getStaticNames(), name, source);
780+
return String.format(this.domain + ":type=ManagedEndpoint,name=%s,bean=%s" + getStaticNames(),
781+
quoteIfNecessary(name), source);
782+
}
783+
784+
/*
785+
* https://www.oracle.com/technetwork/java/javase/tech/best-practices-jsp-136021.html
786+
*
787+
* The set of characters in a value is also limited. If special characters may
788+
* occur, it is recommended that the value be quoted, using ObjectName.quote. If
789+
* the value for a given key is sometimes quoted, then it should always be quoted.
790+
* By default, if a value is a string (rather than a number, say), then it should
791+
* be quoted unless you are sure that it will never contain special characters.
792+
*/
793+
private String quoteIfNecessary(String name) {
794+
return SourceVersion.isName(name) ? name : this.quoteNames ? ObjectName.quote(name) : name;
769795
}
770796

771797
private String getStaticNames() {

spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationPublishingChannelAdapterParserTests-context.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
<si:channel id="channel"/>
2525

26+
<si:channel id="org.springframework.integration.test.anon"/>
27+
2628
<jmx:notification-publishing-channel-adapter
2729
id="adapter" channel="channel"
2830
object-name="test.publisher:name=publisher"

spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationPublishingChannelAdapterParserTests.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -139,8 +139,12 @@ public void publishStringMessageWithinChain() throws Exception {
139139
assertEquals("test.type", notification.getType());
140140
assertNull(notification.getUserData());
141141
Set<ObjectName> names = server
142-
.queryNames(new ObjectName("*:type=MessageHandler," + "name=chainWithJmxNotificationPublishing$child."
143-
+ "jmx-notification-publishing-channel-adapter-within-chain,*"), null);
142+
.queryNames(new ObjectName("*:type=MessageHandler," + "name=\"chainWithJmxNotificationPublishing$child."
143+
+ "jmx-notification-publishing-channel-adapter-within-chain\",*"), null);
144+
assertEquals(1, names.size());
145+
names = server
146+
.queryNames(new ObjectName("*:type=MessageChannel,"
147+
+ "name=org.springframework.integration.test.anon,source=anonymous,*"), null);
144148
assertEquals(1, names.size());
145149
}
146150

spring-integration-jmx/src/test/java/org/springframework/integration/monitor/MessagingGatewaySupportRegistrationTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2015-2016 the original author or authors.
2+
* Copyright 2015-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -60,7 +60,7 @@ public void testHandlerMBeanRegistration() throws Exception {
6060
names = this.server.queryNames(new ObjectName("org.springframework.integration:*,type=MessageSource,name=foo"),
6161
null);
6262
assertEquals(1, names.size());
63-
names = this.server.queryNames(new ObjectName("org.springframework.integration:*,name=foo#2"), null);
63+
names = this.server.queryNames(new ObjectName("org.springframework.integration:*,name=\"foo#2\""), null);
6464
assertEquals(1, names.size());
6565
}
6666

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
spring.integration.jmx.quote.names=true

src/reference/asciidoc/jmx.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,10 @@ NOTE: The default naming strategy is a http://docs.spring.io/spring/docs/current
301301
The exporter propagates the `default-domain` to that object to allow it to generate a fallback object name if parsing of the bean key fails.
302302
If your custom naming strategy is a `MetadataNamingStrategy` (or subclass), the exporter will *not* propagate the `default-domain`; you will need to configure it on your strategy bean.
303303

304+
Starting with version 5.0.9; any bean names (represented by the `name` key in the object name) can be quoted if they contain any characters that are not allowed in a Java identifier (or period `.`).
305+
This requires setting `spring.integration.jmx.quote.names=true` in a `META-INF/spring.integration.properties` file on the class path.
306+
In 5.1 it will not be configurable.
307+
304308
[[jmx-42-improvements]]
305309
===== JMX Improvements
306310

0 commit comments

Comments
 (0)