Search code examples
spring-bootjconsolespring-jmxjmc

how to return object value while using spring boot jmx


I am using spring boot jmx to remotely execute the method when a change happen in database. Every thing works well and I could monitor my app using jmc.exe in windows when I use primitive data type as return value . Now I need to return Object(not primitive data type but a class) value as return of my method. Actually this object is in the same package that my class is. but when I run the method remotely using jmx I get

java.rmi.UnmarshalException: error unmarshalling return; nested exception is: 
java.lang.ClassNotFoundException: com.ff.KeyValue (no security manager: RMI class loader disabled).

my code is as below

@Service
@ManagedResource(description = "JMX managed resource for updating map when database is updated",
        objectName = "chargeCode:name=ChargeCodeService")
public class ChargeCodeService {

    private static final Logger logger = LoggerFactory.getLogger(ChargeCodeService.class);

    private final ChargeCodeRepository chargeCodeRepository;
    private Map<String, KeyValue<String,String>> chargeCodeMap = new HashMap<>();

    @Autowired
    public ChargeCodeService(ChargeCodeRepository chargeCodeRepository) {
        this.chargeCodeRepository = chargeCodeRepository;
    }

    @PostConstruct
    @ManagedOperation
    public Map<String, KeyValue<String,String>> chargeCodMapInitial() {
        logger.info("ready to read chargeCode data from database. this operation will do when an update occure in database");
        List<ChargeCode> chargeCodes = chargeCodeRepository.findAll();
        for (ChargeCode chargeCode : chargeCodes) {
            chargeCodeMap.put(chargeCode.getIacChargeCode(),new KeyValue<>( chargeCode.getBankChargeCode(),chargeCode.getTopupProvider()));
        }
        return chargeCodeMap;
    }

    @ManagedAttribute
    public Map<String, KeyValue<String, String>> getChargeCodeMap() {
        return chargeCodeMap;
    }
}

and the KeyValue class is as below:

public class KeyValue<K, V> implements Map.Entry<K, V>, Serializable {

    private static final long serialVersionUID = -2610138893852455839L;

    private K key;
    private V value;

    public KeyValue() {
    }

    public KeyValue(K key, V value)
    {
        this.key = key;
        this.value = value;
    }

  getter,setter;
}

Solution

  • you must return CompositeData or TabularDataSupport as your return type. Following code demonstrate both and should work for you. Let me know if you have any trouble with it.

    package com.example.demo;
    
    import org.springframework.jmx.export.annotation.ManagedOperation;
    import org.springframework.jmx.export.annotation.ManagedOperationParameter;
    import org.springframework.jmx.export.annotation.ManagedOperationParameters;
    import org.springframework.jmx.export.annotation.ManagedResource;
    import org.springframework.stereotype.Component;
    
    import javax.management.openmbean.*;
    
    @Component
    @ManagedResource(objectName="com.example.demo.jmx:name=ServerManager",
            description="Server manager.")
    public class SampleOperation {
        private static final String[] itemNames = new String[] { "name", "age" };
        private static final String[] itemDescription = new String[] { "Your name", "Your age" };
        private static final OpenType<?>[] itemTypes = new OpenType[] {
                SimpleType.STRING,
                SimpleType.INTEGER
        };
    
        public static final String IAC_CHARGE_CODE = "IacChargeCode";
        public static final String BANK_CHARGE_CODE = "BankChargeCode";
        public static final String TOP_UP_PROVIDER = "TopUpProvider";
        public static final String[] rowItemNames = new String[] { IAC_CHARGE_CODE, BANK_CHARGE_CODE, TOP_UP_PROVIDER };
        public static final String[] rowItemDescriptions = new String[] {
                "Iac charge code",
                "Bank charge code",
                "Top up provider"
        };
    
        @ManagedOperation(description="Get the complex object.")
        @ManagedOperationParameters({
                @ManagedOperationParameter(name="name", description= "Your name."),
                @ManagedOperationParameter(name="age", description= "Your age.")})
        public CompositeData simpleData(String name, int age) throws Exception {
            CompositeType compositeType = new CompositeType(
                    "NameAndAge",
                    "name and age",
                    itemNames, itemDescription, itemTypes);
    
            CompositeData data = new CompositeDataSupport(compositeType, itemNames, new Object[] {
                    name, age
            });
            return data;
        }
    
        @ManagedOperation(description="Retrieve recodes from DB.")
        public TabularDataSupport tabular() throws OpenDataException {
            OpenType<?>[] rowItemTypes = new OpenType[]{
                    SimpleType.STRING, SimpleType.STRING, SimpleType.STRING
            };
    
            CompositeType compositeRowType = new CompositeType(
                    "chargeRow",
                    "some row",
                    rowItemNames,
                    rowItemDescriptions,
                    rowItemTypes);
            TabularType tabularType = new TabularType(
                    "charge table",
                    "Sample table of charge",
                    compositeRowType,
                    rowItemNames);
            TabularDataSupport rows = new TabularDataSupport(tabularType);
    
            rows.putAll(new CompositeData[]{
                    new CompositeDataSupport(compositeRowType, rowItemNames, new Object[]{
                            "charge code #1", "bank charge code #1", "Top up #1"
                    }),
                    new CompositeDataSupport(compositeRowType, rowItemNames, new Object[]{
                            "charge code #2", "bank charge code #2", "Top up #2"
                    }),
                    new CompositeDataSupport(compositeRowType, rowItemNames, new Object[]{
                            "charge code #3", "bank charge code #3", "Top up #3"
                    }),
            });
            return rows;
        }
    }