/** * 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.framework; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.ops4j.pax.exam.CoreOptions.options; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Dictionary; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Set; import javax.management.AttributeChangeNotification; import javax.management.Notification; import javax.management.NotificationListener; import javax.management.ObjectName; import javax.management.openmbean.CompositeData; import javax.management.openmbean.TabularData; import junit.framework.Assert; import org.apache.aries.jmx.AbstractIntegrationTest; import org.apache.aries.jmx.codec.PropertyData; import org.apache.aries.jmx.test.bundlea.api.InterfaceA; import org.apache.aries.jmx.test.bundleb.api.InterfaceB; import org.junit.Before; import org.junit.Test; import org.ops4j.pax.exam.Configuration; import org.ops4j.pax.exam.Option; import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; import org.ops4j.pax.exam.spi.reactors.PerMethod; import org.osgi.framework.Bundle; import org.osgi.framework.Constants; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import org.osgi.jmx.JmxConstants; import org.osgi.jmx.framework.ServiceStateMBean; import org.osgi.service.cm.ManagedService; import org.osgi.service.cm.ManagedServiceFactory; /** * * * @version $Rev$ $Date$ */ @ExamReactorStrategy(PerMethod.class) public class ServiceStateMBeanTest extends AbstractIntegrationTest { private ObjectName objectName; private ServiceStateMBean mbean; @Configuration public Option[] configuration() { return options( // new VMOption( "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000" ), // new TimeoutOption( 0 ), jmxRuntime(), bundlea(), bundleb() ); } @Before public void doSetUp() { waitForMBean(ServiceStateMBean.OBJECTNAME); objectName = waitForMBean(ServiceStateMBean.OBJECTNAME); mbean = getMBean(objectName, ServiceStateMBean.class); assertNotNull(mbean); } @Test public void testObjectName() throws Exception { Set<ObjectName> names = mbeanServer.queryNames(new ObjectName(ServiceStateMBean.OBJECTNAME + ",*"), null); assertEquals(1, names.size()); ObjectName name = names.iterator().next(); Hashtable<String, String> props = name.getKeyPropertyList(); assertEquals(context().getProperty(Constants.FRAMEWORK_UUID), props.get("uuid")); assertEquals(context().getBundle(0).getSymbolicName(), props.get("framework")); } @Test public void testMBeanInterface() throws Exception { //get bundles Bundle a = getBundleByName("org.apache.aries.jmx.test.bundlea"); assertBundleStarted(a); Bundle b = getBundleByName("org.apache.aries.jmx.test.bundleb"); assertBundleStarted(b); // get services ServiceReference refA = bundleContext.getServiceReference(InterfaceA.class.getName()); assertNotNull(refA); long serviceAId = (Long) refA.getProperty(Constants.SERVICE_ID); assertTrue(serviceAId > -1); ServiceReference refB = bundleContext.getServiceReference(InterfaceB.class.getName()); assertNotNull(refB); long serviceBId = (Long) refB.getProperty(Constants.SERVICE_ID); assertTrue(serviceBId > -1); ServiceReference[] refs = bundleContext.getServiceReferences(ManagedServiceFactory.class.getName(), "(" + Constants.SERVICE_PID + "=jmx.test.B.factory)"); assertNotNull(refs); assertEquals(1, refs.length); ServiceReference msf = refs[0]; // getBundleIdentifier assertEquals(a.getBundleId(), mbean.getBundleIdentifier(serviceAId)); //getObjectClass String[] objectClass = mbean.getObjectClass(serviceAId); assertEquals(2, objectClass.length); List<String> classNames = Arrays.asList(objectClass); assertTrue(classNames.contains(InterfaceA.class.getName())); assertTrue(classNames.contains(ManagedService.class.getName())); // getProperties TabularData serviceProperties = mbean.getProperties(serviceBId); assertNotNull(serviceProperties); assertEquals(JmxConstants.PROPERTIES_TYPE, serviceProperties.getTabularType()); assertTrue(serviceProperties.values().size() > 1); assertEquals("org.apache.aries.jmx.test.ServiceB", PropertyData.from(serviceProperties.get(new Object[] { Constants.SERVICE_PID })).getValue()); // getUsingBundles long[] usingBundles = mbean.getUsingBundles(serviceBId); assertEquals(1, usingBundles.length); assertEquals(a.getBundleId(), usingBundles[0]); // listServices ServiceReference[] allSvsRefs = bundleContext.getAllServiceReferences(null, null); TabularData allServices = mbean.listServices(); assertNotNull(allServices); assertEquals(allSvsRefs.length, allServices.values().size()); // notifications final List<Notification> received = new ArrayList<Notification>(); final List<AttributeChangeNotification> attributeChanges = new ArrayList<AttributeChangeNotification>(); mbeanServer.addNotificationListener(objectName, new NotificationListener() { public void handleNotification(Notification notification, Object handback) { if (notification instanceof AttributeChangeNotification) { attributeChanges.add((AttributeChangeNotification) notification); } else { received.add(notification); } } }, null, null); assertNotNull(refB); assertNotNull(msf); b.stop(); refB = bundleContext.getServiceReference(InterfaceB.class.getName()); refs = bundleContext.getServiceReferences(ManagedServiceFactory.class.getName(), "(" + Constants.SERVICE_PID + "=jmx.test.B.factory)"); assertNull(refs); assertNull(refB); b.start(); refB = bundleContext.getServiceReference(InterfaceB.class.getName()); refs = bundleContext.getServiceReferences(ManagedServiceFactory.class.getName(), "(" + Constants.SERVICE_PID + "=jmx.test.B.factory)"); assertNotNull(refB); assertNotNull(refs); assertEquals(1, refs.length); waitForListToReachSize(received, 4); assertEquals(4, received.size()); assertEquals(4, attributeChanges.size()); } private void assertBundleStarted(Bundle bundle) { Assert.assertEquals("Bundle " + bundle.getSymbolicName() + " should be started but is in state " + bundle.getState(), Bundle.ACTIVE, bundle.getState()); } @Test public void testAttributeChangeNotifications() throws Exception { final List<AttributeChangeNotification> attributeChanges = new ArrayList<AttributeChangeNotification>(); mbeanServer.addNotificationListener(objectName, new NotificationListener() { public void handleNotification(Notification notification, Object handback) { if (notification instanceof AttributeChangeNotification) { attributeChanges.add((AttributeChangeNotification) notification); } } }, null, null); assertEquals("Precondition", 0, attributeChanges.size()); long[] idsWithout = mbean.getServiceIds(); String svc = "A String Service"; ServiceRegistration reg = bundleContext.registerService(String.class.getName(), svc, null); long id = (Long) reg.getReference().getProperty(Constants.SERVICE_ID); long[] idsWith = new long[idsWithout.length + 1]; System.arraycopy(idsWithout, 0, idsWith, 0, idsWithout.length); idsWith[idsWith.length - 1] = id; Arrays.sort(idsWith); waitForListToReachSize(attributeChanges, 1); AttributeChangeNotification ac = attributeChanges.get(0); assertEquals("ServiceIds", ac.getAttributeName()); long seq1 = ac.getSequenceNumber(); assertTrue(Arrays.equals(idsWithout, (long []) ac.getOldValue())); assertTrue(Arrays.equals(idsWith, (long []) ac.getNewValue())); Dictionary<String, Object> props = new Hashtable<String, Object>(); props.put("somekey", "someval"); reg.setProperties(props); // Setting the properties updates the service registration, however it should not cause the attribute notification Thread.sleep(500); // Give the system a bit of time to send potential notifications assertEquals("Changing the service registration should not cause an attribute notification", 1, attributeChanges.size()); reg.unregister(); waitForListToReachSize(attributeChanges, 2); AttributeChangeNotification ac2 = attributeChanges.get(1); assertEquals("ServiceIds", ac2.getAttributeName()); assertEquals(seq1 +1, ac2.getSequenceNumber()); assertTrue(Arrays.equals(idsWith, (long []) ac2.getOldValue())); assertTrue(Arrays.equals(idsWithout, (long []) ac2.getNewValue())); } @Test public void testGetServiceIds() throws Exception { ServiceReference[] allSvsRefs = bundleContext.getAllServiceReferences(null, null); long[] expectedServiceIds = new long[allSvsRefs.length]; for (int i=0; i < allSvsRefs.length; i++) { expectedServiceIds[i] = (Long) allSvsRefs[i].getProperty(Constants.SERVICE_ID); } long[] actualServiceIds = mbean.getServiceIds(); Arrays.sort(expectedServiceIds); Arrays.sort(actualServiceIds); assertTrue(Arrays.equals(expectedServiceIds, actualServiceIds)); } @Test public void testGetServiceAndGetProperty() throws Exception { ServiceReference sref = bundleContext.getServiceReference(InterfaceA.class); // Get service to increase service references context().getService(sref); Long serviceID = (Long) sref.getProperty(Constants.SERVICE_ID); CompositeData svcData = mbean.getService(serviceID); assertEquals(serviceID, svcData.get(ServiceStateMBean.IDENTIFIER)); assertEquals(sref.getBundle().getBundleId(), svcData.get(ServiceStateMBean.BUNDLE_IDENTIFIER)); Set<String> expectedClasses = new HashSet<String>(Arrays.asList(InterfaceA.class.getName(), ManagedService.class.getName())); Set<String> actualClasses = new HashSet<String>(Arrays.asList((String []) svcData.get(ServiceStateMBean.OBJECT_CLASS))); assertEquals(expectedClasses, actualClasses); Bundle[] ub = sref.getUsingBundles(); assertEquals("Precondition", 1, ub.length); assertTrue(Arrays.equals(new Long[] {ub[0].getBundleId()}, (Long[]) svcData.get("UsingBundles"))); // Test mbean.getProperty() String pid = (String) sref.getProperty(Constants.SERVICE_PID); CompositeData pidData = mbean.getProperty(serviceID, Constants.SERVICE_PID); assertEquals(pid, pidData.get("Value")); assertEquals("String", pidData.get("Type")); CompositeData idData = mbean.getProperty(serviceID, Constants.SERVICE_ID); assertEquals("" + serviceID, idData.get("Value")); assertEquals("Long", idData.get("Type")); CompositeData ocData = mbean.getProperty(serviceID, Constants.OBJECTCLASS); String form1 = InterfaceA.class.getName() + "," + ManagedService.class.getName(); String form2 = ManagedService.class.getName() + "," + InterfaceA.class.getName(); assertTrue(ocData.get("Value").equals(form1) || ocData.get("Value").equals(form2)); assertEquals("Array of String", ocData.get("Type")); context().ungetService(sref); } @Test @SuppressWarnings("unchecked") public void testServicePropertiesInListServices() throws Exception { ServiceReference[] refs = bundleContext.getAllServiceReferences(InterfaceA.class.getName(), null); assertEquals("Precondition", 1, refs.length); ServiceReference ref = refs[0]; TabularData svcTab = mbean.listServices(); CompositeData svcData = svcTab.get(new Object [] {ref.getProperty(Constants.SERVICE_ID)}); Set<String> expectedOCs = new HashSet<String>(Arrays.asList( InterfaceA.class.getName(), ManagedService.class.getName())); Set<String> actualOCs = new HashSet<String>( Arrays.asList((String [])svcData.get(Constants.OBJECTCLASS))); assertEquals(expectedOCs, actualOCs); Map<String, Object> expectedProperties = new HashMap<String, Object>(); for (String key : ref.getPropertyKeys()) { Object value = ref.getProperty(key); if (value.getClass().isArray()) continue; expectedProperties.put(key, value); } Map<String, Object> actualProperties = new HashMap<String, Object>(); TabularData actualProps = (TabularData) svcData.get(ServiceStateMBean.PROPERTIES); for (CompositeData cd : (Collection<CompositeData>) actualProps.values()) { Object type = cd.get(JmxConstants.TYPE); if (JmxConstants.STRING.equals(type)) { actualProperties.put((String) cd.get(JmxConstants.KEY), cd.get(JmxConstants.VALUE)); } else if (JmxConstants.LONG.equals(type)) { actualProperties.put((String) cd.get(JmxConstants.KEY), Long.valueOf(cd.get(JmxConstants.VALUE).toString())); } } assertEquals(expectedProperties, actualProperties); } @Test public void testListServices() throws Exception { String filter = "(" + Constants.SERVICE_PID + "=*)"; ServiceReference[] refs = bundleContext.getAllServiceReferences(null, filter); TabularData svcData = mbean.listServices(null, filter); assertEquals(refs.length, svcData.size()); ServiceReference sref = bundleContext.getServiceReference(InterfaceA.class); TabularData svcTab = mbean.listServices(InterfaceA.class.getName(), null); assertEquals(1, svcTab.size()); CompositeData actualSvc = (CompositeData) svcTab.values().iterator().next(); CompositeData expectedSvc = mbean.getService((Long) sref.getProperty(Constants.SERVICE_ID)); assertEquals(expectedSvc, actualSvc); } @SuppressWarnings("unchecked") @Test public void testListServicesSelectiveItems() throws Exception { String filter = "(|(service.pid=org.apache.aries.jmx.test.ServiceB)(service.pid=jmx.test.B.factory))"; ServiceReference[] refs = bundleContext.getAllServiceReferences(null, filter); TabularData svcData = mbean.listServices(null, filter, ServiceStateMBean.BUNDLE_IDENTIFIER); assertEquals(refs.length, svcData.size()); long id = refs[0].getBundle().getBundleId(); for (ServiceReference ref : refs) { assertEquals("Precondition", id, ref.getBundle().getBundleId()); } for (CompositeData cd : new ArrayList<CompositeData>((Collection<CompositeData>) svcData.values())) { assertEquals(id, cd.get(ServiceStateMBean.BUNDLE_IDENTIFIER)); assertNotNull(cd.get(ServiceStateMBean.IDENTIFIER)); assertNull(cd.get(ServiceStateMBean.OBJECT_CLASS)); assertNull(cd.get(ServiceStateMBean.USING_BUNDLES)); } } private void waitForListToReachSize(List<?> list, int targetSize) throws InterruptedException { int i = 0; while (list.size() < targetSize && i < 3) { Thread.sleep(1000); i++; } } }