SM-2161: JMX InstanceAlreadyExistsException when restarting web distribution on WebSphere

git-svn-id: https://svn.apache.org/repos/asf/servicemix/smx3/trunk@1366290 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/management/ManagementContext.java b/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/management/ManagementContext.java
index c306633..57eb331 100644
--- a/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/management/ManagementContext.java
+++ b/core/servicemix-core/src/main/java/org/apache/servicemix/jbi/management/ManagementContext.java
@@ -26,11 +26,15 @@
 import java.util.concurrent.Executors;
 
 import javax.jbi.JBIException;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
 import javax.management.JMException;
 import javax.management.MBeanAttributeInfo;
 import javax.management.MBeanOperationInfo;
+import javax.management.MBeanRegistrationException;
 import javax.management.MBeanServer;
 import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
 import javax.management.ObjectName;
 
 import org.apache.servicemix.jbi.container.EnvironmentContext;
@@ -91,6 +95,10 @@
         return mbeanServerContext.getMBeanServer();
     }
 
+    protected void setMBeanServer(MBeanServer server) {
+        mbeanServerContext.setMBeanServer(server);
+    }
+
     /**
      * @return the domain
      */
@@ -531,17 +539,33 @@
      * @param description
      * @throws JMException
      */
-    public void registerMBean(ObjectName name, Object resource, Class interfaceMBean, String description) throws JMException {
+    public ObjectName registerMBean(ObjectName name, Object resource, Class interfaceMBean, String description) throws JMException {
         if (mbeanServerContext.getMBeanServer() != null) {
             Object mbean = MBeanBuilder.buildStandardMBean(resource, interfaceMBean, description, executors);
-            if (mbeanServerContext.getMBeanServer().isRegistered(name)) {
-                mbeanServerContext.getMBeanServer().unregisterMBean(name);
-            }
-            mbeanServerContext.getMBeanServer().registerMBean(mbean, name);
-            beanMap.put(name, resource);
+            return registerMBean(name, resource, mbean);
+        } else {
+            return name;
         }
     }
 
+    /*
+     * Register an MBean for a given resource, specifying the object name and the actual MBean implementation
+     *
+     * @param name the MBean's ObjectName
+     * @param resource the original bean for which the MBean is a wrapper
+     * @param mbean the MBean implementation
+     */
+    protected ObjectName registerMBean(ObjectName name, Object resource, Object mbean)
+        throws InstanceNotFoundException, MBeanRegistrationException, InstanceAlreadyExistsException, NotCompliantMBeanException {
+
+        if (mbeanServerContext.getMBeanServer().isRegistered(name)) {
+            mbeanServerContext.getMBeanServer().unregisterMBean(name);
+        }
+        ObjectName objectName = mbeanServerContext.getMBeanServer().registerMBean(mbean, name).getObjectName();
+        beanMap.put(objectName, resource);
+        return objectName;
+    }
+
     /**
      * Retrive an System ObjectName
      * 
@@ -619,8 +643,8 @@
             }
             ObjectName objName = createObjectName(service);
             LOGGER.debug("Registering system service: {}", objName);
-            registerMBean(objName, service, interfaceType, service.getDescription());
-            systemServices.put(name, objName);
+            ObjectName registeredName = registerMBean(objName, service, interfaceType, service.getDescription());
+            systemServices.put(name, registeredName);
         } catch (MalformedObjectNameException e) {
             throw new JBIException(e);
         } catch (JMException e) {
diff --git a/core/servicemix-core/src/test/java/org/apache/servicemix/jbi/management/WebSphereManagementContextTest.java b/core/servicemix-core/src/test/java/org/apache/servicemix/jbi/management/WebSphereManagementContextTest.java
new file mode 100644
index 0000000..027cefb
--- /dev/null
+++ b/core/servicemix-core/src/test/java/org/apache/servicemix/jbi/management/WebSphereManagementContextTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.servicemix.jbi.management;
+
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectName;
+import javax.management.StandardMBean;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.jbi.container.JBIContainer;
+import org.junit.Test;
+
+/**
+ * Additional tests to ensure the {@link ManagementContext} behaves properly when the actual ObjectName does not match
+ * the ObjectName used for registration (e.g. on WebSphere, where cell/node/process information gets appended to the ObjectName)
+ */
+public class WebSphereManagementContextTest extends TestCase {
+
+    private static final ObjectName OBJECT_NAME =
+            ManagementContext.getSystemObjectName(ManagementContext.DEFAULT_DOMAIN, JBIContainer.DEFAULT_NAME, SampleMBean.class);
+
+    @Test
+    public void testObjectNameAltered() throws Exception {
+        ManagementContext context = new ManagementContext();
+        MBeanServer server = MBeanServerFactory.createMBeanServer();
+        context.setMBeanServer(server);
+
+        SampleMBeanImpl impl = new SampleMBeanImpl();
+        RenamingStandardMBean mbean = new RenamingStandardMBean(impl, SampleMBean.class);
+
+        // let's register and unregister the MBean
+        context.registerMBean(OBJECT_NAME, impl, mbean);
+        context.unregisterMBean(impl);
+
+        // if the previous unregistration failed because of non-matching object names, the method below will throw
+        // javax.management.InstanceAlreadyExistsException
+        context.registerMBean(OBJECT_NAME, impl, mbean);
+    }
+
+
+    /*
+     * MBean interface definition used for testing
+     */
+    public static interface SampleMBean {
+
+        void doSomething();
+
+    }
+
+    /*
+     * MBean implementation
+     */
+    public static final class SampleMBeanImpl implements SampleMBean {
+
+        public void doSomething() {
+            // graciously do nothing here
+        }
+    }
+
+    /*
+     * {@link StandardMBean} implementation that will append parts to the MBean's object name upon registration
+     * (i.e. adding node/cell/process parts similar to what WebSphere does)
+     */
+    public final class RenamingStandardMBean extends StandardMBean implements MBeanRegistration {
+
+
+        public <T> RenamingStandardMBean(T object, Class<T> type) throws NotCompliantMBeanException {
+            super(object, type);
+        }
+
+
+        public ObjectName preRegister(MBeanServer mBeanServer, ObjectName objectName) throws Exception {
+            return new ObjectName(objectName.toString() + ",node=node1,cell=cell1,process=process1");
+        }
+
+        public void postRegister(Boolean aBoolean) {
+            // graciously do nothing here
+        }
+
+        public void preDeregister() throws Exception {
+            // graciously do nothing here
+        }
+
+        public void postDeregister() {
+            // graciously do nothing here
+        }
+    }
+}