This is an automated email from the ASF dual-hosted git repository.

rmaucher pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit d10d57466d941af78373df7169de60e25df148d9
Author: opencode <[email protected]>
AuthorDate: Thu Oct 15 10:44:16 2026 +0200

    Throw InstanceNotFoundException from the no-JMX MBean server
    
    When JMX support is disabled with --no-jmx, Registry.getMBeanServer()
    returns the NoJmxMBeanServer whose methods returned null or silently
    did nothing where the MBeanServer contract requires an exception or a
    non-null value. Downstream code that trusted the contract then hit
    null pointer exceptions, for example Registry.getMethodInfo()
    dereferencing the null MBeanInfo returned by getMBeanInfo (reached
    through the Manager application JMX proxy) and the boolean unboxing
    of the null result of invoke in ManagerServlet.isDeployed() and
    tryAddServiced().
    
    Since no MBean can ever be registered with this server, the truthful
    contract-compliant answer for any operation referencing an object
    name is InstanceNotFoundException, and the count of registered MBeans
    is zero. Update all such methods accordingly and return 0 from
    getMBeanCount. Methods without an InstanceNotFoundException throws
    clause that are unreachable through the overridden no-op Registry
    methods keep their previous behavior.
    
    Add a unit test covering the previously failing contract paths.
---
 .../tomcat/util/modeler/LocalStrings.properties    |   2 +
 .../tomcat/util/modeler/NoDescriptorRegistry.java  |  55 +++++++-----
 .../util/modeler/TestNoDescriptorRegistry.java     | 100 +++++++++++++++++++++
 3 files changed, 137 insertions(+), 20 deletions(-)

diff --git a/java/org/apache/tomcat/util/modeler/LocalStrings.properties 
b/java/org/apache/tomcat/util/modeler/LocalStrings.properties
index dfbd545fe3..fe3238e3e9 100644
--- a/java/org/apache/tomcat/util/modeler/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/modeler/LocalStrings.properties
@@ -40,6 +40,8 @@ managedMBean.noSet=Cannot find setter method [{0}] on 
resource [{1}]
 modules.digesterParseError=Error parsing registry data
 modules.readDescriptorsError=Error reading descriptors
 
+noJmxMBeanServer.mBeanNotFound=The MBean [{0}] was not found. JMX support is 
disabled.
+
 registry.cannotAccessRegistry=Guard object [{0}] does not allow access to the 
registry
 registry.createdServer=Created MBeanServer
 registry.existingServer=Using existing MBeanServer
diff --git a/java/org/apache/tomcat/util/modeler/NoDescriptorRegistry.java 
b/java/org/apache/tomcat/util/modeler/NoDescriptorRegistry.java
index 10bfce2c2f..e95dc7a23a 100644
--- a/java/org/apache/tomcat/util/modeler/NoDescriptorRegistry.java
+++ b/java/org/apache/tomcat/util/modeler/NoDescriptorRegistry.java
@@ -45,12 +45,16 @@ import javax.management.QueryExp;
 import javax.management.ReflectionException;
 import javax.management.loading.ClassLoaderRepository;
 
+import org.apache.tomcat.util.res.StringManager;
+
 /**
  * An implementation of the MBean registry that effectively disables MBean 
registration. This is typically used when low
  * memory footprint is a primary concern.
  */
 public class NoDescriptorRegistry extends Registry {
 
+    private static final StringManager sm = 
StringManager.getManager(NoDescriptorRegistry.class);
+
     /**
      * Constructs a new NoDescriptorRegistry instance.
      */
@@ -149,6 +153,16 @@ public class NoDescriptorRegistry extends Registry {
 
     private static class NoJmxMBeanServer implements MBeanServer {
 
+        /*
+         * No MBeans can be registered with this server, so any operation that 
references an object name fails with
+         * InstanceNotFoundException as required by the MBeanServer contract. 
Callers that relied on the previous null
+         * returns were subject to null pointer exceptions.
+         */
+
+        private static InstanceNotFoundException notFound(ObjectName name) {
+            return new 
InstanceNotFoundException(sm.getString("noJmxMBeanServer.mBeanNotFound", name));
+        }
+
         @Override
         public ObjectInstance createMBean(String className, ObjectName name) 
throws ReflectionException,
                 InstanceAlreadyExistsException, MBeanRegistrationException, 
NotCompliantMBeanException {
@@ -189,13 +203,13 @@ public class NoDescriptorRegistry extends Registry {
 
         @Override
         public void unregisterMBean(ObjectName name) throws 
InstanceNotFoundException, MBeanRegistrationException {
-
+            throw notFound(name);
         }
 
 
         @Override
         public ObjectInstance getObjectInstance(ObjectName name) throws 
InstanceNotFoundException {
-            return null;
+            throw notFound(name);
         }
 
 
@@ -219,42 +233,43 @@ public class NoDescriptorRegistry extends Registry {
 
         @Override
         public Integer getMBeanCount() {
-            return null;
+            // No MBeans are ever registered with this server
+            return Integer.valueOf(0);
         }
 
 
         @Override
         public Object getAttribute(ObjectName name, String attribute)
                 throws MBeanException, AttributeNotFoundException, 
InstanceNotFoundException, ReflectionException {
-            return null;
+            throw notFound(name);
         }
 
 
         @Override
         public AttributeList getAttributes(ObjectName name, String[] 
attributes)
                 throws InstanceNotFoundException, ReflectionException {
-            return null;
+            throw notFound(name);
         }
 
 
         @Override
         public void setAttribute(ObjectName name, Attribute attribute) throws 
InstanceNotFoundException,
                 AttributeNotFoundException, InvalidAttributeValueException, 
MBeanException, ReflectionException {
-
+            throw notFound(name);
         }
 
 
         @Override
         public AttributeList setAttributes(ObjectName name, AttributeList 
attributes)
                 throws InstanceNotFoundException, ReflectionException {
-            return null;
+            throw notFound(name);
         }
 
 
         @Override
         public Object invoke(ObjectName name, String operationName, Object[] 
params, String[] signature)
                 throws InstanceNotFoundException, MBeanException, 
ReflectionException {
-            return null;
+            throw notFound(name);
         }
 
 
@@ -273,35 +288,35 @@ public class NoDescriptorRegistry extends Registry {
         @Override
         public void addNotificationListener(ObjectName name, 
NotificationListener listener, NotificationFilter filter,
                 Object handback) throws InstanceNotFoundException {
-
+            throw notFound(name);
         }
 
 
         @Override
         public void addNotificationListener(ObjectName name, ObjectName 
listener, NotificationFilter filter,
                 Object handback) throws InstanceNotFoundException {
-
+            throw notFound(name);
         }
 
 
         @Override
         public void removeNotificationListener(ObjectName name, ObjectName 
listener)
                 throws InstanceNotFoundException, ListenerNotFoundException {
-
+            throw notFound(name);
         }
 
 
         @Override
         public void removeNotificationListener(ObjectName name, ObjectName 
listener, NotificationFilter filter,
                 Object handback) throws InstanceNotFoundException, 
ListenerNotFoundException {
-
+            throw notFound(name);
         }
 
 
         @Override
         public void removeNotificationListener(ObjectName name, 
NotificationListener listener)
                 throws InstanceNotFoundException, ListenerNotFoundException {
-
+            throw notFound(name);
         }
 
 
@@ -309,20 +324,20 @@ public class NoDescriptorRegistry extends Registry {
         public void removeNotificationListener(ObjectName name, 
NotificationListener listener,
                 NotificationFilter filter, Object handback)
                 throws InstanceNotFoundException, ListenerNotFoundException {
-
+            throw notFound(name);
         }
 
 
         @Override
         public MBeanInfo getMBeanInfo(ObjectName name)
                 throws InstanceNotFoundException, IntrospectionException, 
ReflectionException {
-            return null;
+            throw notFound(name);
         }
 
 
         @Override
         public boolean isInstanceOf(ObjectName name, String className) throws 
InstanceNotFoundException {
-            return false;
+            throw notFound(name);
         }
 
 
@@ -356,7 +371,7 @@ public class NoDescriptorRegistry extends Registry {
         @Override
         public ObjectInputStream deserialize(ObjectName name, byte[] data)
                 throws InstanceNotFoundException, OperationsException {
-            return null;
+            throw notFound(name);
         }
 
 
@@ -370,19 +385,19 @@ public class NoDescriptorRegistry extends Registry {
         @Override
         public ObjectInputStream deserialize(String className, ObjectName 
loaderName, byte[] data)
                 throws InstanceNotFoundException, OperationsException, 
ReflectionException {
-            return null;
+            throw notFound(loaderName);
         }
 
 
         @Override
         public ClassLoader getClassLoaderFor(ObjectName mbeanName) throws 
InstanceNotFoundException {
-            return null;
+            throw notFound(mbeanName);
         }
 
 
         @Override
         public ClassLoader getClassLoader(ObjectName loaderName) throws 
InstanceNotFoundException {
-            return null;
+            throw notFound(loaderName);
         }
 
 
diff --git a/test/org/apache/tomcat/util/modeler/TestNoDescriptorRegistry.java 
b/test/org/apache/tomcat/util/modeler/TestNoDescriptorRegistry.java
new file mode 100644
index 0000000000..5bdca9987b
--- /dev/null
+++ b/test/org/apache/tomcat/util/modeler/TestNoDescriptorRegistry.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.modeler;
+
+import java.util.Collections;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestNoDescriptorRegistry {
+
+    private static final ObjectName ONAME;
+
+    static {
+        try {
+            ONAME = new ObjectName("Tomcat:type=Tester,name=NoJmx");
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    public void testGetMBeanInfoThrows() throws Exception {
+        MBeanServer server = new NoDescriptorRegistry().getMBeanServer();
+        Assert.assertThrows(InstanceNotFoundException.class, () -> 
server.getMBeanInfo(ONAME));
+    }
+
+    @Test
+    public void testInvokeThrows() throws Exception {
+        MBeanServer server = new NoDescriptorRegistry().getMBeanServer();
+        Assert.assertThrows(InstanceNotFoundException.class,
+                () -> server.invoke(ONAME, "isDeployed", new Object[] { "ctx" 
}, new String[] { "java.lang.String" }));
+    }
+
+    @Test
+    public void testGetAttributeThrows() throws Exception {
+        MBeanServer server = new NoDescriptorRegistry().getMBeanServer();
+        Assert.assertThrows(InstanceNotFoundException.class, () -> 
server.getAttribute(ONAME, "name"));
+    }
+
+    @Test
+    public void testGetObjectInstanceThrows() throws Exception {
+        MBeanServer server = new NoDescriptorRegistry().getMBeanServer();
+        Assert.assertThrows(InstanceNotFoundException.class, () -> 
server.getObjectInstance(ONAME));
+    }
+
+    @Test
+    public void testRegistryGetMethodInfoThrows() throws Exception {
+        Registry registry = new NoDescriptorRegistry();
+        Assert.assertThrows(InstanceNotFoundException.class, () -> 
registry.getMethodInfo(ONAME, "test", 0));
+    }
+
+    @Test
+    public void testRegistryGetTypeInfoReturnsNull() throws Exception {
+        // Overridden by NoDescriptorRegistry, must not throw or fail
+        Registry registry = new NoDescriptorRegistry();
+        Assert.assertNull(registry.getType(ONAME, "name"));
+        Assert.assertNull(registry.getMethodInfo(ONAME, "test"));
+    }
+
+    @Test
+    public void testCountsAndQueries() throws Exception {
+        MBeanServer server = new NoDescriptorRegistry().getMBeanServer();
+        Assert.assertEquals(Integer.valueOf(0), server.getMBeanCount());
+        Assert.assertFalse(server.isRegistered(ONAME));
+        Assert.assertTrue(server.queryNames(null, null).isEmpty());
+        Assert.assertTrue(server.queryMBeans(null, null).isEmpty());
+        Assert.assertEquals(0, server.getDomains().length);
+    }
+
+    @Test
+    public void testUnregisterIsSilentlyIgnoredViaRegistry() throws Exception {
+        // unregisterComponent is a no-op for NoDescriptorRegistry and must not
+        // reach the MBeanServer methods that now throw
+        Registry registry = new NoDescriptorRegistry();
+        registry.unregisterComponent(ONAME);
+        registry.unregisterComponent(ONAME.getCanonicalName());
+        registry.registerComponent(new Object(), ONAME, null);
+        registry.registerComponent(new Object(), ONAME.getCanonicalName(), 
null);
+        registry.invoke(Collections.emptyList(), "test", false);
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to