JMX MBean not getting displayed on JConsole when bean declared using annotations

Prasanth picture Prasanth · Oct 18, 2013 · Viewed 7.2k times · Source

I am trying to write a sample JMX application. Here is my MBean class:

package com.pramati.jmx;

@Component
@ManagedResource(objectName="modelMBean:type=simple-calculator",
        description="Calculator performing basic arithmetic on integers")
public class SimpleCalculator {

    private int operand1;
    private int operand2;

    public SimpleCalculator() {
        System.out.println("SimpleCalculator - MBean created!!");
    }

    @ManagedOperation(description="Addition operation")
    public int add() {
        return operand1 + operand2;
    }

    @ManagedOperation(description="Multiplication operation")
    public int multiply() {
        return operand1 * operand2;
    }

    @ManagedOperation(description="Division operation")
    @ManagedOperationParameters({
        @ManagedOperationParameter(name="operand1", description="Dividend"),
        @ManagedOperationParameter(name="operand2", description="Divisor")
    })
    public int divide(int operand1, int operand2) {
        if(operand2 == 0) {
            throw new IllegalArgumentException("Can not divide by zero");
        }
        return operand1 / operand2;
    }

    @ManagedAttribute
    public int getOperand1() {
        return operand1;
    }

    @ManagedAttribute
    public void setOperand1(int operand1) {
        this.operand1 = operand1;
    }

    @ManagedAttribute
    public int getOperand2() {
        return operand2;
    }

    @ManagedAttribute
    public void setOperand2(int operand2) {
        this.operand2 = operand2;
    }


}

And here is the bean declaration from applicationContext:

<context:component-scan  base-package="com.pramati.jmx"/>
<bean id="mbeanExporter" class="org.springframework.jmx.export.annotation.AnnotationMBeanExporter"/>

Now when I run jconsole, I am not seeing my Mbean in it. But when I explicitly declare the bean as follows:

<bean id="calculator" class="com.pramati.jmx.SimpleCalculator">
    <property name="operand1" value="1000"/>
    <property name="operand2" value="50"/>
</bean>
 <bean id="mbeanExporter" class="org.springframework.jmx.export.annotation.AnnotationMBeanExporter"/>

Now if I run jconsole, I am seeing the Mbean. Why is it that the MBean is not getting registered when I am using component-scan?

Here is my complete applicationContext:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/mvc 
    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd    
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<bean id="mbeanExporter" class="org.springframework.jmx.export.annotation.AnnotationMBeanExporter"/>

<context:component-scan  base-package="com.pramati.model"/>
<context:component-scan  base-package="com.pramati.controller"/>
<context:component-scan  base-package="com.pramati.service"/>
<context:component-scan  base-package="com.pramati.validator"/>
<context:component-scan  base-package="com.pramati.type.converters"/>
<context:component-scan  base-package="com.pramati.jmx"/>

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="webBindingInitializer">
        <bean class="com.pramati.spring.mvc.CustomWebBindingInitializer"/>
    </property>
</bean>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

<mvc:interceptors>
    <bean class="com.pramati.spring.mvc.LoggingInterceptor"/>
    <mvc:interceptor>
        <mvc:mapping path="/reservationQuery/**"/>
        <bean class="com.pramati.spring.mvc.AuditTimeInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

<bean name="internalresourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
    <property name="prefix" value="/WEB-INF/jsp/" />
    <property name="suffix" value=".jsp" />
    <property name="order" value="#{xmlViewResolver.order+1}"/>
</bean>

<bean name="xmlViewResolver" class="org.springframework.web.servlet.view.XmlViewResolver">
    <property name="location" value="/WEB-INF/court-views.xml"/>
    <property name="order" value="#{T(org.springframework.core.Ordered).HIGHEST_PRECEDENCE}"/>
</bean>

<bean name="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
    <property name="defaultLocale" value="fr_FR"></property>
</bean>

<bean name="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${connection.driverClassName}"/>
    <property name="url" value="${connection.url}"/>
    <property name="username" value="${connection.username}"/>
    <property name="password" value="${connection.password}"/>
    <property name="initialSize" value="${connection.initialSize}"/>
    <property name="maxActive" value="${connection.maxActive}"/>
</bean>

<context:property-placeholder location="classpath:spring/config.properties"/>

<bean name="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename">
        <value>properties/resourceBundle</value>
    </property>
</bean>

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <prop key="com.pramati.exception.ResourceNotFoundException">
                exceptions/resourceNotFound
            </prop>
        </props>
    </property>
    <property name="defaultErrorView" value="exceptions/error"/>
</bean>

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean" >
    <property name="converters">
        <set>
            <bean class="com.pramati.type.converters.StringToSportTypeConverter"/>
            <bean class="com.pramati.type.converters.StringToDateConverter"/>
            <bean class="com.pramati.type.converters.StringToPlayerConverter"/>
        </set>
    </property>
</bean>

<bean id="defaultReservation" class="com.pramati.model.Reservation">
    <property name="courtName" value="Soccer Court #1"/>
    <property name="date" value="11-11-2011"/>
    <property name="hour" value="15"/>
    <property name="player" value="Prasanth,9010107771"/>
    <property name="sportType" value="2"></property>
</bean>

</beans>

Here is security context:

<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security-3.1.xsd
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

<beans:bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
    <beans:property name="decisionVoters">
        <beans:list>
            <beans:bean class="org.springframework.security.access.vote.RoleVoter">
                <beans:property name="rolePrefix" value="ROLE_"/>
            </beans:bean>
            <beans:bean class="org.springframework.security.access.vote.AuthenticatedVoter"/>
            <beans:bean class="com.pramati.spring.mvc.LocalIpVoter"/>
        </beans:list>
    </beans:property>
</beans:bean>

<http access-decision-manager-ref="accessDecisionManager">
    <intercept-url pattern="/app/messageList*" access="ROLE_USER,ROLE_ANONYMOUS"/>
    <intercept-url pattern="/app/messagePost*" access="ROLE_USER"/>
    <intercept-url pattern="/app/messageDelete*" access="ROLE_ADMIN,IP_LOCAL_HOST"/>

    <form-login login-page="/login.jsp" default-target-url="/app/messagePost" 
        authentication-failure-url="/login.jsp?error=true"/>

    <logout logout-success-url="/login.jsp"/>

    <remember-me services-alias="rememberMeService" key="springRocks" data-source-ref="dataSource"/>
    <!-- <remember-me data-source-ref="dataSource" key="pramati"/> -->

    <session-management session-authentication-error-url="/login.jsp?error=alreadyLoggedin" >
        <concurrency-control max-sessions="1" error-if-maximum-exceeded="true"
            expired-url="/login.jsp?error=alreadyLoggedin"/>
    </session-management>

</http>

<beans:bean id="tokenRepository" class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
    <beans:property name="dataSource" ref="dataSource"/>
    <beans:property name="createTableOnStartup" value="false"/>
</beans:bean>

<beans:bean id="rememberMeService" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
    <beans:property name="key" value="springRocks"/>
    <beans:property name="userDetailsService" ref="userDetailsService"/>
    <beans:property name="tokenRepository" ref="tokenRepository"/>
    <beans:property name="alwaysRemember" value="true"/>
</beans:bean>

<authentication-manager>
    <authentication-provider>

        <!-- TBD <password-encoder hash="md5"/> -->
        <jdbc-user-service id="userDetailsService" data-source-ref="dataSource" 
            users-by-username-query=
                "SELECT username, password, true as enabled
                 FROM MEMBER
                 WHERE username=?"
            authorities-by-username-query=
                "SELECT member.username, role.role as authorities
                 FROM ROLE role, MEMBER member
                 WHERE role.member_id=member.id and member.username=?"/>
        <!-- <user-service>
            <user name="admin" password="password" authorities="ROLE_ADMIN,ROLE_USER"/>
            <user name="user" password="password" authorities="ROLE_USER"/>
        </user-service> -->
    </authentication-provider>
</authentication-manager>

</beans:beans>

Answer

Keerthivasan picture Keerthivasan · Oct 18, 2013

As I understand, the @Component annotation indicates that this class is eligible to be a Spring bean when context:component-scan runs. The @ManagedResource annotation indicates that the bean can be exported as a JMX MBean. It's objectName attribute is optional, but can be used to specify a specific name for the MBean. Please change your @ManagedResource as follows

@ManagedResource(objectName="com.pramati.jmx:name=simplecalculator",
        description="Calculator performing basic arithmetic on integers")

We are just giving the name for the Mbean.In this case it will be exposed under the 'com.pramati.jmx' directory with the name 'simplecalculator'.

Add this line and check

<context:mbean-export/>

You can add the logging options to debug

@ManagedResource(objectName="com.pramati.jmx:name=simplecalculator", description="Calculator performing basic arithmetic on integers", log=true,
    logFile="jmx.log")