javajmx

Java 21 JMX: InvalidClassException when passing Instant as a parameter


I'm using Java 21 and invoking remote JMX methods.

Calling a remote JMX method that returns a java.time.Instant works fine. However, when I try to invoke a method that takes an java.time.Instant as a parameter, I get the following exception:

Exception in thread "main" java.io.InvalidClassException: filter status: REJECTED
        at java.base/java.io.ObjectInputStream.filterCheck(ObjectInputStream.java:1439)
        at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2071)
        at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1927)
        at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2252)
        at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1762)
        at java.base/java.io.ObjectInputStream.readArray(ObjectInputStream.java:2186)
        at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1750)
        at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:540)
        at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:498)
        at java.rmi/java.rmi.MarshalledObject.get(MarshalledObject.java:183)
        at java.management.rmi/javax.management.remote.rmi.RMIConnectionImpl.unwrap(RMIConnectionImpl.java:1590)
        at java.management.rmi/javax.management.remote.rmi.RMIConnectionImpl.unwrap(RMIConnectionImpl.java:1632)
        at java.management.rmi/javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:812)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:360)
        at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200)
        at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:714)
        at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196)
        at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:598)
        at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:844)
        at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:721)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
        at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:720)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
        at java.base/java.lang.Thread.run(Thread.java:1583)
        at java.rmi/sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:304)
        at java.rmi/sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:280)
        at java.rmi/sun.rmi.server.UnicastRef.invoke(UnicastRef.java:166)
        at jdk.remoteref/jdk.jmx.remote.internal.rmi.PRef.invoke(Unknown Source)
        at java.management.rmi/javax.management.remote.rmi.RMIConnectionImpl_Stub.invoke(RMIConnectionImpl_Stub.java:419)
        at java.management.rmi/javax.management.remote.rmi.RMIConnector$RemoteMBeanServerConnection.invoke(RMIConnector.java:1020)

Both the client and the server are running the same JDK - jdk-21.0.7.

I tried launching both JVMs with -Djdk.serialFilter=* but it made no difference.

server:

public class JMXServer {
    public interface JMXScriptMBean {
        Instant oper1();
        void oper2(Instant inst);
    }
    static class JMXScript implements JMXScriptMBean {
        @Override
        public Instant oper1() {
            return Instant.now(); 
        }
        @Override
        public void oper2(Instant inst) {
        }
    }
    public static void main(String[] args) throws Exception {
        // Create and register the MBean
        javax.management.MBeanServer mbs = java.lang.management.ManagementFactory.getPlatformMBeanServer();
        JMXScriptMBean scriptBean = new JMXScript();
        javax.management.ObjectName name = new javax.management.ObjectName("MDPubBeans:name=functionEnricher");
        mbs.registerMBean(scriptBean, name);

        System.out.println("JMXServer is running. Press Enter to exit...");
        System.in.read();
    }
}

client:

  public static void main(String[] args) throws Exception
  {
      JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
      JMXConnector jmxc = JMXConnectorFactory.connect(url);
      MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
      ObjectName on = new ObjectName("MDPubBeans:name=functionEnricher");
      Object output = mbsc.invoke(on, "oper1", new Object[0], new String[0]);
      System.out.println(output);
      output = mbsc.invoke(on, "oper2", new Object[]{Instant.now()}, new String[]{Instant.class.getName()});
      System.out.println(output);
  }

Is there a way to allow java.time.Instant as a parameter for remote JMX calls in Java 21? Any workaround or additional configuration needed?


Solution

  • JMX (open MBean) supports only a limited set of basic data types, and unfortunately, Instant is not one of them. Looks like internally, the JMX serializer enforces this restriction using a hardcoded serialization filter, which blocks unsupported types like Instant.

    Proper way to pass Instant is to wrap it in a CompositeData. Interestingly, Instant did seem to work when returned directly as an operator return value. I'm not entirely sure why that is.