You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by jg...@apache.org on 2015/04/17 14:23:17 UTC

[1/2] tomee git commit: TOMEE-1547 adding test to ensure additional properties are not leaked when creating resources using class-name

Repository: tomee
Updated Branches:
  refs/heads/tomee-1.7.x 59437efcb -> 4c0e766d9


TOMEE-1547 adding test to ensure additional properties are not leaked when creating resources using class-name


Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/c3d247a6
Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/c3d247a6
Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/c3d247a6

Branch: refs/heads/tomee-1.7.x
Commit: c3d247a671539e2cec081e840f09526a99ba6840
Parents: 59437ef
Author: Jonathan Gallimore <jo...@jrg.me.uk>
Authored: Fri Apr 17 11:05:17 2015 +0100
Committer: Jonathan Gallimore <jo...@jrg.me.uk>
Committed: Fri Apr 17 11:05:17 2015 +0100

----------------------------------------------------------------------
 .../classic/ResourcePropertyLeakTest.java       | 112 +++++++++++++++++++
 1 file changed, 112 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tomee/blob/c3d247a6/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/ResourcePropertyLeakTest.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/ResourcePropertyLeakTest.java b/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/ResourcePropertyLeakTest.java
new file mode 100644
index 0000000..e576e6c
--- /dev/null
+++ b/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/ResourcePropertyLeakTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.openejb.assembler.classic;
+
+import org.apache.geronimo.transaction.manager.GeronimoTransactionManager;
+import org.apache.openejb.config.AppModule;
+import org.apache.openejb.config.EjbModule;
+import org.apache.openejb.jee.EjbJar;
+import org.apache.openejb.junit.ApplicationComposer;
+import org.apache.openejb.loader.SystemInstance;
+import org.apache.openejb.spi.ContainerSystem;
+import org.apache.openejb.testing.Configuration;
+import org.apache.openejb.testing.Module;
+import org.apache.openejb.testng.PropertiesBuilder;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.naming.NamingException;
+import java.util.Properties;
+
+
+/**
+ * This test ensures that additional properties are not leaked into the properties map when creating a resource
+ * using class-name, without a provider or type. When using "SkipImpliedAttributes" only the properties the
+ * user specified in their configuration should be available in the map.
+ *
+ * With "SkipImpliedAttributes" set to false (the default), ServiceId (the id of the resource itself) and transactionManager
+ * (the Geronimo Transaction Manager) are the only additional properties that should be available for resources to consume.
+ */
+@RunWith(ApplicationComposer.class)
+public class ResourcePropertyLeakTest {
+    @Test
+    public void testResourceProperties() throws NamingException {
+
+        final MyResource r1 = (MyResource) SystemInstance.get()
+                .getComponent(ContainerSystem.class).getJNDIContext().lookup("openejb/Resource/r1");
+
+        Assert.assertEquals(4, r1.properties.size());
+        Assert.assertTrue(r1.properties.containsKey("prop1"));
+        Assert.assertTrue(r1.properties.containsKey("prop2"));
+        Assert.assertTrue(r1.properties.containsKey("ServiceId"));
+        Assert.assertTrue(r1.properties.containsKey("transactionManager"));
+
+        Assert.assertEquals("value1", r1.properties.get("prop1"));
+        Assert.assertEquals("value2", r1.properties.get("prop2"));
+        Assert.assertEquals("r1", r1.properties.get("ServiceId"));
+        Assert.assertTrue(GeronimoTransactionManager.class.isInstance(r1.properties.get("transactionManager")));
+
+        // Resource 2 should not contain implied attributes
+        final MyResource r2 = (MyResource) SystemInstance.get()
+                .getComponent(ContainerSystem.class).getJNDIContext().lookup("openejb/Resource/r2");
+
+        Assert.assertEquals(2, r2.properties.size());
+        Assert.assertTrue(r2.properties.containsKey("prop1"));
+        Assert.assertTrue(r2.properties.containsKey("prop2"));
+        Assert.assertEquals("value1", r1.properties.get("prop1"));
+        Assert.assertEquals("value2", r1.properties.get("prop2"));
+
+    }
+
+    @Module
+    public AppModule application() {
+        final EjbModule ejbModule = new EjbModule(new EjbJar());
+
+        final AppModule appModule = new AppModule(Thread.currentThread().getContextClassLoader(), null);
+        appModule.getEjbModules().add(ejbModule);
+
+        return appModule;
+    }
+
+    @Configuration
+    public Properties config() {
+        return new PropertiesBuilder()
+            .p("r1", "new://Resource?class-name=org.apache.openejb.assembler.classic.ResourcePropertyLeakTest$MyResource")
+            .p("r1.prop1", "value1")
+            .p("r1.prop2", "value2")
+            .p("r2", "new://Resource?class-name=org.apache.openejb.assembler.classic.ResourcePropertyLeakTest$MyResource")
+            .p("r2.SkipImplicitAttributes", "true")
+            .p("r2.prop1", "value1")
+            .p("r2.prop2", "value2")
+            .build();
+    }
+
+    public static class MyResource {
+        private Properties properties;
+
+        public Properties getProperties() {
+            return properties;
+        }
+
+        public void setProperties(final Properties properties) {
+            this.properties = properties;
+        }
+    }
+}


[2/2] tomee git commit: Iterate over properties and set values on MBean as opposed to looping through fields using reflection. Demonstrate SkipImplicitAttributes and using a prefix.

Posted by jg...@apache.org.
Iterate over properties and set values on MBean as opposed to looping through fields using reflection. Demonstrate SkipImplicitAttributes and using a prefix.


Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/4c0e766d
Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/4c0e766d
Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/4c0e766d

Branch: refs/heads/tomee-1.7.x
Commit: 4c0e766d9c0114e3d9b85d86ca273e03637d3536
Parents: c3d247a
Author: Jonathan Gallimore <jo...@jrg.me.uk>
Authored: Fri Apr 17 13:20:31 2015 +0100
Committer: Jonathan Gallimore <jo...@jrg.me.uk>
Committed: Fri Apr 17 13:20:31 2015 +0100

----------------------------------------------------------------------
 .../resource/jmx/factory/JMXBeanCreator.java    | 114 ++++++++++++-------
 .../resource/jmx/resources/Alternative.java     | 110 +++++++++++++-----
 .../src/main/resources/META-INF/resources.xml   |  19 +++-
 .../java/org/superbiz/resource/jmx/JMXTest.java |  19 +++-
 4 files changed, 186 insertions(+), 76 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tomee/blob/4c0e766d/examples/resources-jmx-example/resources-jmx-ejb/src/main/java/org/superbiz/resource/jmx/factory/JMXBeanCreator.java
----------------------------------------------------------------------
diff --git a/examples/resources-jmx-example/resources-jmx-ejb/src/main/java/org/superbiz/resource/jmx/factory/JMXBeanCreator.java b/examples/resources-jmx-example/resources-jmx-ejb/src/main/java/org/superbiz/resource/jmx/factory/JMXBeanCreator.java
index 311879c..14a21a6 100644
--- a/examples/resources-jmx-example/resources-jmx-ejb/src/main/java/org/superbiz/resource/jmx/factory/JMXBeanCreator.java
+++ b/examples/resources-jmx-example/resources-jmx-ejb/src/main/java/org/superbiz/resource/jmx/factory/JMXBeanCreator.java
@@ -19,48 +19,69 @@
 
 package org.superbiz.resource.jmx.factory;
 
-import javax.management.InstanceAlreadyExistsException;
+import javax.management.Attribute;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanInfo;
 import javax.management.MBeanServer;
-import javax.management.MalformedObjectNameException;
-import javax.management.NotCompliantMBeanException;
 import javax.management.ObjectName;
+import javax.management.StandardMBean;
 import java.lang.management.ManagementFactory;
-import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Properties;
 import java.util.logging.Logger;
 
 public class JMXBeanCreator {
 
+    private final Map<String, Class<?>> primitives = new HashMap<String, Class<?>>() {
+        {
+            put("boolean", Boolean.TYPE);
+            put("byte", Byte.TYPE);
+            put("char", Character.TYPE);
+            put("long", Long.TYPE);
+            put("float", Float.TYPE);
+            put("int", Integer.TYPE);
+            put("double", Double.TYPE);
+            put("short", Short.TYPE);
+        }
+    };
+
     private static Logger LOGGER = Logger.getLogger(JMXBeanCreator.class.getName());
     private Properties properties;
 
-    public Object create() throws MBeanRegistrationException {
-        // instantiate the bean
-
-        final String code = properties.getProperty("code");
-        final String name = properties.getProperty("name");
+    public <T> Object create() throws MBeanRegistrationException {
+        final String code = (String) properties.remove("code");
+        final String name = (String) properties.remove("name");
+        final String iface = (String) properties.remove("interface");
+        final String prefix = (String) properties.remove("prefix");
 
         requireNotNull(code);
         requireNotNull(name);
+        requireNotNull(iface);
 
         try {
-            final Class<?> cls = Class.forName(code, true, Thread.currentThread().getContextClassLoader());
-            final Object instance = cls.newInstance();
-
-            final Field[] fields = cls.getDeclaredFields();
-            for (final Field field : fields) {
-
-                final String property = properties.getProperty(field.getName());
-                if (property == null) {
-                    continue;
+            final Class<? extends T> cls = (Class<? extends T>) Class.forName(code, true, Thread.currentThread().getContextClassLoader());
+            final Class<T> ifaceCls = (Class<T>) Class.forName(iface, true, Thread.currentThread().getContextClassLoader());
+            final T instance = (T) cls.newInstance();
+            final StandardMBean mBean = new StandardMBean(instance, ifaceCls);
+
+            for (Object property : properties.keySet()) {
+                String attributeName = (String) property;
+                final Object value = properties.getProperty(attributeName);
+
+                if (prefix != null) {
+                    if (! attributeName.startsWith(prefix + ".")) {
+                        continue;
+                    } else {
+                        attributeName = attributeName.substring(prefix.length() + 1);
+                    }
                 }
 
-                try {
-                    field.setAccessible(true);
-                    field.set(instance, Converter.convert(property, field.getType(), field.getName()));
-                } catch (Exception e) {
-                    LOGGER.info(String.format("Unable to set value %s on field %s", property, field.getName()));
-                }
+                final Class<?> targetType = findAttributeType(mBean.getMBeanInfo(), attributeName);
+                final Object targetValue = Converter.convert(value, targetType, null);
+
+                final Attribute attribute = new Attribute(attributeName, targetValue);
+                mBean.setAttribute(attribute);
             }
 
             final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
@@ -69,30 +90,35 @@ public class JMXBeanCreator {
 
             return instance;
 
-        } catch (final ClassNotFoundException e) {
-            LOGGER.severe("Unable to find class " + code);
-            throw new MBeanRegistrationException(e);
-        } catch (final InstantiationException e) {
-            LOGGER.severe("Unable to create instance of class " + code);
-            throw new MBeanRegistrationException(e);
-        } catch (final IllegalAccessException e) {
-            LOGGER.severe("Illegal access: " + code);
-            throw new MBeanRegistrationException(e);
-        } catch (final MalformedObjectNameException e) {
-            LOGGER.severe("Malformed MBean name: " + name);
-            throw new MBeanRegistrationException(e);
-        } catch (final InstanceAlreadyExistsException e) {
-            LOGGER.severe("Instance already exists: " + name);
-            throw new MBeanRegistrationException(e);
-        } catch (final NotCompliantMBeanException e) {
-            LOGGER.severe("Class is not a valid MBean: " + code);
-            throw new MBeanRegistrationException(e);
-        } catch (final javax.management.MBeanRegistrationException e) {
-            LOGGER.severe("Error registering " + name + ", " + code);
+        } catch (final Exception e) {
+            e.printStackTrace();
+            LOGGER.severe("Unable to register mbean " + e.getMessage());
             throw new MBeanRegistrationException(e);
         }
     }
 
+    private Class<?> findAttributeType(MBeanInfo mBeanInfo, String attributeName) {
+        try {
+            for (final MBeanAttributeInfo attribute : mBeanInfo.getAttributes()) {
+                if (attribute.getName().equals(attributeName)) {
+                    return convertPrimitive(attribute.getType());
+                }
+            }
+
+            return null;
+        } catch (final ClassNotFoundException e) {
+            return null;
+        }
+    }
+
+    private Class<?> convertPrimitive(final String type) throws ClassNotFoundException {
+        if (primitives.containsKey(type)) {
+            return primitives.get(type);
+        }
+
+        return Class.forName(type, true, Thread.currentThread().getContextClassLoader());
+    }
+
     private void requireNotNull(final String object) throws MBeanRegistrationException {
         if (object == null) {
             throw new MBeanRegistrationException("code property not specified, stopping");

http://git-wip-us.apache.org/repos/asf/tomee/blob/4c0e766d/examples/resources-jmx-example/resources-jmx-ejb/src/main/java/org/superbiz/resource/jmx/resources/Alternative.java
----------------------------------------------------------------------
diff --git a/examples/resources-jmx-example/resources-jmx-ejb/src/main/java/org/superbiz/resource/jmx/resources/Alternative.java b/examples/resources-jmx-example/resources-jmx-ejb/src/main/java/org/superbiz/resource/jmx/resources/Alternative.java
index 546c53a..20799da 100644
--- a/examples/resources-jmx-example/resources-jmx-ejb/src/main/java/org/superbiz/resource/jmx/resources/Alternative.java
+++ b/examples/resources-jmx-example/resources-jmx-ejb/src/main/java/org/superbiz/resource/jmx/resources/Alternative.java
@@ -19,75 +19,129 @@
 
 package org.superbiz.resource.jmx.resources;
 
+import org.superbiz.resource.jmx.factory.Converter;
 import org.superbiz.resource.jmx.factory.MBeanRegistrationException;
 
 import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
-import javax.management.InstanceAlreadyExistsException;
+import javax.management.Attribute;
 import javax.management.InstanceNotFoundException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanInfo;
 import javax.management.MBeanServer;
 import javax.management.MalformedObjectNameException;
-import javax.management.NotCompliantMBeanException;
 import javax.management.ObjectName;
+import javax.management.StandardMBean;
 import java.lang.management.ManagementFactory;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Properties;
 import java.util.logging.Logger;
 
 public class Alternative implements AlternativeMBean {
 
+    private final Map<String, Class<?>> primitives = new HashMap<String, Class<?>>() {
+        {
+            put("boolean", Boolean.TYPE);
+            put("byte", Byte.TYPE);
+            put("char", Character.TYPE);
+            put("long", Long.TYPE);
+            put("float", Float.TYPE);
+            put("int", Integer.TYPE);
+            put("double", Double.TYPE);
+            put("short", Short.TYPE);
+        }
+    };
+
     private static Logger LOGGER = Logger.getLogger(Alternative.class.getName());
     private Properties properties;
 
-    @PostConstruct
-    public void postConstruct() throws MBeanRegistrationException {
-        // initialize the bean
-
-        final String code = properties.getProperty("code");
+    @PreDestroy
+    public void preDestroy() throws MBeanRegistrationException {
         final String name = properties.getProperty("name");
-
-        requireNotNull(code);
         requireNotNull(name);
 
         try {
             final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
             final ObjectName objectName = new ObjectName(name);
-            mbs.registerMBean(this, objectName);
+            mbs.unregisterMBean(objectName);
         } catch (final MalformedObjectNameException e) {
             LOGGER.severe("Malformed MBean name: " + name);
             throw new MBeanRegistrationException(e);
-        } catch (final InstanceAlreadyExistsException e) {
-            LOGGER.severe("Instance already exists: " + name);
-            throw new MBeanRegistrationException(e);
-        } catch (final NotCompliantMBeanException e) {
-            LOGGER.severe("Class is not a valid MBean: " + code);
-            throw new MBeanRegistrationException(e);
         } catch (final javax.management.MBeanRegistrationException e) {
-            LOGGER.severe("Error registering " + name + ", " + code);
+            LOGGER.severe("Error unregistering " + name);
+            throw new MBeanRegistrationException(e);
+        } catch (InstanceNotFoundException e) {
+            LOGGER.severe("Error unregistering " + name);
             throw new MBeanRegistrationException(e);
         }
     }
 
-    @PreDestroy
-    public void preDestroy() throws MBeanRegistrationException {
+    @PostConstruct
+    public <T> void postConstruct() throws MBeanRegistrationException {
+
         final String name = properties.getProperty("name");
+        final String iface = properties.getProperty("interface");
+        final String prefix = properties.getProperty("prefix");
+
         requireNotNull(name);
+        requireNotNull(iface);
 
         try {
+            final Class<T> ifaceCls = (Class<T>) Class.forName(iface, true, Thread.currentThread().getContextClassLoader());
+            final StandardMBean mBean = new StandardMBean((T) this, ifaceCls);
+
+            for (Object property : properties.keySet()) {
+                String attributeName = (String) property;
+                final Object value = properties.getProperty(attributeName);
+
+                if (prefix != null) {
+                    if (! attributeName.startsWith(prefix + ".")) {
+                        continue;
+                    } else {
+                        attributeName = attributeName.substring(prefix.length() + 1);
+                    }
+                }
+
+                final Class<?> targetType = findAttributeType(mBean.getMBeanInfo(), attributeName);
+                final Object targetValue = Converter.convert(value, targetType, null);
+
+                final Attribute attribute = new Attribute(attributeName, targetValue);
+                mBean.setAttribute(attribute);
+            }
+
             final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
             final ObjectName objectName = new ObjectName(name);
-            mbs.unregisterMBean(objectName);
-        } catch (final MalformedObjectNameException e) {
-            LOGGER.severe("Malformed MBean name: " + name);
-            throw new MBeanRegistrationException(e);
-        } catch (final javax.management.MBeanRegistrationException e) {
-            LOGGER.severe("Error unregistering " + name);
-            throw new MBeanRegistrationException(e);
-        } catch (InstanceNotFoundException e) {
-            LOGGER.severe("Error unregistering " + name);
+            mbs.registerMBean(this, objectName);
+
+        } catch (final Exception e) {
+            LOGGER.severe("Unable to register mbean " + e.getMessage());
             throw new MBeanRegistrationException(e);
         }
     }
 
+    private Class<?> findAttributeType(MBeanInfo mBeanInfo, String attributeName) {
+        try {
+            for (final MBeanAttributeInfo attribute : mBeanInfo.getAttributes()) {
+                if (attribute.getName().equals(attributeName)) {
+                    return convertPrimitive(attribute.getType());
+                }
+            }
+
+            return null;
+        } catch (final ClassNotFoundException e) {
+            return null;
+        }
+    }
+
+    private Class<?> convertPrimitive(final String type) throws ClassNotFoundException {
+        if (primitives.containsKey(type)) {
+            return primitives.get(type);
+        }
+
+        return Class.forName(type, true, Thread.currentThread().getContextClassLoader());
+    }
+
     private void requireNotNull(final String object) throws MBeanRegistrationException {
         if (object == null) {
             throw new MBeanRegistrationException("code property not specified, stopping");

http://git-wip-us.apache.org/repos/asf/tomee/blob/4c0e766d/examples/resources-jmx-example/resources-jmx-ejb/src/main/resources/META-INF/resources.xml
----------------------------------------------------------------------
diff --git a/examples/resources-jmx-example/resources-jmx-ejb/src/main/resources/META-INF/resources.xml b/examples/resources-jmx-example/resources-jmx-ejb/src/main/resources/META-INF/resources.xml
index 03bd9d0..54eda50 100644
--- a/examples/resources-jmx-example/resources-jmx-ejb/src/main/resources/META-INF/resources.xml
+++ b/examples/resources-jmx-example/resources-jmx-ejb/src/main/resources/META-INF/resources.xml
@@ -21,14 +21,27 @@
 <Resources>
   <Resource id="Hello" class-name="org.superbiz.resource.jmx.factory.JMXBeanCreator" factory-name="create">
     code org.superbiz.resource.jmx.resources.Hello
+    interface org.superbiz.resource.jmx.resources.HelloMBean
     name superbiz.test:name=Hello
-    count 20
+    prefix mbean
+    mbean.Count 20
   </Resource>
 
+  <!-- no prefix, but implied attributes are skipped, so additional properties should not appear in the map -->
+  <Resource id="Hello2" class-name="org.superbiz.resource.jmx.factory.JMXBeanCreator" factory-name="create">
+    code org.superbiz.resource.jmx.resources.Hello
+    interface org.superbiz.resource.jmx.resources.HelloMBean
+    name superbiz.test:name=Hello2
+    SkipImplicitAttributes true
+    Count 20
+  </Resource>
+
+
   <Resource id="Alternative" class-name="org.superbiz.resource.jmx.resources.Alternative">
-    code org.superbiz.resource.jmx.resources.Alternative
+    interface org.superbiz.resource.jmx.resources.AlternativeMBean
     name superbiz.test:name=Alternative
-    count 20
+    prefix mbean
+    mbean.Count 20
   </Resource>
 
 </Resources>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tomee/blob/4c0e766d/examples/resources-jmx-example/resources-jmx-ejb/src/test/java/org/superbiz/resource/jmx/JMXTest.java
----------------------------------------------------------------------
diff --git a/examples/resources-jmx-example/resources-jmx-ejb/src/test/java/org/superbiz/resource/jmx/JMXTest.java b/examples/resources-jmx-example/resources-jmx-ejb/src/test/java/org/superbiz/resource/jmx/JMXTest.java
index 42fc51f..9476cda 100644
--- a/examples/resources-jmx-example/resources-jmx-ejb/src/test/java/org/superbiz/resource/jmx/JMXTest.java
+++ b/examples/resources-jmx-example/resources-jmx-ejb/src/test/java/org/superbiz/resource/jmx/JMXTest.java
@@ -66,7 +66,7 @@ public class JMXTest {
     }
 
     @Test
-    public void test() throws Exception {
+    public void testFactory() throws Exception {
         final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
         final ObjectName objectName = new ObjectName("superbiz.test:name=Hello");
 
@@ -97,6 +97,23 @@ public class JMXTest {
     }
 
     @Test
+    public void testFactorySkipImpliedAttributes() throws Exception {
+        final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+        final ObjectName objectName = new ObjectName("superbiz.test:name=Hello2");
+
+        Assert.assertEquals(20, mbs.getAttribute(objectName, "Count"));
+
+        mbs.invoke(objectName, "increment", new Object[0], new String[0]);
+        Assert.assertEquals(21, mbs.getAttribute(objectName, "Count"));
+
+        Attribute attribute = new Attribute("Count", 12345);
+        mbs.setAttribute(objectName, attribute);
+        Assert.assertEquals(12345, mbs.getAttribute(objectName, "Count"));
+
+        Assert.assertEquals("Hello, world", mbs.invoke(objectName, "greet", new Object[] { "world" }, new String[] { String.class.getName() }));
+    }
+
+    @Test
     public void testPostConstruct() throws Exception {
         final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
         final ObjectName objectName = new ObjectName("superbiz.test:name=Alternative");