New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exporting a lazily initialized bean (which implements SelfNaming and is annotated with ManagedResource annotation) gives IllegalStateException [SPR-17592] #22124

Closed
spring-issuemaster opened this Issue Dec 11, 2018 · 2 comments

Comments

Projects
None yet
2 participants
@spring-issuemaster
Copy link
Collaborator

spring-issuemaster commented Dec 11, 2018

Lavish Kothari opened SPR-17592 and commented

I'm having a bean that is

  • annotated with ManagedResource
  • lazily initialized
  • implements 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:

  • When I don't lazily initialize the SampleBean, I don't get this IllegalStateException.
  • When I annotate SampleBean with ManagedResource(objectName = "com.jmx.trial:name=sampleBean") and make SampleBean not implement SelfNaming interface, again I don't get IllegalStateException
  • When I debug this and place a lot of breakpoints in MBeanExporter and CGLibAopProxy I don't get this exception. (This is specifically interesting that in debug-mode the tests run successfully but in normal run mode the tests fails.)

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?

 

I feel that the Assert.state that is getting called in postProcessTargetObject should be moved into the method injectNotificationPublisherIfNecessary and should be called only if managedResource instanceof NotificationPublisherAware is true.


Affects: 5.0.5, 5.1.3

Reference URL: https://stackoverflow.com/questions/53711613/exporting-a-lazily-initialized-bean-which-implements-selfnaming-and-is-annotate

Attachments:

Issue Links:

  • #20276 Extend null-safety to field level

Referenced from: commits e95c1b3, 2c98c1b

Backported to: 5.0.12

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator

spring-issuemaster commented Dec 12, 2018

Juergen Hoeller commented

This turns out to be a side effect of our nullability efforts in 5.0. I've effectively relaxed it in injectNotificationPublisherIfNecessary itself, simply skipping the callback if there is no state to pass in.

Thanks for raising this! Please give the upcoming 5.1.4.BUILD-SNAPSHOT / 5.0.12.BUILD-SNAPSHOT a try and let me know whether it works for you now...

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator

spring-issuemaster commented Dec 14, 2018

Lavish Kothari commented

Juergen Hoeller Thanks for a quick response.

I confirm that 5.1.4.BUILD-SNAPSHOT works good for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment