frankgh commented on code in PR #2318:
URL: https://github.com/apache/cassandra/pull/2318#discussion_r1190132193


##########
build.xml:
##########
@@ -263,6 +265,8 @@
         <string>--add-opens java.base/java.math=ALL-UNNAMED</string>
         <string>--add-opens java.base/java.lang.reflect=ALL-UNNAMED</string>
         <string>--add-opens java.base/java.net=ALL-UNNAMED</string>
+
+        <string>--add-opens java.rmi/sun.rmi.transport.tcp=ALL-UNNAMED</string>

Review Comment:
   is this `--add-opens` scoped to the in-jvm dtests only?



##########
src/java/org/apache/cassandra/config/CassandraRelevantProperties.java:
##########
@@ -219,6 +219,8 @@
 
     /** This property indicates whether disable_mbean_registration is true */
     
IS_DISABLED_MBEAN_REGISTRATION("org.apache.cassandra.disable_mbean_registration"),
+    /** This property indicates if the code is running under the in-jvm dtest 
framework */

Review Comment:
   does the order of the enum matter here? Some enums might be sensitive to the 
order, so usually new enums are added at the end. It might not be the case here



##########
src/java/org/apache/cassandra/utils/MBeanWrapper.java:
##########
@@ -170,6 +186,125 @@ public void unregisterMBean(ObjectName mbeanName, 
OnException onException)
         }
     }
 
+    static class InstanceMBeanWrapper implements MBeanWrapper
+    {
+        private final MBeanServer mbs;
+        public final UUID id = UUID.randomUUID();
+
+        public InstanceMBeanWrapper(String hostname)
+        {
+            mbs = MBeanServerFactory.createMBeanServer(hostname + "-" + id);
+        }
+
+        public void registerMBean(Object obj, ObjectName mbeanName, 
OnException onException)
+        {
+            try
+            {
+                mbs.registerMBean(obj, mbeanName);
+            }
+            catch (Exception e)
+            {
+                onException.handler.accept(e);
+            }
+        }
+
+        public boolean isRegistered(ObjectName mbeanName, OnException 
onException)
+        {
+            try
+            {
+                return mbs.isRegistered(mbeanName);
+            }
+            catch (Exception e)
+            {
+                onException.handler.accept(e);
+            }
+            return false;
+        }
+
+        public void unregisterMBean(ObjectName mbeanName, OnException 
onException)
+        {
+            try
+            {
+                mbs.unregisterMBean(mbeanName);
+            }
+            catch (Exception e)
+            {
+                onException.handler.accept(e);
+            }
+        }
+
+        public MBeanServer getMBeanServer()
+        {
+            return mbs;
+        }
+
+        public void close() {
+            mbs.queryNames(null, null).forEach(name -> {
+                try {
+                    if 
(!name.getCanonicalName().contains("MBeanServerDelegate"))
+                        mbs.unregisterMBean(name);
+                } catch (Throwable e) {
+                    logger.debug("Could not unregister mbean {}", 
name.getCanonicalName());
+                }
+            });
+            MBeanServerFactory.releaseMBeanServer(mbs);
+        }
+    }
+
+    static class DelegatingMbeanWrapper implements MBeanWrapper {
+        MBeanWrapper delegate;
+
+        public DelegatingMbeanWrapper(MBeanWrapper mBeanWrapper)
+        {
+            delegate = mBeanWrapper;
+        }
+
+        public void setDelegate(MBeanWrapper wrapper) {
+            delegate = wrapper;
+        }
+
+        public MBeanWrapper getDelegate()
+        {
+            return delegate;
+        }

Review Comment:
   new line after brace?
   ```suggestion
           }
   
   ```



##########
test/distributed/org/apache/cassandra/distributed/impl/Instance.java:
##########
@@ -754,6 +786,146 @@ else if (cluster instanceof Cluster)
         initialized = true;
     }
 
+    private void startJmx()
+    {
+        try
+        {
+            IS_DISABLED_MBEAN_REGISTRATION.setBoolean(false);
+            InetAddress addr = config.broadcastAddress().getAddress();
+
+            int jmxPort = config.jmxPort();
+
+            String hostname = addr.getHostAddress();
+            wrapper = new MBeanWrapper.InstanceMBeanWrapper(hostname + ":" + 
jmxPort);
+            ((MBeanWrapper.DelegatingMbeanWrapper) 
MBeanWrapper.instance).setDelegate(wrapper);
+            Map<String, Object> env = new HashMap<>();
+
+            serverSocketFactory = new 
CollectingRMIServerSocketFactoryImpl(addr);
+            env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE,
+                    serverSocketFactory);
+            clientSocketFactory = new RMIClientSocketFactoryImpl(addr);
+            env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE,
+                    clientSocketFactory);
+
+            // configure the RMI registry
+            registry = new JMXServerUtils.JmxRegistry(jmxPort,
+                                                      clientSocketFactory,
+                                                      serverSocketFactory,
+                                                      "jmxrmi");
+
+            // Mark the JMX server as a permanently exported object. This 
allows the JVM to exit with the
+            // server running and also exempts it from the distributed GC 
scheduler which otherwise would
+            // potentially attempt a full GC every 
`sun.rmi.dgc.server.gcInterval` millis (default is 3600000ms)
+            // For more background see:
+            //   - CASSANDRA-2967
+            //   - https://www.jclarity.com/2015/01/27/rmi-system-gc-unplugged/
+            //   - https://bugs.openjdk.java.net/browse/JDK-6760712
+            env.put("jmx.remote.x.daemon", "true");
+
+            // Set the port used to create subsequent connections to exported 
objects over RMI. This simplifies
+            // configuration in firewalled environments, but it can't be used 
in conjuction with SSL sockets.
+            // See: CASSANDRA-7087
+            int rmiPort = COM_SUN_MANAGEMENT_JMXREMOTE_RMI_PORT.getInt();
+
+            // We create the underlying RMIJRMPServerImpl so that we can 
manually bind it to the registry,
+            // rather then specifying a binding address in the JMXServiceURL 
and letting it be done automatically
+            // when the server is started. The reason for this is that if the 
registry is configured with SSL
+            // sockets, the JMXConnectorServer acts as its client during the 
binding which means it needs to
+            // have a truststore configured which contains the registry's 
certificate. Manually binding removes
+            // this problem.
+            // See CASSANDRA-12109.
+            jmxRmiServer = new RMIJRMPServerImpl(rmiPort,
+                                                 (RMIClientSocketFactory) 
env.get(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE),
+                                                 (RMIServerSocketFactory) 
env.get(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE),
+                                                 env);
+            JMXServiceURL serviceURL = new JMXServiceURL("rmi", hostname, 
rmiPort);
+            jmxConnectorServer = new RMIConnectorServer(serviceURL, env, 
jmxRmiServer, wrapper.getMBeanServer());
+
+            jmxConnectorServer.start();
+
+            IS_DISABLED_MBEAN_REGISTRATION.setBoolean(false);
+
+            registry.setRemoteServerStub(jmxRmiServer.toStub());
+            JMXServerUtils.logJmxServiceUrl(addr, jmxPort);
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException("Feature.JMX was enabled but could not 
be started.", e);
+        }
+    }
+
+    private void stopJmx() throws ClassNotFoundException, 
IllegalAccessException, InvocationTargetException, NoSuchMethodException, 
IOException, NoSuchFieldException, InterruptedException
+    {
+        if (!config.has(JMX))
+            return;
+        // First, swap the mbean wrapper back to a NoOp wrapper
+        // This prevents later attempts to unregister mbeans from failing in 
Cassandra code, as we're going to
+        // unregister all of them here
+        
((MBeanWrapper.DelegatingMbeanWrapper)MBeanWrapper.instance).setDelegate(new 
MBeanWrapper.NoOpMBeanWrapper());
+        try {

Review Comment:
   seems like formatting is off for this method, for example 
   ```suggestion
           try
           {
   ```



##########
src/java/org/apache/cassandra/utils/RMIClientSocketFactoryImpl.java:
##########
@@ -0,0 +1,45 @@
+/*
+ * 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.cassandra.utils;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.rmi.server.RMIClientSocketFactory;
+
+/**
+ * This class is used to override the local address the JMX client calculates 
when trying to connect,
+ * which can otherwise be influences by the system property 
"java.rmi.server.hostname" in strange and

Review Comment:
   ```suggestion
    * which can otherwise be influenced by the system property 
"java.rmi.server.hostname" in strange and
   ```



##########
src/java/org/apache/cassandra/utils/JMXServerUtils.java:
##########
@@ -362,5 +365,24 @@ public String[] list() throws RemoteException {
         public void setRemoteServerStub(Remote remoteServerStub) {
             this.remoteServerStub = remoteServerStub;
         }
+
+        /**
+         * Closes the underlying JMX registry by unexporting this instance.
+         * There is no reason to do this except for in-jvm dtests where we need
+         * to stop the registry, so we can start with a clean slate for future 
cluster
+         * builds, and the superclass never expects to be shut down and 
therefore doesn't
+         * handle this edge case at all.
+         */
+        @VisibleForTesting
+        public void close() {
+            try
+            {
+                UnicastRemoteObject.unexportObject(this, true);
+            }
+            catch (NoSuchObjectException e)

Review Comment:
   maybe rename the variable to indicate it's ignored
   ```suggestion
               catch (NoSuchObjectException ignored)
   ```



##########
src/java/org/apache/cassandra/utils/MBeanWrapper.java:
##########
@@ -170,6 +186,125 @@ public void unregisterMBean(ObjectName mbeanName, 
OnException onException)
         }
     }
 
+    static class InstanceMBeanWrapper implements MBeanWrapper
+    {
+        private final MBeanServer mbs;
+        public final UUID id = UUID.randomUUID();
+
+        public InstanceMBeanWrapper(String hostname)
+        {
+            mbs = MBeanServerFactory.createMBeanServer(hostname + "-" + id);
+        }
+
+        public void registerMBean(Object obj, ObjectName mbeanName, 
OnException onException)

Review Comment:
   can we add the `@Override` annotation to distinguish between internal 
methods and overridden methods?



##########
test/distributed/org/apache/cassandra/distributed/impl/INodeProvisionStrategy.java:
##########
@@ -56,6 +56,11 @@ public int nativeTransportPort(int nodeNum)
                     {
                         return 9041 + nodeNum;
                     }
+
+                    public int jmxPort(int nodeNum)

Review Comment:
   `@Override` annotation?



##########
src/java/org/apache/cassandra/utils/MBeanWrapper.java:
##########
@@ -170,6 +186,125 @@ public void unregisterMBean(ObjectName mbeanName, 
OnException onException)
         }
     }
 
+    static class InstanceMBeanWrapper implements MBeanWrapper
+    {
+        private final MBeanServer mbs;
+        public final UUID id = UUID.randomUUID();
+
+        public InstanceMBeanWrapper(String hostname)
+        {
+            mbs = MBeanServerFactory.createMBeanServer(hostname + "-" + id);
+        }
+
+        public void registerMBean(Object obj, ObjectName mbeanName, 
OnException onException)
+        {
+            try
+            {
+                mbs.registerMBean(obj, mbeanName);
+            }
+            catch (Exception e)
+            {
+                onException.handler.accept(e);
+            }
+        }
+
+        public boolean isRegistered(ObjectName mbeanName, OnException 
onException)
+        {
+            try
+            {
+                return mbs.isRegistered(mbeanName);
+            }
+            catch (Exception e)
+            {
+                onException.handler.accept(e);
+            }
+            return false;
+        }
+
+        public void unregisterMBean(ObjectName mbeanName, OnException 
onException)
+        {
+            try
+            {
+                mbs.unregisterMBean(mbeanName);
+            }
+            catch (Exception e)
+            {
+                onException.handler.accept(e);
+            }
+        }
+
+        public MBeanServer getMBeanServer()
+        {
+            return mbs;
+        }
+
+        public void close() {
+            mbs.queryNames(null, null).forEach(name -> {
+                try {
+                    if 
(!name.getCanonicalName().contains("MBeanServerDelegate"))
+                        mbs.unregisterMBean(name);
+                } catch (Throwable e) {
+                    logger.debug("Could not unregister mbean {}", 
name.getCanonicalName());

Review Comment:
   log at warn maybe?
   ```suggestion
                       logger.warn("Could not unregister mbean {}", 
name.getCanonicalName(), e);
   ```



##########
test/distributed/org/apache/cassandra/distributed/impl/Instance.java:
##########
@@ -754,6 +786,146 @@ else if (cluster instanceof Cluster)
         initialized = true;
     }
 
+    private void startJmx()
+    {
+        try
+        {
+            IS_DISABLED_MBEAN_REGISTRATION.setBoolean(false);
+            InetAddress addr = config.broadcastAddress().getAddress();
+
+            int jmxPort = config.jmxPort();
+
+            String hostname = addr.getHostAddress();
+            wrapper = new MBeanWrapper.InstanceMBeanWrapper(hostname + ":" + 
jmxPort);
+            ((MBeanWrapper.DelegatingMbeanWrapper) 
MBeanWrapper.instance).setDelegate(wrapper);
+            Map<String, Object> env = new HashMap<>();
+
+            serverSocketFactory = new 
CollectingRMIServerSocketFactoryImpl(addr);
+            env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE,
+                    serverSocketFactory);
+            clientSocketFactory = new RMIClientSocketFactoryImpl(addr);
+            env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE,
+                    clientSocketFactory);
+
+            // configure the RMI registry
+            registry = new JMXServerUtils.JmxRegistry(jmxPort,
+                                                      clientSocketFactory,
+                                                      serverSocketFactory,
+                                                      "jmxrmi");
+
+            // Mark the JMX server as a permanently exported object. This 
allows the JVM to exit with the
+            // server running and also exempts it from the distributed GC 
scheduler which otherwise would
+            // potentially attempt a full GC every 
`sun.rmi.dgc.server.gcInterval` millis (default is 3600000ms)
+            // For more background see:
+            //   - CASSANDRA-2967
+            //   - https://www.jclarity.com/2015/01/27/rmi-system-gc-unplugged/
+            //   - https://bugs.openjdk.java.net/browse/JDK-6760712
+            env.put("jmx.remote.x.daemon", "true");
+
+            // Set the port used to create subsequent connections to exported 
objects over RMI. This simplifies
+            // configuration in firewalled environments, but it can't be used 
in conjuction with SSL sockets.
+            // See: CASSANDRA-7087
+            int rmiPort = COM_SUN_MANAGEMENT_JMXREMOTE_RMI_PORT.getInt();
+
+            // We create the underlying RMIJRMPServerImpl so that we can 
manually bind it to the registry,
+            // rather then specifying a binding address in the JMXServiceURL 
and letting it be done automatically
+            // when the server is started. The reason for this is that if the 
registry is configured with SSL
+            // sockets, the JMXConnectorServer acts as its client during the 
binding which means it needs to
+            // have a truststore configured which contains the registry's 
certificate. Manually binding removes
+            // this problem.
+            // See CASSANDRA-12109.
+            jmxRmiServer = new RMIJRMPServerImpl(rmiPort,
+                                                 (RMIClientSocketFactory) 
env.get(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE),
+                                                 (RMIServerSocketFactory) 
env.get(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE),
+                                                 env);
+            JMXServiceURL serviceURL = new JMXServiceURL("rmi", hostname, 
rmiPort);
+            jmxConnectorServer = new RMIConnectorServer(serviceURL, env, 
jmxRmiServer, wrapper.getMBeanServer());
+
+            jmxConnectorServer.start();
+
+            IS_DISABLED_MBEAN_REGISTRATION.setBoolean(false);
+
+            registry.setRemoteServerStub(jmxRmiServer.toStub());
+            JMXServerUtils.logJmxServiceUrl(addr, jmxPort);
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException("Feature.JMX was enabled but could not 
be started.", e);
+        }
+    }
+
+    private void stopJmx() throws ClassNotFoundException, 
IllegalAccessException, InvocationTargetException, NoSuchMethodException, 
IOException, NoSuchFieldException, InterruptedException
+    {
+        if (!config.has(JMX))
+            return;
+        // First, swap the mbean wrapper back to a NoOp wrapper
+        // This prevents later attempts to unregister mbeans from failing in 
Cassandra code, as we're going to
+        // unregister all of them here
+        
((MBeanWrapper.DelegatingMbeanWrapper)MBeanWrapper.instance).setDelegate(new 
MBeanWrapper.NoOpMBeanWrapper());
+        try {
+            wrapper.close();
+        } catch (Throwable e) {
+            inInstancelogger.warn("failed to close wrapper.", e);
+        }
+        try {
+            jmxConnectorServer.stop();
+        } catch (Throwable e) {
+            inInstancelogger.warn("failed to close jmxConnectorServer.", e);
+        }
+        try {
+            registry.close();
+        } catch (Throwable e) {
+            inInstancelogger.warn("failed to close registry.", e);
+        }
+        try {
+            serverSocketFactory.close();
+        } catch (Throwable e) {
+            inInstancelogger.warn("failed to close serverSocketFactory.", e);
+        }
+        // The TCPEndpoint class holds references to a class in the in-jvm 
dtest framework
+        // which transitively has a reference to the InstanceClassLoader, so 
we need to
+        // make sure to remove the reference to them when the instance is 
shutting down
+        clearMapField(TCPEndpoint.class, null, "localEndpoints", 
this::endpointCreatedByThisClassloader);
+        Thread.sleep(2000); // Allow JMX background processes time to stop
+    }
+
+    /**
+     * Checks to make sure the endpoint in question was created by the 
instance classloader
+     * for this instance to prevent us from over-removing items.
+     * @param entry The map entry to be checked
+     * @return true, if the TCPEndpoint in the map entry was created by the 
InstanceClassloader instance
+     *         created by this Instance. Otherwise, false.
+     */
+    private boolean endpointCreatedByThisClassloader(Map.Entry<TCPEndpoint, 
LinkedList> entry) {

Review Comment:
   formatting?
   ```suggestion
       private boolean endpointCreatedByThisClassloader(Map.Entry<TCPEndpoint, 
LinkedList> entry)
       {
   ```



##########
build.xml:
##########
@@ -207,6 +207,7 @@
         <string>--add-exports 
java.management.rmi/com.sun.jmx.remote.internal.rmi=ALL-UNNAMED</string>
         <string>--add-exports java.rmi/sun.rmi.registry=ALL-UNNAMED</string>
         <string>--add-exports java.rmi/sun.rmi.server=ALL-UNNAMED</string>
+        <string>--add-exports 
java.rmi/sun.rmi.transport.tcp=ALL-UNNAMED</string>

Review Comment:
   is this `--add-opens` scoped to the in-jvm dtests only?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to