content: /**
   *  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(
                  setupRemoteDebugging(),
                  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++;
          }
      }
  }
  