Repository: deltaspike Updated Branches: refs/heads/master 135c59f05 -> a82f6ad27
merging with master Project: http://git-wip-us.apache.org/repos/asf/deltaspike/repo Commit: http://git-wip-us.apache.org/repos/asf/deltaspike/commit/a82f6ad2 Tree: http://git-wip-us.apache.org/repos/asf/deltaspike/tree/a82f6ad2 Diff: http://git-wip-us.apache.org/repos/asf/deltaspike/diff/a82f6ad2 Branch: refs/heads/master Commit: a82f6ad27486756cc6d80d3a2db1695b779e36be Parents: 135c59f Author: rmannibucau <rmannibu...@apache.org> Authored: Tue Apr 25 22:47:28 2017 +0200 Committer: rmannibucau <rmannibu...@apache.org> Committed: Tue Apr 25 22:47:28 2017 +0200 ---------------------------------------------------------------------- .../deltaspike/core/api/jmx/JmxManaged.java | 6 +- .../apache/deltaspike/core/api/jmx/Table.java | 89 ++++++++++++++++++++ .../core/impl/jmx/AttributeAccessor.java | 9 +- .../core/impl/jmx/DynamicMBeanWrapper.java | 49 +++++++---- .../deltaspike/core/impl/jmx/Operation.java | 46 ++++++++++ .../deltaspike/test/core/impl/jmx/MyMBean.java | 9 ++ .../core/impl/jmx/SimpleRegistrationTest.java | 42 +++++++-- 7 files changed, 222 insertions(+), 28 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/deltaspike/blob/a82f6ad2/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/jmx/JmxManaged.java ---------------------------------------------------------------------- diff --git a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/jmx/JmxManaged.java b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/jmx/JmxManaged.java index 441df8c..5237e00 100644 --- a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/jmx/JmxManaged.java +++ b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/jmx/JmxManaged.java @@ -47,8 +47,8 @@ public @interface JmxManaged String description() default ""; /** - * @return if {@code true} a Map will be converted to a TabularData with a CompositeData entry, - * if {@code false} the map will be returned directly. + * @return if {@code true} a Map or Table will be converted to a TabularData with a CompositeData entry, + * if {@code false} the Map or Table will be returned directly. */ - boolean convertMapToTabularData() default true; + boolean convertToTabularData() default true; } http://git-wip-us.apache.org/repos/asf/deltaspike/blob/a82f6ad2/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/jmx/Table.java ---------------------------------------------------------------------- diff --git a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/jmx/Table.java b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/jmx/Table.java new file mode 100644 index 0000000..897e6ad --- /dev/null +++ b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/jmx/Table.java @@ -0,0 +1,89 @@ +/* + * 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.deltaspike.core.api.jmx; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +import static java.util.Arrays.asList; + +/** + * Allows to expose in JMX a STRING TabularData without having to built it in the MBean. + * + * Ensure to register columns before the lines. + * + * You just have to type the operation or attribute with this type to expose it as a TabularData. + */ +public class Table +{ + private Collection<String> columns = new ArrayList<String>(); + private Collection<Collection<String>> values = new ArrayList<Collection<String>>(); + + public Table withColumns(final Collection<String> names) + { + columns.addAll(names); + return this; + } + + public Table withColumns(final String... names) + { + columns.addAll(asList(names)); + return this; + } + + public Table withLines(final Collection<Collection<String>> lines) + { + for (final Collection<String> line : lines) + { + withLine(line); + } + return this; + } + + public Table withLine(final Collection<String> line) + { + if (line.size() != columns.size()) + { + throw new IllegalArgumentException("Please set columns before lines"); + } + values.add(line); + return this; + } + + public Table withLine(final String... line) + { + if (line.length != columns.size()) + { + throw new IllegalArgumentException("Please set columns before lines"); + } + values.add(asList(line)); + return this; + } + + public Collection<String> getColumnNames() + { + return Collections.unmodifiableCollection(columns); + } + + public Collection<Collection<String>> getLines() + { + return Collections.unmodifiableCollection(values); + } +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/a82f6ad2/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/jmx/AttributeAccessor.java ---------------------------------------------------------------------- diff --git a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/jmx/AttributeAccessor.java b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/jmx/AttributeAccessor.java index 0b4d3ba..79d97d6 100644 --- a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/jmx/AttributeAccessor.java +++ b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/jmx/AttributeAccessor.java @@ -30,11 +30,18 @@ public class AttributeAccessor { private final Method getter; private final Method setter; + private final boolean presentAsTabularIfPossible; - public AttributeAccessor(final Method get, final Method set) + public AttributeAccessor(final Method get, final Method set, final boolean presentAsTabularIfPossible) { this.setter = set; this.getter = get; + this.presentAsTabularIfPossible = presentAsTabularIfPossible; + } + + public boolean isPresentAsTabularIfPossible() + { + return presentAsTabularIfPossible; } public Object get(final Object instance) throws InvocationTargetException, IllegalAccessException http://git-wip-us.apache.org/repos/asf/deltaspike/blob/a82f6ad2/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/jmx/DynamicMBeanWrapper.java ---------------------------------------------------------------------- diff --git a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/jmx/DynamicMBeanWrapper.java b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/jmx/DynamicMBeanWrapper.java index 3146a21..c37585c 100644 --- a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/jmx/DynamicMBeanWrapper.java +++ b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/jmx/DynamicMBeanWrapper.java @@ -24,6 +24,7 @@ import org.apache.deltaspike.core.api.jmx.JmxManaged; import org.apache.deltaspike.core.api.jmx.JmxParameter; import org.apache.deltaspike.core.api.jmx.MBean; import org.apache.deltaspike.core.api.jmx.NotificationInfo; +import org.apache.deltaspike.core.api.jmx.Table; import org.apache.deltaspike.core.api.provider.BeanManagerProvider; import org.apache.deltaspike.core.api.provider.BeanProvider; import org.apache.deltaspike.core.util.ParameterUtil; @@ -61,6 +62,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -78,7 +80,7 @@ public class DynamicMBeanWrapper extends NotificationBroadcasterSupport implemen private final MBeanInfo info; private final Map<String, AttributeAccessor> fields = new HashMap<String, AttributeAccessor>(); - private final Map<String, Method> operations = new HashMap<String, Method>(); + private final Map<String, Operation> operations = new HashMap<String, Operation>(); private final ClassLoader classloader; private final Class<?> clazz; private final boolean normalScope; @@ -141,7 +143,7 @@ public class DynamicMBeanWrapper extends NotificationBroadcasterSupport implemen continue; } - operations.put(method.getName(), method); + operations.put(method.getName(), new Operation(method, annotation.convertToTabularData())); String operationDescr = getDescription(annotation.description(), method.getName()); @@ -240,7 +242,7 @@ public class DynamicMBeanWrapper extends NotificationBroadcasterSupport implemen fieldName, toMBeanType(type).getName(), fieldDescription, getter != null, setter != null, false)); - fields.put(fieldName, new AttributeAccessor(getter, setter)); + fields.put(fieldName, new AttributeAccessor(getter, setter, annotation.convertToTabularData())); } } clazz = clazz.getSuperclass(); @@ -256,7 +258,7 @@ public class DynamicMBeanWrapper extends NotificationBroadcasterSupport implemen private Class<?> toMBeanType(final Class<?> type) { - if (Map.class == type) + if (Map.class.isAssignableFrom(type) || Table.class.isAssignableFrom(type)) { return TabularData.class; } @@ -305,12 +307,9 @@ public class DynamicMBeanWrapper extends NotificationBroadcasterSupport implemen Thread.currentThread().setContextClassLoader(classloader); try { - final Object o = fields.get(attribute).get(instance()); - if (Map.class.isInstance(o)) - { - return toTabularData(attribute, attribute, Map.class.cast(o)); - } - return o; + final AttributeAccessor attributeAccessor = fields.get(attribute); + final Object value = attributeAccessor.get(instance()); + return attributeAccessor.isPresentAsTabularIfPossible() ? toResult(attribute, value) : value; } catch (IllegalArgumentException e) { @@ -332,9 +331,24 @@ public class DynamicMBeanWrapper extends NotificationBroadcasterSupport implemen throw new AttributeNotFoundException(); } - private TabularData toTabularData(final String typeName, final String description, final Map map) + private Object toResult(final String attribute, final Object value) + throws InvocationTargetException, IllegalAccessException { - final OpenType<?>[] types = new OpenType<?>[map.size()]; + if (Map.class.isInstance(value)) + { + Map map = Map.class.cast(value); + return toTabularData(attribute, attribute, new Table().withColumns(map.keySet()).withLine(map.values())); + } + if (Table.class.isInstance(value)) + { + return toTabularData(attribute, attribute, Table.class.cast(value)); + } + return value; + } + + private TabularData toTabularData(final String typeName, final String description, final Table table) + { + final OpenType<?>[] types = new OpenType<?>[table.getColumnNames().size()]; for (int i = 0; i < types.length; i++) { types[i] = SimpleType.STRING; @@ -342,12 +356,15 @@ public class DynamicMBeanWrapper extends NotificationBroadcasterSupport implemen try { - final String[] keys = String[].class.cast(map.keySet().toArray(new String[map.size()])); + final String[] keys = table.getColumnNames().toArray(new String[table.getColumnNames().size()]); final CompositeType ct = new CompositeType( typeName, description, keys, keys, types); final TabularType type = new TabularType(typeName, description, ct, keys); final TabularDataSupport data = new TabularDataSupport(type); - data.put(new CompositeDataSupport(ct, map)); + for (final Collection<String> line : table.getLines()) + { + data.put(new CompositeDataSupport(ct, keys, line.toArray(new Object[line.size()]))); + } return data; } catch (final OpenDataException e) @@ -439,7 +456,9 @@ public class DynamicMBeanWrapper extends NotificationBroadcasterSupport implemen Thread.currentThread().setContextClassLoader(classloader); try { - return operations.get(actionName).invoke(instance(), params); + final Operation operation = operations.get(actionName); + final Object result = operation.getOperation().invoke(instance(), params); + return operation.isPresentAsTabularIfPossible() ? toResult(actionName, result) : result; } catch (InvocationTargetException e) { http://git-wip-us.apache.org/repos/asf/deltaspike/blob/a82f6ad2/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/jmx/Operation.java ---------------------------------------------------------------------- diff --git a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/jmx/Operation.java b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/jmx/Operation.java new file mode 100644 index 0000000..40920bc --- /dev/null +++ b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/jmx/Operation.java @@ -0,0 +1,46 @@ +/* + * 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.deltaspike.core.impl.jmx; + +import java.lang.reflect.Method; + +/** + * Just a helper class mapping a JMX operation. + */ +class Operation +{ + private final Method operation; + private final boolean presentAsTabularIfPossible; + + public Operation(final Method operation, final boolean presentAsTabularIfPossible) + { + this.operation = operation; + this.presentAsTabularIfPossible = presentAsTabularIfPossible; + } + + public boolean isPresentAsTabularIfPossible() + { + return presentAsTabularIfPossible; + } + + public Method getOperation() + { + return operation; + } +} http://git-wip-us.apache.org/repos/asf/deltaspike/blob/a82f6ad2/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/impl/jmx/MyMBean.java ---------------------------------------------------------------------- diff --git a/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/impl/jmx/MyMBean.java b/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/impl/jmx/MyMBean.java index 16e8cbd..3cf6370 100644 --- a/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/impl/jmx/MyMBean.java +++ b/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/impl/jmx/MyMBean.java @@ -22,6 +22,7 @@ import org.apache.deltaspike.core.api.jmx.JmxBroadcaster; import org.apache.deltaspike.core.api.jmx.JmxManaged; import org.apache.deltaspike.core.api.jmx.JmxParameter; import org.apache.deltaspike.core.api.jmx.MBean; +import org.apache.deltaspike.core.api.jmx.Table; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; @@ -42,6 +43,9 @@ public class MyMBean @JmxManaged private Map<String, String> table; + @JmxManaged // just a marker to expose it as an attribute, will call the getter + private Table table2; + public Map<String, String> getTable() { return table != null ? table : (table = new HashMap<String, String>() {{ put("key1", "value1"); @@ -49,6 +53,11 @@ public class MyMBean }}); } + @JmxManaged + public Table getTable2() { + return new Table().withColumns("a", "b", "c").withLine("1", "2", "3").withLine("alpha", "beta", "gamma"); + } + public int getCounter() { return counter; http://git-wip-us.apache.org/repos/asf/deltaspike/blob/a82f6ad2/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/impl/jmx/SimpleRegistrationTest.java ---------------------------------------------------------------------- diff --git a/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/impl/jmx/SimpleRegistrationTest.java b/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/impl/jmx/SimpleRegistrationTest.java index 5409fac..512d24c 100644 --- a/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/impl/jmx/SimpleRegistrationTest.java +++ b/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/impl/jmx/SimpleRegistrationTest.java @@ -32,6 +32,7 @@ import javax.management.openmbean.TabularData; import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -75,14 +76,37 @@ public abstract class SimpleRegistrationTest { assertEquals("multiplier", parameterInfo.getName()); assertEquals("the multiplier", parameterInfo.getDescription()); - // table support - Object table = server.getAttribute(on, "table"); - assertTrue(TabularData.class.isInstance(table)); - final TabularData data = TabularData.class.cast(table); - assertEquals(1, data.size()); - final CompositeData compositeData = CompositeData.class.cast(data.values().iterator().next()); - assertEquals(2, compositeData.values().size()); - assertEquals("value1", compositeData.get("key1")); - assertEquals("value2", compositeData.get("key2")); + { // table support - through map + Object table = server.getAttribute(on, "table"); + assertTrue(TabularData.class.isInstance(table)); + final TabularData data = TabularData.class.cast(table); + assertEquals(1, data.size()); + final CompositeData compositeData = CompositeData.class.cast(data.values().iterator().next()); + assertEquals(2, compositeData.values().size()); + assertEquals("value1", compositeData.get("key1")); + assertEquals("value2", compositeData.get("key2")); + } + + { // table support - through Table + Object table = server.getAttribute(on, "table2"); + assertTrue(TabularData.class.isInstance(table)); + final TabularData data = TabularData.class.cast(table); + assertEquals(2, data.size()); + final Iterator<?> iterator = data.values().iterator(); + { + final CompositeData compositeData = CompositeData.class.cast(iterator.next()); + assertEquals(3, compositeData.values().size()); + assertEquals("1", compositeData.get("a")); + assertEquals("2", compositeData.get("b")); + assertEquals("3", compositeData.get("c")); + } + { + final CompositeData compositeData = CompositeData.class.cast(iterator.next()); + assertEquals(3, compositeData.values().size()); + assertEquals("alpha", compositeData.get("a")); + assertEquals("beta", compositeData.get("b")); + assertEquals("gamma", compositeData.get("c")); + } + } } }