Unable to register MBean - Invalid character `:' in value
Hi all. I have similar(I think) issues to spring-cloud-stream/#1497 when I try run application to listening mqtt broker. Im trying to run this code: https://docs.spring.io/spring-integration/docs/5.2.0.BUILD-SNAPSHOT/reference/html/mqtt.html#configuring-with-the-java-dsl Configuring with the Java DSL
for Configuring with Java Configuration everything works.
My stacktrace
org.springframework.jmx.export.UnableToRegisterMBeanException: Unable to register MBean [mqttInbound.mqtt:inbound-channel-adapter#0] with key 'mqttInbound.mqtt:inbound-channel-adapter#0'; nested exception is javax.management.MalformedObjectNameException: Invalid character `:' in value
at org.springframework.jmx.export.MBeanExporter.registerBeanNameOrInstance(MBeanExporter.java:625) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.jmx.export.MBeanExporter.lambda$registerBeans$2(MBeanExporter.java:551) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at java.base/java.util.HashMap.forEach(HashMap.java:1333) ~[na:na]
at org.springframework.jmx.export.MBeanExporter.registerBeans(MBeanExporter.java:551) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.jmx.export.MBeanExporter.afterSingletonsInstantiated(MBeanExporter.java:434) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:862) ~[spring-beans-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:743) ~[spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:390) ~[spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:312) ~[spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1214) ~[spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1203) ~[spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at com.firstnamelastname.grl.MiddlewareApplication.main(MiddlewareApplication.java:10) ~[classes/:na]
Caused by: javax.management.MalformedObjectNameException: Invalid character `:' in value
at java.management/javax.management.ObjectName.parseValue(ObjectName.java:978) ~[na:na]
at java.management/javax.management.ObjectName.checkValue(ObjectName.java:1010) ~[na:na]
at java.management/javax.management.ObjectName.construct(ObjectName.java:729) ~[na:na]
at java.management/javax.management.ObjectName.<init>(ObjectName.java:1450) ~[na:na]
at java.management/javax.management.ObjectName.getInstance(ObjectName.java:1356) ~[na:na]
at org.springframework.jmx.support.ObjectNameManager.getInstance(ObjectNameManager.java:99) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.jmx.export.naming.MetadataNamingStrategy.getObjectName(MetadataNamingStrategy.java:135) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.boot.autoconfigure.jmx.ParentAwareNamingStrategy.getObjectName(ParentAwareNamingStrategy.java:59) ~[spring-boot-autoconfigure-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.jmx.export.MBeanExporter.getObjectName(MBeanExporter.java:755) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.jmx.export.MBeanExporter.registerBeanInstance(MBeanExporter.java:654) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.jmx.export.MBeanExporter.registerBeanNameOrInstance(MBeanExporter.java:615) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
... 13 common frames omitted
My code
public class MeasurementListener {
@Bean
public IntegrationFlow mqttInbound() {
return IntegrationFlows.from(
new MqttPahoMessageDrivenChannelAdapter("tcp://localhost:1883", "/measurement/+/+"))
.handle(m -> System.out.println(m.getPayload()))
.get();
}
}
My main pom
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.firstnamelastname</groupId>
<artifactId>grl</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>green-real-time</name>
<description>Green Real Time</description>
<packaging>pom</packaging>
<modules>
<module>grl-core</module>
<module>grl-firmware-simulator</module>
<module>grl-middleware</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>12</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<configuration>
<goals>install</goals>
<autoVersionSubmodules>true</autoVersionSubmodules>
</configuration>
</plugin>
</plugins>
</build>
</project>
pom for this module
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.firstnamelastname</groupId>
<artifactId>grl</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>grl-middleware</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>green-real-time-middleware</name>
<description>Green Real Time - Middleware</description>
<properties>
<spring-boot.repackage.skip>true</spring-boot.repackage.skip>
</properties>
<dependencies>
<dependency>
<groupId>com.firstnamelastname</groupId>
<artifactId>grl-core</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mqtt</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
The IntegrationMBeanExporter has this logic:
/*
* 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.
*/
private String quoteIfNecessary(String name) {
return SourceVersion.isName(name) ? name : ObjectName.quote(name);
}
Looks like something similar must happen in Spring Boot or Spring Framework. According provided stack trace, there is no Spring Integration involved:
at java.management/javax.management.ObjectName.getInstance(ObjectName.java:1356) ~[na:na]
at org.springframework.jmx.support.ObjectNameManager.getInstance(ObjectNameManager.java:99) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.jmx.export.naming.MetadataNamingStrategy.getObjectName(MetadataNamingStrategy.java:135) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.boot.autoconfigure.jmx.ParentAwareNamingStrategy.getObjectName(ParentAwareNamingStrategy.java:59) ~[spring-boot-autoconfigure-2.1.7.RELEASE.jar:2.1.7.RELEASE]
at org.springframework.jmx.export.MBeanExporter.getObjectName(MBeanExporter.java:755) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.jmx.export.MBeanExporter.registerBeanInstance(MBeanExporter.java:654) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.jmx.export.MBeanExporter.registerBeanNameOrInstance(MBeanExporter.java:615) ~[spring-context-5.1.9.RELEASE.jar:5.1.9.RELEASE]
Although, you can add an explicit bean name for that MQTT endpoint to workaround this problem:
IntegrationFlows.from(
new MqttPahoMessageDrivenChannelAdapter("tcp://localhost:1883", "/measurement/+/+"), e -> e.id("myMqttEndpoint"))
Your workaround is enought for me. But I have problem with that:
And it doesn't compile...
I add casting (as told me IDE)
but now I have huge stacktrace in runtime (related to wrong casting)
MqttPahoMessageDrivenChannelAdapter not implement MessageSource. It implement something like MessageProducerSupport
I mean the issue is there, but it is not a part of Spring Integration to fix.
@philwebb , any thoughts about quoting MBean names in Spring Boot? Or let's push it down to Spring Framework then!
Oh! Sorry, @Stiuil06 . The MqttPahoMessageDrivenChannelAdapter is not a MessageSource. It has to be configured as a bean instead to get a proper bean name:
@Bean
public MqttPahoMessageDrivenChannelAdapter myMqttEndpoint() {
return new MqttPahoMessageDrivenChannelAdapter("tcp://localhost:1883", "/measurement/+/+");
}
...
@Bean
public IntegrationFlow mqttInbound() {
return IntegrationFlows.from(myMqttEndpoint())
...
As I wrote in opening post in this case (Configuring with Java Configuration) it works, but not work for Configuring with the Java DSL. (I refer to that).
Now I notice, that im working on 5.1.7.RELEASE, and example from the above documentation is for 5.2.0.BUILD-SNAPSHOT. Maybe it will be work on this build. How can I get this snapshot version? On mvnrepository the latest is 5.1.7.RELEASE. (This is a training program so it is not important to use official releases)
On the other hand it is not clear how this Spring Integration component (marked with @IntegrationManagedResource) get processed by the regular org.springframework.jmx.export.MBeanExporter, since there is indeed appropriate auto-configuration:
/**
* Spring Integration JMX configuration.
*/
@Configuration
@ConditionalOnClass(EnableIntegrationMBeanExport.class)
@ConditionalOnMissingBean(value = IntegrationMBeanExporter.class, search = SearchStrategy.CURRENT)
@ConditionalOnBean(MBeanServer.class)
@ConditionalOnProperty(prefix = "spring.jmx", name = "enabled", havingValue = "true", matchIfMissing = true)
protected static class IntegrationJmxConfiguration implements EnvironmentAware, BeanFactoryAware {
and there is a MBeanExporterHelper bean anyway...
@Stiuil06 ,
one more time: your stack trace doesn't show any Spring Integration code. Only the problem that Spring Integration registers some bean which is not going to be properly exposed into JMX by that MBeanExporter in Spring Boot.
Therefore changing version for Spring Integration won't help you at all.
It looks like would be great if you can share a simple project with us to play with and reproduce.
Thanks for understanding.
@artembilan I mean to change version of all spring components which I use. (boot etc.)
I create simple project with that issue. https://github.com/Stiuil06/demo-mqtt-listener
Thanks for all your help and time.
OK. Another solution is for you like this:
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-jmx</artifactId>
</dependency>
So, all Spring Integration components are going to be registered in JMX by the Spring Integration mechanism.
Anyway I think we still have quoting problem in the regular Spring Boot/Spring Framework solution.
/CC @philwebb
Spring Boot is just delegating to MetadataNamingStrategy so I think it would be a Framework problem.
@Stiuil06 ,
would you mind sharing with us your final experience? Plus, would you mind moving this issue into https://github.com/spring-projects/spring-framework/issues ?
It looks like we agreed with Spring Boot team that an issue has to be resolved on the core level if at all...
Thanks
@artembilan I just add
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-jmx</artifactId>
</dependency>
as you advised, and it work for me. I haven't explore subject more.
Sure, we should move issue to spring-framework. Should I open a new issue there? Or maybe you have possibility to move it?
Great! Good to know that workaround has made it!
Well, looks like I don't have permissions to move issue over there. Maybe @philwebb can do that for us, but I guess it is OK if you just open a new one over there with a link to this one. Thanks
@artembilan So I opened new issue there. https://github.com/spring-projects/spring-framework/issues/23608
Thanks, @Stiuil06 !
We will keep this issue opened until resolution on that SF one. We may just remove the mention logic here in favor of a new one in SF.
google brought me here.. I think it is rather unfortunate that the quickstart given in documentation here:
https://docs.spring.io/spring-integration/reference/html/mqtt.html#configuring-with-the-java-dsl
leads to this error. adding
implementation 'org.springframework.integration:spring-integration-jmx'
to build.gradle fixes it as described, IMHO that should be mentioned in above documentation link.
@dividebyzero ,
I'm sorry to hear about your bad experience, but unfortunately that code snippet does nothing with JMX, so the problem you are facing is really out of that chapter scope.
What I'm afraid of even if we add some note into our JMX chapter, it is still not going to be opaque for end-users as you since you all just don't use that JMX module from Spring Integration. That's you who set spring.jmx.enabled=true on your Spring Boot application. Probably the best place is, of course, to mention naming quoting is still Spring Framework since everything in the end is based on it. See that linked issue for more info. Plus this: https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#jmx-naming
Of course, there is another workaround to provide a custom naming strategy instead of an out-of-the-box one in Spring Boot:
@Bean
@ConditionalOnMissingBean(value = ObjectNamingStrategy.class, search = SearchStrategy.CURRENT)
public ParentAwareNamingStrategy objectNamingStrategy() {
But this one not easier then just adding spring-integration-jmx dependency...