/**
* 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.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.ops4j.pax.exam.CoreOptions.options;
import static org.osgi.jmx.framework.BundleStateMBean.OBJECTNAME;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import javax.management.AttributeChangeNotification;
import javax.management.AttributeChangeNotificationFilter;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.TabularData;
import org.apache.aries.jmx.AbstractIntegrationTest;
import org.apache.aries.jmx.codec.BundleData.Header;
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.Version;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.jmx.framework.BundleStateMBean;
/**
* @version $Rev$ $Date$
*/
@ExamReactorStrategy(PerMethod.class)
public class BundleStateMBeanTest extends AbstractIntegrationTest {
private ObjectName objectName;
private BundleStateMBean mbean;
private Bundle a;
private Bundle b;
private Bundle fragc;
private Bundle d;
@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(),
fragmentc(),
bundled(),
bundlee());
}
@Before
public void doSetUp() throws Exception {
objectName = waitForMBean(BundleStateMBean.OBJECTNAME);
mbean = getMBean(BundleStateMBean.OBJECTNAME, BundleStateMBean.class);
//get bundles
a = getBundleByName("org.apache.aries.jmx.test.bundlea");
b = getBundleByName("org.apache.aries.jmx.test.bundleb");
fragc = getBundleByName("org.apache.aries.jmx.test.fragc");
d = getBundleByName("org.apache.aries.jmx.test.bundled");
}
@Test
public void testObjectName() throws Exception {
Set<ObjectName> names = mbeanServer.queryNames(new ObjectName(BundleStateMBean.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 {
// exportedPackages
String[] exports = mbean.getExportedPackages(a.getBundleId());
assertEquals(2, exports.length);
List<String> packages = Arrays.asList(exports);
assertTrue(packages.contains("org.apache.aries.jmx.test.bundlea.api;2.0.0"));
assertTrue(packages.contains("org.apache.aries.jmx.test.fragmentc;0.0.0"));
// fragments
long[] fragments = mbean.getFragments(a.getBundleId());
assertEquals(1, fragments.length);
assertEquals(fragc.getBundleId() , fragments[0]);
// headers
TabularData headers = mbean.getHeaders(b.getBundleId());
assertNotNull(headers);
assertEquals(BundleStateMBean.HEADERS_TYPE, headers.getTabularType());
assertTrue(headers.values().size() >= 4 );
assertEquals("org.apache.aries.jmx.test.bundleb", Header.from(headers.get(new Object[] {Constants.BUNDLE_SYMBOLICNAME})).getValue());
// hosts
long[] hosts = mbean.getHosts(fragc.getBundleId());
assertEquals(1, hosts.length);
assertEquals(a.getBundleId() , hosts[0]);
//imported packages
String[] imports = mbean.getImportedPackages(a.getBundleId());
assertTrue(imports.length >= 3);
List<String> importedPackages = Arrays.asList(imports);
Version version = getPackageVersion("org.osgi.framework");
assertTrue(importedPackages.contains("org.osgi.framework;" + version.toString()));
assertTrue(importedPackages.contains("org.apache.aries.jmx.test.bundleb.api;1.1.0"));
//last modified
assertTrue(mbean.getLastModified(b.getBundleId()) > 0);
//location
assertEquals(b.getLocation(), mbean.getLocation(b.getBundleId()));
//registered services
long[] serviceIds = mbean.getRegisteredServices(a.getBundleId());
assertEquals(1, serviceIds.length);
//required bundles
long[] required = mbean.getRequiredBundles(d.getBundleId());
assertEquals(1, required.length);
assertEquals(a.getBundleId(), required[0]);
//requiring bundles
long[] requiring = mbean.getRequiringBundles(a.getBundleId());
assertEquals(2, requiring.length);
assertTrue(fragc.getSymbolicName(), arrayContains(fragc.getBundleId(), requiring));
assertTrue(d.getSymbolicName(), arrayContains(d.getBundleId(), requiring));
//services in use
long[] servicesInUse = mbean.getServicesInUse(a.getBundleId());
assertEquals(1, servicesInUse.length);
//start level
long startLevel = mbean.getStartLevel(b.getBundleId());
assertTrue(startLevel >= 0);
//state
assertEquals("ACTIVE", mbean.getState(b.getBundleId()));
//isFragment
assertFalse(mbean.isFragment(b.getBundleId()));
assertTrue(mbean.isFragment(fragc.getBundleId()));
//isRemovalPending
assertFalse(mbean.isRemovalPending(b.getBundleId()));
// isRequired
assertTrue(mbean.isRequired(a.getBundleId()));
assertTrue(mbean.isRequired(b.getBundleId()));
// listBundles
TabularData bundlesTable = mbean.listBundles();
assertNotNull(bundlesTable);
assertEquals(BundleStateMBean.BUNDLES_TYPE, bundlesTable.getTabularType());
assertEquals(bundleContext.getBundles().length, bundlesTable.values().size());
// notifications
final List<Notification> received = new ArrayList<Notification>();
mbeanServer.addNotificationListener(objectName, new NotificationListener() {
public void handleNotification(Notification notification, Object handback) {
received.add(notification);
}
}, null, null);
assertEquals(Bundle.ACTIVE, b.getState());
b.stop();
assertEquals(Bundle.RESOLVED, b.getState());
b.start();
assertEquals(Bundle.ACTIVE, b.getState());
int i = 0;
while (received.size() < 2 && i < 3) {
Thread.sleep(1000);
i++;
}
assertEquals(2, received.size());
}
@Test
public void testAttributeChangeNotifications() throws Exception {
final List<AttributeChangeNotification> attributeChanges = new ArrayList<AttributeChangeNotification>();
AttributeChangeNotificationFilter filter = new AttributeChangeNotificationFilter();
filter.disableAllAttributes();
filter.enableAttribute("BundleIds");
mbeanServer.addNotificationListener(objectName, new NotificationListener() {
public void handleNotification(Notification notification, Object handback) {
attributeChanges.add((AttributeChangeNotification) notification);
}
}, filter, null);
long[] idsWithout = mbean.getBundleIds();
assertEquals("Precondition", 0, attributeChanges.size());
Manifest mf = new Manifest();
mf.getMainAttributes().putValue("Bundle-ManifestVersion", "2");
mf.getMainAttributes().putValue("Bundle-SymbolicName", "empty-test-bundle");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
JarOutputStream jos = new JarOutputStream(baos, mf);
jos.closeEntry();
jos.close();
InputStream bais = new ByteArrayInputStream(baos.toByteArray());
Bundle bundle = bundleContext.installBundle("http://somelocation", bais);
long[] idsWith = new long[idsWithout.length + 1];
System.arraycopy(idsWithout, 0, idsWith, 0, idsWithout.length);
idsWith[idsWith.length - 1] = bundle.getBundleId();
Arrays.sort(idsWith);
waitForListToReachSize(attributeChanges, 1);
assertEquals(1, attributeChanges.size());
AttributeChangeNotification ac = attributeChanges.get(0);
assertEquals("BundleIds", ac.getAttributeName());
long oldSequence = ac.getSequenceNumber();
assertTrue(Arrays.equals(idsWithout, (long []) ac.getOldValue()));
assertTrue(Arrays.equals(idsWith, (long []) ac.getNewValue()));
bundle.uninstall();
waitForListToReachSize(attributeChanges, 2);
AttributeChangeNotification ac2 = attributeChanges.get(1);
assertEquals("BundleIds", ac2.getAttributeName());
assertEquals(oldSequence +1, ac2.getSequenceNumber());
assertTrue(Arrays.equals(idsWith, (long []) ac2.getOldValue()));
assertTrue(Arrays.equals(idsWithout, (long []) ac2.getNewValue()));
}
@Test
public void testBundleIDsAttribute() throws Exception{
Set<Long> expectedIDs = new HashSet<Long>();
for (Bundle b : context().getBundles()) {
expectedIDs.add(b.getBundleId());
}
BundleStateMBean mbean = getMBean(OBJECTNAME, BundleStateMBean.class);
long[] actual = mbean.getBundleIds();
Set<Long> actualIDs = new HashSet<Long>();
for (long id : actual) {
actualIDs.add(id);
}
assertEquals(expectedIDs, actualIDs);
}
@Test
@SuppressWarnings({ "unchecked" })
public void testHeaderLocalization() throws Exception {
Bundle bundleE = context().getBundleByName("org.apache.aries.jmx.test.bundlee");
CompositeData cd = mbean.getBundle(bundleE.getBundleId());
long id = (Long) cd.get(BundleStateMBean.IDENTIFIER);
assertEquals("Description", mbean.getHeader(id, Constants.BUNDLE_DESCRIPTION));
assertEquals("Description", mbean.getHeader(id, Constants.BUNDLE_DESCRIPTION, "en"));
assertEquals("Omschrijving", mbean.getHeader(id, Constants.BUNDLE_DESCRIPTION, "nl"));
TabularData td = mbean.getHeaders(id);
boolean found = false;
for (CompositeData d : (Collection<CompositeData>) td.values()) {
if (Constants.BUNDLE_DESCRIPTION.equals(d.get(BundleStateMBean.KEY))) {
assertEquals("Description", d.get(BundleStateMBean.VALUE));
found = true;
break;
}
}
assertTrue(found);
TabularData tdNL = mbean.getHeaders(id, "nl");
boolean foundNL = false;
for (CompositeData d : (Collection<CompositeData>) tdNL.values()) {
if (Constants.BUNDLE_DESCRIPTION.equals(d.get(BundleStateMBean.KEY))) {
assertEquals("Omschrijving", d.get(BundleStateMBean.VALUE));
foundNL = true;
break;
}
}
assertTrue(foundNL);
}
private Version getPackageVersion(String packageName) {
Bundle systemBundle = context().getBundle(0);
BundleWiring wiring = (BundleWiring) systemBundle.adapt(BundleWiring.class);
List<BundleCapability> packages = wiring.getCapabilities(BundleRevision.PACKAGE_NAMESPACE);
for (BundleCapability pkg : packages) {
Map<String, Object> attrs = pkg.getAttributes();
if (attrs.get(BundleRevision.PACKAGE_NAMESPACE).equals(packageName)) {
return (Version) attrs.get(Constants.VERSION_ATTRIBUTE);
}
}
throw new IllegalStateException("Package version not found for " + packageName);
}
private static boolean arrayContains(long value, long[] values) {
for (long i : values) {
if (i == value) {
return true;
}
}
return false;
}
private void waitForListToReachSize(List<?> list, int targetSize) throws InterruptedException {
int i = 0;
while (list.size() < targetSize && i < 3) {
Thread.sleep(1000);
i++;
}
}
}