I'm having a bean that is
ManagedResource
SelfNaming
I'm exporting it using spring's AnnotationMBeanExporter
.
All this works good when I'm using spring version 4.3.16.RELEASE
, but when I upgraded my spring version to 5.0.5.RELEASE
or 5.1.3.RELEASE
this code started giving me IllegalStateException
.
My Bean definition and the spring's context.xml looks like this:
SampleBean.java:
package com.jmx.trial.dummybeans;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.export.naming.SelfNaming;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
@ManagedResource
public class SampleBean implements SelfNaming {
@Override
public ObjectName getObjectName() throws MalformedObjectNameException {
return new ObjectName("com.jmx.trial:name=sampleBean");
}
}
application-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="server" class="org.springframework.jmx.support.MBeanServerFactoryBean"/>
<bean id="exporter" class="org.springframework.jmx.export.annotation.AnnotationMBeanExporter">
<property name="server" ref="server"/>
</bean>
<bean id="sampleBean" class="com.jmx.trial.dummybeans.SampleBean" lazy-init="true"/>
</beans>
I understand that an extra validation was added here, and this is causing IllegalStateException
but I'm not completely sure why that was added.
The stack-trace looks like this:
org.springframework.jmx.export.UnableToRegisterMBeanException: Unable to register MBean [sampleBean] with key 'sampleBean'; nested exception is java.lang.IllegalStateException: Not initialized
at org.springframework.jmx.export.MBeanExporter.registerBeanNameOrInstance(MBeanExporter.java:625)
at org.springframework.jmx.export.MBeanExporter.lambda$registerBeans$2(MBeanExporter.java:551)
at java.base/java.util.HashMap.forEach(HashMap.java:1336)
at org.springframework.jmx.export.MBeanExporter.registerBeans(MBeanExporter.java:551)
at org.springframework.jmx.export.MBeanExporter.afterSingletonsInstantiated(MBeanExporter.java:434)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:863)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:863)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85)
at com.jmx.trial.MBeanExporterTest.testBeanExportedWithXml(MBeanExporterTest.java:79)
at com.jmx.trial.MBeanExporterTest.testForLazyAutoDetectWithSelfNaming(MBeanExporterTest.java:44)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.IllegalStateException: Not initialized
at org.springframework.util.Assert.state(Assert.java:73)
at org.springframework.jmx.export.MBeanExporter$NotificationPublisherAwareLazyTargetSource.postProcessTargetObject(MBeanExporter.java:1115)
at org.springframework.aop.target.LazyInitTargetSource.getTarget(LazyInitTargetSource.java:72)
at org.springframework.jmx.export.MBeanExporter$NotificationPublisherAwareLazyTargetSource.getTarget(MBeanExporter.java:1103)
at org.springframework.aop.framework.CglibAopProxy$DynamicUnadvisedInterceptor.intercept(CglibAopProxy.java:475)
at com.jmx.trial.dummybeans.SampleBean$$EnhancerBySpringCGLIB$$9cd1c95b.getObjectName(<generated>)
at org.springframework.jmx.export.MBeanExporter.getObjectName(MBeanExporter.java:752)
at org.springframework.jmx.export.MBeanExporter.registerLazyInit(MBeanExporter.java:726)
at org.springframework.jmx.export.MBeanExporter.registerBeanNameOrInstance(MBeanExporter.java:596)
... 33 more
My experiments:
sampleBean
, I don't get this IllegalStateException
.ManagedResource(objectName = "com.jmx.trial:name=sampleBean")
and make SampleBean
not implement SelfNaming
interface, again I don't get IllegalStateException
Though not very sure, but I think implementing SelfNaming
interface is not a very good idea as the javadocs of SelfNaming
interface says that:
This interface is mainly intended for internal usage.
I'm not sure if it is the problem with using SelfNaming
interface or I'm doing something fundamentally wrong. Can you please explain this behavior and point me to the fundamentals that I'm missing.
PS: some of my findings: (might be unrelated) From here I found this caution point:
Do not use interface-based AOP proxies in combination with autodetection of JMX annotations in your bean classes.
I don't completely understand this, but is this the rule that I'm violating?
It's possibly a bug; injectNotificationPublisherIfNecessary
only takes any action if your class also implements NotificationPublisherAware
.
This would be invalid since we don't have an objectName
, yet, because the bean is lazy and this would violate the contract for ModelMBeanNotificationPublisher
.
I don't know if postProcessTargetObject
can ignore the assert if the resource doesn't implement the publisher aware interface, or whether the publisher injection needs to be made lazily too. I don't know the internals well enough.
I suggest you open an SPR
JIRA issue so the Spring Team can take a look and give you a definitive answer.