Author: davidb
Date: Thu Dec 14 11:02:25 2017
New Revision: 1818103
URL: http://svn.apache.org/viewvc?rev=1818103&view=rev
Log:
ARIES-1766 Configure log severity for Whiteboard JMX registration exceptions
The severity can be configured by specifying one or more classes with the
warning.exceptions
service property of the whiteboard service. Any exceptions listed there are
logged
as warnings, other ones are logged as errors.
Added:
aries/trunk/jmx/jmx-whiteboard/src/test/java/org/apache/aries/jmx/whiteboard/JmxWhiteboardSupportTest.java
aries/trunk/jmx/jmx-whiteboard/src/test/java/org/apache/aries/jmx/whiteboard/MBeanHolderTest.java
Modified:
aries/trunk/jmx/jmx-whiteboard/pom.xml
aries/trunk/jmx/jmx-whiteboard/src/main/java/org/apache/aries/jmx/whiteboard/JmxWhiteboardSupport.java
aries/trunk/jmx/jmx-whiteboard/src/main/java/org/apache/aries/jmx/whiteboard/MBeanHolder.java
Modified: aries/trunk/jmx/jmx-whiteboard/pom.xml
URL:
http://svn.apache.org/viewvc/aries/trunk/jmx/jmx-whiteboard/pom.xml?rev=1818103&r1=1818102&r2=1818103&view=diff
==============================================================================
--- aries/trunk/jmx/jmx-whiteboard/pom.xml (original)
+++ aries/trunk/jmx/jmx-whiteboard/pom.xml Thu Dec 14 11:02:25 2017
@@ -142,12 +142,19 @@
<scope>provided</scope>
</dependency>
- <!-- Integration Testing with Pax Exam -->
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ <version>1.10.19</version>
+ </dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
+
+ <!-- Integration Testing with Pax Exam -->
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam</artifactId>
Modified:
aries/trunk/jmx/jmx-whiteboard/src/main/java/org/apache/aries/jmx/whiteboard/JmxWhiteboardSupport.java
URL:
http://svn.apache.org/viewvc/aries/trunk/jmx/jmx-whiteboard/src/main/java/org/apache/aries/jmx/whiteboard/JmxWhiteboardSupport.java?rev=1818103&r1=1818102&r2=1818103&view=diff
==============================================================================
---
aries/trunk/jmx/jmx-whiteboard/src/main/java/org/apache/aries/jmx/whiteboard/JmxWhiteboardSupport.java
(original)
+++
aries/trunk/jmx/jmx-whiteboard/src/main/java/org/apache/aries/jmx/whiteboard/JmxWhiteboardSupport.java
Thu Dec 14 11:02:25 2017
@@ -20,6 +20,7 @@ package org.apache.aries.jmx.whiteboard;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.IdentityHashMap;
import javax.management.MBeanRegistration;
@@ -54,7 +55,7 @@ class JmxWhiteboardSupport {
// register all mbeans with the new server
for (MBeanHolder mbean : mbeans.values()) {
- mbean.register(mbeanServer);
+ mbean.register(mbeanServer, null);
}
}
@@ -79,11 +80,12 @@ class JmxWhiteboardSupport {
ObjectName objectName = getObjectName(props);
if (objectName != null || mbean instanceof MBeanRegistration) {
- MBeanHolder holder = MBeanHolder.create(mbean, objectName);
+ MBeanHolder holder = createMBeanHolder(mbean, objectName);
if (holder != null) {
MBeanServer[] mbeanServers = this.mbeanServers;
+ String[] warnExceptions = getStringPlusProperty(props,
"warning.exceptions");
for (MBeanServer mbeanServer : mbeanServers) {
- holder.register(mbeanServer);
+ holder.register(mbeanServer, warnExceptions);
}
mbeans.put(mbean, holder);
} else {
@@ -98,6 +100,10 @@ class JmxWhiteboardSupport {
}
}
+ MBeanHolder createMBeanHolder(Object mbean, ObjectName objectName) {
+ return MBeanHolder.create(mbean, objectName);
+ }
+
protected synchronized void unregisterMBean(Object mbean) {
log.debug("unregisterMBean: Removing MBean {}", mbean);
@@ -127,4 +133,27 @@ class JmxWhiteboardSupport {
return null;
}
+
+ private static String[] getStringPlusProperty(ServiceReference<?> ref,
String propertyName) {
+ final String[] res;
+ Object prop = ref.getProperty(propertyName);
+ if (prop == null) {
+ res = null;
+ } else if (prop instanceof String) {
+ res = new String[] { (String) prop };
+ } else if ( prop instanceof Collection ) {
+ final Object[] col = ((Collection<?>) prop).toArray();
+ res = new String[col.length];
+ for (int i = 0; i < res.length; i++) {
+ res[i] = String.valueOf(col[i]);
+ }
+ } else if (prop.getClass().isArray() &&
String.class.equals(prop.getClass().getComponentType())) {
+ res = (String[]) prop;
+ } else {
+ // unsupported type of property
+ res = null;
+ }
+
+ return res;
+ }
}
Modified:
aries/trunk/jmx/jmx-whiteboard/src/main/java/org/apache/aries/jmx/whiteboard/MBeanHolder.java
URL:
http://svn.apache.org/viewvc/aries/trunk/jmx/jmx-whiteboard/src/main/java/org/apache/aries/jmx/whiteboard/MBeanHolder.java?rev=1818103&r1=1818102&r2=1818103&view=diff
==============================================================================
---
aries/trunk/jmx/jmx-whiteboard/src/main/java/org/apache/aries/jmx/whiteboard/MBeanHolder.java
(original)
+++
aries/trunk/jmx/jmx-whiteboard/src/main/java/org/apache/aries/jmx/whiteboard/MBeanHolder.java
Thu Dec 14 11:02:25 2017
@@ -102,23 +102,36 @@ final class MBeanHolder {
return null;
}
- private MBeanHolder(final Object mbean, final ObjectName
requestedObjectName) {
+ MBeanHolder(final Object mbean, final ObjectName requestedObjectName) {
this.mbean = mbean;
this.requestedObjectName = requestedObjectName;
this.registrations = new IdentityHashMap<MBeanServer, ObjectName>();
}
- void register(final MBeanServer server) {
+ void register(final MBeanServer server, String[] warnExceptions) {
ObjectInstance instance;
try {
instance = server.registerMBean(mbean, requestedObjectName);
registrations.put(server, instance.getObjectName());
- } catch (InstanceAlreadyExistsException e) {
- log.error("register: Failure registering MBean " + mbean, e);
- } catch (MBeanRegistrationException e) {
- log.error("register: Failure registering MBean " + mbean, e);
- } catch (NotCompliantMBeanException e) {
- log.error("register: Failure registering MBean " + mbean, e);
+ } catch (Exception e) {
+ String exClass = e.getClass().getName();
+ if (warnExceptions == null)
+ warnExceptions = new String[] {};
+
+ for (String exCls : warnExceptions) {
+ if (exClass.equals(exCls)) {
+ log.warn("register: problem registering MBean " + mbean,
e);
+ return;
+ }
+ }
+
+ if (e instanceof InstanceAlreadyExistsException ||
+ e instanceof MBeanRegistrationException ||
+ e instanceof NotCompliantMBeanException) {
+ log.error("register: Failure registering MBean " + mbean, e);
+ } else if (e instanceof RuntimeException) {
+ throw ((RuntimeException) e);
+ }
}
}
Added:
aries/trunk/jmx/jmx-whiteboard/src/test/java/org/apache/aries/jmx/whiteboard/JmxWhiteboardSupportTest.java
URL:
http://svn.apache.org/viewvc/aries/trunk/jmx/jmx-whiteboard/src/test/java/org/apache/aries/jmx/whiteboard/JmxWhiteboardSupportTest.java?rev=1818103&view=auto
==============================================================================
---
aries/trunk/jmx/jmx-whiteboard/src/test/java/org/apache/aries/jmx/whiteboard/JmxWhiteboardSupportTest.java
(added)
+++
aries/trunk/jmx/jmx-whiteboard/src/test/java/org/apache/aries/jmx/whiteboard/JmxWhiteboardSupportTest.java
Thu Dec 14 11:02:25 2017
@@ -0,0 +1,172 @@
+/*
+ * 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.aries.jmx.whiteboard;
+
+import java.lang.reflect.Field;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.management.DynamicMBean;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+
+public class JmxWhiteboardSupportTest {
+ @Test
+ public void testRegisterMBean() throws Exception {
+ final Logger ml = Mockito.mock(Logger.class);
+
+ JmxWhiteboardSupport s = new JmxWhiteboardSupport() {
+ @Override
+ MBeanHolder createMBeanHolder(Object mbean, ObjectName objectName)
{
+ MBeanHolder mbh = super.createMBeanHolder(mbean, objectName);
+ setPrivateField(mbh, "log", ml);
+ return mbh;
+ }
+ };
+
+ final Dictionary<String, Object> props = new Hashtable<String,
Object>();
+ props.put("jmx.objectname", "test:key=val");
+
+ ServiceReference<?> sref = Mockito.mock(ServiceReference.class);
+ Mockito.when(sref.getProperty(Mockito.anyString())).then(new
Answer<Object>() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable
{
+ return props.get(invocation.getArguments()[0].toString());
+ }
+ });
+
+ MBeanServer ms = mockMBeanServer();
+
+ s.addMBeanServer(ms);
+ Mockito.verify(ml, Mockito.never()).error(Mockito.anyString(),
Mockito.isA(Throwable.class));
+ Mockito.verify(ml, Mockito.never()).warn(Mockito.anyString(),
Mockito.isA(Throwable.class));
+ s.registerMBean(Mockito.mock(DynamicMBean.class), sref);
+ Mockito.verify(ml, Mockito.never()).error(Mockito.anyString(),
Mockito.isA(Throwable.class));
+ Mockito.verify(ml, Mockito.never()).warn(Mockito.anyString(),
Mockito.isA(Throwable.class));
+ }
+
+ @Test
+ public void testRegisterMBeanError() throws Exception {
+ final Logger ml = Mockito.mock(Logger.class);
+
+ JmxWhiteboardSupport s = new JmxWhiteboardSupport() {
+ @Override
+ MBeanHolder createMBeanHolder(Object mbean, ObjectName objectName)
{
+ MBeanHolder mbh = super.createMBeanHolder(mbean, objectName);
+ setPrivateField(mbh, "log", ml);
+ return mbh;
+ }
+ };
+
+ final Dictionary<String, Object> props = new Hashtable<String,
Object>();
+ props.put("jmx.objectname", "test:key=val");
+
+ ServiceReference<?> sref = Mockito.mock(ServiceReference.class);
+ Mockito.when(sref.getProperty(Mockito.anyString())).then(new
Answer<Object>() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable
{
+ return props.get(invocation.getArguments()[0].toString());
+ }
+ });
+
+ MBeanServer ms = Mockito.mock(MBeanServer.class);
+ Mockito.when(ms.registerMBean(Mockito.any(), Mockito.eq(new
ObjectName("test:key=val")))).
+ thenThrow(new InstanceAlreadyExistsException());
+
+ s.addMBeanServer(ms);
+ Mockito.verify(ml, Mockito.never()).error(Mockito.anyString(),
Mockito.isA(Throwable.class));
+ Mockito.verify(ml, Mockito.never()).warn(Mockito.anyString(),
Mockito.isA(Throwable.class));
+ s.registerMBean(Mockito.mock(DynamicMBean.class), sref);
+ Mockito.verify(ml, Mockito.times(1)).error(Mockito.anyString(),
Mockito.isA(Throwable.class));
+ Mockito.verify(ml, Mockito.never()).warn(Mockito.anyString(),
Mockito.isA(Throwable.class));
+ }
+
+ @Test
+ public void testRegisterMBeanWarning() throws Exception {
+ final Logger ml = Mockito.mock(Logger.class);
+
+ JmxWhiteboardSupport s = new JmxWhiteboardSupport() {
+ @Override
+ MBeanHolder createMBeanHolder(Object mbean, ObjectName objectName)
{
+ MBeanHolder mbh = super.createMBeanHolder(mbean, objectName);
+ setPrivateField(mbh, "log", ml);
+ return mbh;
+ }
+ };
+
+ final Dictionary<String, Object> props = new Hashtable<String,
Object>();
+ props.put("jmx.objectname", "test:key=val");
+ props.put("warning.exceptions", new String []
{InstanceAlreadyExistsException.class.getName(),
+ MBeanRegistrationException.class.getName()});
+
+ ServiceReference<?> sref = Mockito.mock(ServiceReference.class);
+ Mockito.when(sref.getProperty(Mockito.anyString())).then(new
Answer<Object>() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable
{
+ return props.get(invocation.getArguments()[0].toString());
+ }
+ });
+
+ MBeanServer ms = Mockito.mock(MBeanServer.class);
+ Mockito.when(ms.registerMBean(Mockito.any(), Mockito.eq(new
ObjectName("test:key=val")))).
+ thenThrow(new InstanceAlreadyExistsException());
+
+ s.addMBeanServer(ms);
+ Mockito.verify(ml, Mockito.never()).error(Mockito.anyString(),
Mockito.isA(Throwable.class));
+ Mockito.verify(ml, Mockito.never()).warn(Mockito.anyString(),
Mockito.isA(Throwable.class));
+ s.registerMBean(Mockito.mock(DynamicMBean.class), sref);
+ Mockito.verify(ml, Mockito.never()).error(Mockito.anyString(),
Mockito.isA(Throwable.class));
+ Mockito.verify(ml, Mockito.times(1)).warn(Mockito.anyString(),
Mockito.isA(Throwable.class));
+ }
+
+ private MBeanServer mockMBeanServer()
+ throws InstanceAlreadyExistsException, MBeanRegistrationException,
NotCompliantMBeanException {
+ MBeanServer ms = Mockito.mock(MBeanServer.class);
+ Mockito.when(ms.registerMBean(Mockito.any(),
Mockito.isA(ObjectName.class))).then(
+ new Answer<ObjectInstance>() {
+ @Override
+ public ObjectInstance answer(InvocationOnMock invocation)
throws Throwable {
+ Object[] args = invocation.getArguments();
+ return new ObjectInstance(((ObjectName)
args[1]).toString(), args[0].getClass().getName());
+ }
+ });
+ return ms;
+ }
+
+ private void setPrivateField(Object obj, String name, Object val) {
+ try {
+ Field field = obj.getClass().getDeclaredField(name);
+ field.setAccessible(true);
+ field.set(obj, val);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
Added:
aries/trunk/jmx/jmx-whiteboard/src/test/java/org/apache/aries/jmx/whiteboard/MBeanHolderTest.java
URL:
http://svn.apache.org/viewvc/aries/trunk/jmx/jmx-whiteboard/src/test/java/org/apache/aries/jmx/whiteboard/MBeanHolderTest.java?rev=1818103&view=auto
==============================================================================
---
aries/trunk/jmx/jmx-whiteboard/src/test/java/org/apache/aries/jmx/whiteboard/MBeanHolderTest.java
(added)
+++
aries/trunk/jmx/jmx-whiteboard/src/test/java/org/apache/aries/jmx/whiteboard/MBeanHolderTest.java
Thu Dec 14 11:02:25 2017
@@ -0,0 +1,155 @@
+/*
+ * 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.aries.jmx.whiteboard;
+
+import java.lang.reflect.Field;
+import java.util.Map;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.slf4j.Logger;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class MBeanHolderTest {
+ @Test
+ public void testPlainRegistration() throws Exception {
+ MBeanHolder mbh = new MBeanHolder(new Object(), new
ObjectName("foo:bar=123"));
+ Map<?,?> registrations = (Map<?,?>) getPrivateField(mbh,
"registrations");
+ Logger ml = Mockito.mock(Logger.class);
+ setPrivateField(mbh, "log", ml);
+
+ MBeanServer ms = mockMBeanServer();
+
+ assertEquals(0, registrations.size());
+ mbh.register(ms, new String [] {});
+ assertEquals(1, registrations.size());
+ assertEquals(new ObjectName("foo:bar=123"), registrations.get(ms));
+ Mockito.verify(ml, Mockito.never()).error(Mockito.anyString(),
Mockito.isA(Throwable.class));
+ }
+
+ @Test
+ public void testRegistrationException() throws Exception {
+ MBeanHolder mbh = new MBeanHolder(new Object(), new
ObjectName("foo:bar=123"));
+ Map<?,?> registrations = (Map<?,?>) getPrivateField(mbh,
"registrations");
+ Logger ml = Mockito.mock(Logger.class);
+ setPrivateField(mbh, "log", ml);
+
+ MBeanServer ms = Mockito.mock(MBeanServer.class);
+ Mockito.when(ms.registerMBean(Mockito.any(), Mockito.eq(new
ObjectName("foo:bar=123")))).
+ thenThrow(new InstanceAlreadyExistsException());
+
+ assertEquals(0, registrations.size());
+ mbh.register(ms, new String [] {});
+ assertEquals(0, registrations.size());
+ Mockito.verify(ml, Mockito.times(1)).error(Mockito.anyString(),
Mockito.isA(InstanceAlreadyExistsException.class));
+ }
+
+ @Test
+ public void testExceptionsAsWarnings() throws Exception {
+ MBeanHolder mbh = new MBeanHolder(new Object(), new
ObjectName("foo:bar=123"));
+ Map<?,?> registrations = (Map<?,?>) getPrivateField(mbh,
"registrations");
+ Logger ml = Mockito.mock(Logger.class);
+ setPrivateField(mbh, "log", ml);
+
+ MBeanServer ms = Mockito.mock(MBeanServer.class);
+ Mockito.when(ms.registerMBean(Mockito.any(), Mockito.eq(new
ObjectName("foo:bar=123")))).
+ thenThrow(new InstanceAlreadyExistsException());
+
+ assertEquals(0, registrations.size());
+ mbh.register(ms, new String []
{InstanceAlreadyExistsException.class.getName()});
+ assertEquals(0, registrations.size());
+ Mockito.verify(ml, Mockito.times(1)).warn(Mockito.anyString(),
Mockito.isA(InstanceAlreadyExistsException.class));
+ }
+
+ @Test
+ public void testExceptionsAsWarnings2() throws Exception {
+ MBeanHolder mbh = new MBeanHolder(new Object(), new
ObjectName("foo:bar=123"));
+ Map<?,?> registrations = (Map<?,?>) getPrivateField(mbh,
"registrations");
+ Logger ml = Mockito.mock(Logger.class);
+ setPrivateField(mbh, "log", ml);
+
+ MBeanServer ms = Mockito.mock(MBeanServer.class);
+ Mockito.when(ms.registerMBean(Mockito.any(), Mockito.eq(new
ObjectName("foo:bar=123")))).
+ thenThrow(new NullPointerException());
+
+ assertEquals(0, registrations.size());
+ mbh.register(ms, new String []
{InstanceAlreadyExistsException.class.getName(),
NullPointerException.class.getName()});
+ assertEquals(0, registrations.size());
+ Mockito.verify(ml, Mockito.times(1)).warn(Mockito.anyString(),
Mockito.isA(NullPointerException.class));
+ }
+
+ @Test
+ public void testThrowRuntimeException() throws Exception {
+ MBeanHolder mbh = new MBeanHolder(new Object(), new
ObjectName("foo:bar=123"));
+ Map<?,?> registrations = (Map<?,?>) getPrivateField(mbh,
"registrations");
+ Logger ml = Mockito.mock(Logger.class);
+ setPrivateField(mbh, "log", ml);
+
+ MBeanServer ms = Mockito.mock(MBeanServer.class);
+ Mockito.when(ms.registerMBean(Mockito.any(), Mockito.eq(new
ObjectName("foo:bar=123")))).
+ thenThrow(new NullPointerException());
+
+ assertEquals(0, registrations.size());
+ try {
+ mbh.register(ms, new String [] {});
+ fail("Should have thrown a NullPointerException");
+ } catch (NullPointerException npe) {
+ // good!
+ }
+ }
+
+ private MBeanServer mockMBeanServer()
+ throws InstanceAlreadyExistsException, MBeanRegistrationException,
NotCompliantMBeanException {
+ MBeanServer ms = Mockito.mock(MBeanServer.class);
+ Mockito.when(ms.registerMBean(Mockito.any(),
Mockito.isA(ObjectName.class))).then(
+ new Answer<ObjectInstance>() {
+ @Override
+ public ObjectInstance answer(InvocationOnMock invocation)
throws Throwable {
+ Object[] args = invocation.getArguments();
+ return new ObjectInstance(((ObjectName)
args[1]).toString(), args[0].getClass().getName());
+ }
+ });
+ return ms;
+ }
+
+ private Object getPrivateField(Object obj, String name)
+ throws SecurityException, NoSuchFieldException,
IllegalArgumentException, IllegalAccessException {
+ Field field = obj.getClass().getDeclaredField(name);
+ field.setAccessible(true);
+ return field.get(obj);
+ }
+
+ private void setPrivateField(Object obj, String name, Object val)
+ throws SecurityException, NoSuchFieldException,
IllegalArgumentException, IllegalAccessException {
+ Field field = obj.getClass().getDeclaredField(name);
+ field.setAccessible(true);
+ field.set(obj, val);
+ }
+}