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.assertNotNull;
  import static org.junit.Assert.assertNull;
  import static org.junit.Assert.assertTrue;
  import static org.junit.Assert.fail;
  import static org.ops4j.pax.exam.CoreOptions.provision;
  import static org.ops4j.pax.tinybundles.core.TinyBundles.bundle;
  import static org.ops4j.pax.tinybundles.core.TinyBundles.withBnd;
  
  import java.io.ByteArrayInputStream;
  import java.io.ByteArrayOutputStream;
  import java.io.File;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.HashSet;
  import java.util.Hashtable;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import java.util.TreeSet;
  import java.util.jar.JarEntry;
  import java.util.jar.JarOutputStream;
  import java.util.jar.Manifest;
  import javax.management.ObjectName;
  import javax.management.openmbean.CompositeData;
  
  import org.apache.aries.jmx.AbstractIntegrationTest;
  import org.apache.aries.jmx.codec.BatchActionResult;
  import org.junit.Before;
  import org.junit.Test;
  import org.ops4j.pax.exam.Configuration;
  import org.ops4j.pax.exam.CoreOptions;
  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.BundleException;
  import org.osgi.framework.Constants;
  import org.osgi.framework.Version;
  import org.osgi.framework.wiring.BundleRevision;
  import org.osgi.framework.wiring.BundleRevisions;
  import org.osgi.framework.wiring.BundleWire;
  import org.osgi.framework.wiring.BundleWiring;
  import org.osgi.framework.wiring.FrameworkWiring;
  import org.osgi.jmx.framework.FrameworkMBean;
  
  /**
   * @version $Rev$ $Date$
   */
  @ExamReactorStrategy(PerMethod.class)
  public class FrameworkMBeanTest extends AbstractIntegrationTest {
  
      private FrameworkMBean framework;
  
  	@Configuration
      public Option[] configuration() {
          return CoreOptions.options(
                  setupRemoteDebugging(),
                  jmxRuntime(),
                  bundlea1(),
                  bundleb1()
          );
      }
      
  	protected Option bundlea1() {
  		return provision(bundle()
  		        .add(org.apache.aries.jmx.test.bundlea.api.InterfaceA.class)
  		        .add(org.apache.aries.jmx.test.bundlea.impl.A2.class)
  		        .set(Constants.BUNDLE_SYMBOLICNAME, "org.apache.aries.jmx.test.bundlea")
  		        .set(Constants.BUNDLE_VERSION, "1")
  		        .set(Constants.EXPORT_PACKAGE, "org.apache.aries.jmx.test.bundlea.api")
  		        .build(withBnd()));
  	}
  	
  	protected Option bundleb1() {
  		return provision(bundle()
  		        .add(org.apache.aries.jmx.test.bundleb.api.InterfaceB.class)
  		        .set(Constants.BUNDLE_SYMBOLICNAME, "org.apache.aries.jmx.test.bundleb")
  		        .set(Constants.IMPORT_PACKAGE, "org.apache.aries.jmx.test.bundlea.api," +
  		                "org.apache.aries.jmx.test.bundlea.impl;resolution:=optional")
  		        .build(withBnd()));
  	}
  
      @Before
      public void doSetUp() throws BundleException {
      	for (Bundle bundle : context().getBundles()) {
  			System.out.println(bundle.getBundleId() + " " + bundle.getSymbolicName() + " " + bundle.getState());
  		};
          waitForMBean(FrameworkMBean.OBJECTNAME);
          framework = getMBean(FrameworkMBean.OBJECTNAME, FrameworkMBean.class);
      }
  
      @Test
      public void testObjectName() throws Exception {
          Set<ObjectName> names = mbeanServer.queryNames(new ObjectName(FrameworkMBean.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 testGetProperty() throws Exception {
          String expectedVer = context().getProperty(Constants.FRAMEWORK_VERSION);
          String actualVer = framework.getProperty(Constants.FRAMEWORK_VERSION);
          assertEquals(expectedVer, actualVer);
  
          String expectedTmp = context().getProperty("java.io.tmpdir");
          String actualTmp = framework.getProperty("java.io.tmpdir");
          assertEquals(expectedTmp, actualTmp);
      }
  
      @Test
      public void testGetDependencyClosure() throws Exception {
          Bundle bundleA = getBundleByName("org.apache.aries.jmx.test.bundlea");
          Bundle bundleB = getBundleByName("org.apache.aries.jmx.test.bundleb");
  
          BundleWiring bw = (BundleWiring) bundleB.adapt(BundleWiring.class);
  
          List<BundleWire> initialRequiredWires = bw.getRequiredWires(BundleRevision.PACKAGE_NAMESPACE);
          assertEquals(1, initialRequiredWires.size());
          BundleWire wire = initialRequiredWires.get(0);
          Map<String, Object> capabilityAttributes = wire.getCapability().getAttributes();
          assertEquals("Precondition", bundleA.getSymbolicName(), capabilityAttributes.get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE));
          assertEquals("Precondition", new Version("1.0"), capabilityAttributes.get(Constants.BUNDLE_VERSION_ATTRIBUTE));
          assertEquals("Precondition", "org.apache.aries.jmx.test.bundlea.api", capabilityAttributes.get(BundleRevision.PACKAGE_NAMESPACE));
  
          Collection<Bundle> expectedDC = ((FrameworkWiring) context().getBundle(0).adapt(FrameworkWiring.class)).getDependencyClosure(Collections.singleton(bundleA));
          Set<Long> expectedClosure = new TreeSet<Long>();
          for (Bundle b : expectedDC) {
              expectedClosure.add(b.getBundleId());
          }
  
          long[] actualDC = framework.getDependencyClosure(new long [] {bundleA.getBundleId()});
          Set<Long> actualClosure = new TreeSet<Long>();
          for (long l : actualDC) {
              actualClosure.add(l);
          }
  
          assertEquals(expectedClosure, actualClosure);
      }
  
      @Test
      public void testRefreshBundleAndWait() throws Exception {
          FrameworkWiring frameworkWiring = (FrameworkWiring) context().getBundle(0).adapt(FrameworkWiring.class);
  
          Bundle bundleA = getBundleByName("org.apache.aries.jmx.test.bundlea");
          Bundle bundleB = getBundleByName("org.apache.aries.jmx.test.bundleb");
  
          BundleWiring bw = (BundleWiring) bundleB.adapt(BundleWiring.class);
  
          List<BundleWire> initialRequiredWires = bw.getRequiredWires(BundleRevision.PACKAGE_NAMESPACE);
          assertEquals(1, initialRequiredWires.size());
          BundleWire wire = initialRequiredWires.get(0);
          Map<String, Object> capabilityAttributes = wire.getCapability().getAttributes();
          assertEquals("Precondition", bundleA.getSymbolicName(), capabilityAttributes.get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE));
          assertEquals("Precondition", new Version("1.0"), capabilityAttributes.get(Constants.BUNDLE_VERSION_ATTRIBUTE));
          assertEquals("Precondition", "org.apache.aries.jmx.test.bundlea.api", capabilityAttributes.get(BundleRevision.PACKAGE_NAMESPACE));
  
          // Create an updated version of Bundle A, which an extra export and version 1.1
          Manifest manifest = new Manifest();
          manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
          manifest.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, "org.apache.aries.jmx.test.bundlea");
          manifest.getMainAttributes().putValue(Constants.BUNDLE_VERSION, "1.1");
          manifest.getMainAttributes().putValue(Constants.EXPORT_PACKAGE, "org.apache.aries.jmx.test.bundlea.api,org.apache.aries.jmx.test.bundlea.impl");
  
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          JarOutputStream jos = new JarOutputStream(baos, manifest);
          addResourceToJar("org/apache/aries/jmx/test/bundlea/api/InterfaceA.class", jos, bundleA);
          addResourceToJar("org/apache/aries/jmx/test/bundlea/impl/A2.class", jos, bundleA);
          jos.close();
  
          assertEquals("Precondition", 0, frameworkWiring.getRemovalPendingBundles().size());
          assertEquals(0, framework.getRemovalPendingBundles().length);
  
          assertEquals("Precondition", 1, ((BundleRevisions) bundleA.adapt(BundleRevisions.class)).getRevisions().size());
          bundleA.update(new ByteArrayInputStream(baos.toByteArray()));
          assertEquals("There should be 2 revisions now", 2, ((BundleRevisions) bundleA.adapt(BundleRevisions.class)).getRevisions().size());
          assertEquals("No refresh called, the bundle wiring for B should still be the old one",
                  bw, bundleB.adapt(BundleWiring.class));
  
          assertEquals("Precondition", 1, frameworkWiring.getRemovalPendingBundles().size());
          assertEquals(1, framework.getRemovalPendingBundles().length);
          assertEquals(((Bundle) frameworkWiring.getRemovalPendingBundles().iterator().next()).getBundleId(),
                  framework.getRemovalPendingBundles()[0]);
  
          assertTrue(framework.refreshBundleAndWait(bundleB.getBundleId()));
  
          List<BundleWire> requiredWires = ((BundleWiring) bundleB.adapt(BundleWiring.class)).getRequiredWires(BundleRevision.PACKAGE_NAMESPACE);
          assertEquals(2, requiredWires.size());
          List<String> imported = new ArrayList<String>();
          for (BundleWire w : requiredWires) {
              Map<String, Object> ca = w.getCapability().getAttributes();
              assertEquals(bundleA.getSymbolicName(), ca.get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE));
              imported.add(ca.get(BundleRevision.PACKAGE_NAMESPACE).toString());
  
              if ("org.apache.aries.jmx.test.bundlea.impl".equals(ca.get(BundleRevision.PACKAGE_NAMESPACE))) {
                  // Came across an issue where equinox was reporting the other package as still coming from from the 1.0 bundle
                  // not sure if this is a bug or not...
                  assertEquals(new Version("1.1"), ca.get(Constants.BUNDLE_VERSION_ATTRIBUTE));
              }
          }
          assertEquals(Arrays.asList("org.apache.aries.jmx.test.bundlea.api", "org.apache.aries.jmx.test.bundlea.impl"), imported);
      }
  
      @Test
      public void testRefreshBundlesAndWait() throws Exception {
          Bundle bundleA = getBundleByName("org.apache.aries.jmx.test.bundlea");
          Bundle bundleB = getBundleByName("org.apache.aries.jmx.test.bundleb");
  
          BundleWiring bw = (BundleWiring) bundleB.adapt(BundleWiring.class);
  
          List<BundleWire> initialRequiredWires = bw.getRequiredWires(BundleRevision.PACKAGE_NAMESPACE);
          assertEquals(1, initialRequiredWires.size());
          BundleWire wire = initialRequiredWires.get(0);
          Map<String, Object> capabilityAttributes = wire.getCapability().getAttributes();
          assertEquals("Precondition", bundleA.getSymbolicName(), capabilityAttributes.get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE));
          assertEquals("Precondition", new Version("1.0"), capabilityAttributes.get(Constants.BUNDLE_VERSION_ATTRIBUTE));
          assertEquals("Precondition", "org.apache.aries.jmx.test.bundlea.api", capabilityAttributes.get(BundleRevision.PACKAGE_NAMESPACE));
  
          // Create an updated version of Bundle A, which an extra export and version 1.1
          Manifest manifest = new Manifest();
          manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
          manifest.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, "org.apache.aries.jmx.test.bundlea");
          manifest.getMainAttributes().putValue(Constants.BUNDLE_VERSION, "1.1");
          manifest.getMainAttributes().putValue(Constants.EXPORT_PACKAGE, "org.apache.aries.jmx.test.bundlea.api,org.apache.aries.jmx.test.bundlea.impl");
  
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          JarOutputStream jos = new JarOutputStream(baos, manifest);
          addResourceToJar("org/apache/aries/jmx/test/bundlea/api/InterfaceA.class", jos, bundleA);
          addResourceToJar("org/apache/aries/jmx/test/bundlea/impl/A2.class", jos, bundleA);
          jos.close();
  
          assertEquals("Precondition", 1, ((BundleRevisions) bundleA.adapt(BundleRevisions.class)).getRevisions().size());
          bundleA.update(new ByteArrayInputStream(baos.toByteArray()));
          assertEquals("There should be 2 revisions now", 2, ((BundleRevisions) bundleA.adapt(BundleRevisions.class)).getRevisions().size());
          assertEquals("No refresh called, the bundle wiring for B should still be the old one",
                  bw, bundleB.adapt(BundleWiring.class));
  
          FrameworkMBean framework = getMBean(FrameworkMBean.OBJECTNAME, FrameworkMBean.class);
          CompositeData result = framework.refreshBundlesAndWait(new long[] {bundleB.getBundleId()});
          assertTrue((Boolean) result.get(FrameworkMBean.SUCCESS));
          assertTrue(Arrays.equals(new Long[] {bundleB.getBundleId()}, (Long []) result.get(FrameworkMBean.COMPLETED)));
  
          List<BundleWire> requiredWires = ((BundleWiring) bundleB.adapt(BundleWiring.class)).getRequiredWires(BundleRevision.PACKAGE_NAMESPACE);
          assertEquals(2, requiredWires.size());
          List<String> imported = new ArrayList<String>();
          for (BundleWire w : requiredWires) {
              Map<String, Object> ca = w.getCapability().getAttributes();
              assertEquals(bundleA.getSymbolicName(), ca.get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE));
              imported.add(ca.get(BundleRevision.PACKAGE_NAMESPACE).toString());
  
              if ("org.apache.aries.jmx.test.bundlea.impl".equals(ca.get(BundleRevision.PACKAGE_NAMESPACE))) {
                  // Came across an issue where equinox was reporting the other package as still coming from from the 1.0 bundle
                  // not sure if this is a bug or not...
                  assertEquals(new Version("1.1"), ca.get(Constants.BUNDLE_VERSION_ATTRIBUTE));
              }
          }
          assertEquals(Arrays.asList("org.apache.aries.jmx.test.bundlea.api", "org.apache.aries.jmx.test.bundlea.impl"), imported);
      }
  
      @Test
      public void testRefreshBundlesAndWait2() throws Exception {
          Bundle bundleA = getBundleByName("org.apache.aries.jmx.test.bundlea");
          Bundle bundleB = getBundleByName("org.apache.aries.jmx.test.bundleb");
  
          BundleWiring bw = (BundleWiring) bundleB.adapt(BundleWiring.class);
  
          List<BundleWire> initialRequiredWires = bw.getRequiredWires(BundleRevision.PACKAGE_NAMESPACE);
          assertEquals(1, initialRequiredWires.size());
          BundleWire wire = initialRequiredWires.get(0);
          Map<String, Object> capabilityAttributes = wire.getCapability().getAttributes();
          assertEquals("Precondition", bundleA.getSymbolicName(), capabilityAttributes.get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE));
          assertEquals("Precondition", new Version("1.0"), capabilityAttributes.get(Constants.BUNDLE_VERSION_ATTRIBUTE));
          assertEquals("Precondition", "org.apache.aries.jmx.test.bundlea.api", capabilityAttributes.get(BundleRevision.PACKAGE_NAMESPACE));
  
          // Create an updated version of Bundle A, which an extra export and version 1.1
          Manifest manifest = new Manifest();
          manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
          manifest.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, "org.apache.aries.jmx.test.bundlea");
          manifest.getMainAttributes().putValue(Constants.BUNDLE_VERSION, "1.1");
          manifest.getMainAttributes().putValue(Constants.EXPORT_PACKAGE, "org.apache.aries.jmx.test.bundlea.api,org.apache.aries.jmx.test.bundlea.impl");
  
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          JarOutputStream jos = new JarOutputStream(baos, manifest);
          addResourceToJar("org/apache/aries/jmx/test/bundlea/api/InterfaceA.class", jos, bundleA);
          addResourceToJar("org/apache/aries/jmx/test/bundlea/impl/A2.class", jos, bundleA);
          jos.close();
  
          assertEquals("Precondition", 1, ((BundleRevisions) bundleA.adapt(BundleRevisions.class)).getRevisions().size());
          bundleA.update(new ByteArrayInputStream(baos.toByteArray()));
          assertEquals("There should be 2 revisions now", 2, ((BundleRevisions) bundleA.adapt(BundleRevisions.class)).getRevisions().size());
          assertEquals("No refresh called, the bundle wiring for B should still be the old one",
                  bw, ((BundleWiring) bundleB.adapt(BundleWiring.class)));
  
          FrameworkMBean framework = getMBean(FrameworkMBean.OBJECTNAME, FrameworkMBean.class);
          CompositeData result = framework.refreshBundlesAndWait(null);
          Set<Long> completed = new HashSet<Long>(Arrays.asList((Long []) result.get(FrameworkMBean.COMPLETED)));
          assertTrue(completed.contains(bundleA.getBundleId()));
          assertTrue(completed.contains(bundleB.getBundleId()));
  
          List<BundleWire> requiredWires = ((BundleWiring) bundleB.adapt(BundleWiring.class)).getRequiredWires(BundleRevision.PACKAGE_NAMESPACE);
          assertEquals(2, requiredWires.size());
          List<String> imported = new ArrayList<String>();
          for (BundleWire w : requiredWires) {
              Map<String, Object> ca = w.getCapability().getAttributes();
              assertEquals(bundleA.getSymbolicName(), ca.get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE));
              imported.add(ca.get(BundleRevision.PACKAGE_NAMESPACE).toString());
  
              if ("org.apache.aries.jmx.test.bundlea.impl".equals(ca.get(BundleRevision.PACKAGE_NAMESPACE))) {
                  // Came across an issue where equinox was reporting the other package as still coming from from the 1.0 bundle
                  // not sure if this is a bug or not...
                  assertEquals(new Version("1.1"), ca.get(Constants.BUNDLE_VERSION_ATTRIBUTE));
              }
          }
          assertEquals(Arrays.asList("org.apache.aries.jmx.test.bundlea.api", "org.apache.aries.jmx.test.bundlea.impl"), imported);
      }
  
      private void addResourceToJar(String resourceName, JarOutputStream jos, Bundle bundle) throws IOException {
          InputStream intfIs = bundle.getResource("/" + resourceName).openStream();
          JarEntry entry = new JarEntry(resourceName);
          jos.putNextEntry(entry);
          try {
              Streams.pump(intfIs, jos);
          } finally {
              jos.closeEntry();
          }
      }
  
      @Test
      public void testMBeanInterface() throws IOException {
          long[] bundleIds = new long[]{1,2};
          int[] newlevels = new int[]{1,1};
          CompositeData compData = framework.setBundleStartLevels(bundleIds, newlevels);
          assertNotNull(compData);
  
          BatchActionResult batch2 = BatchActionResult.from(compData);
          assertNotNull(batch2.getCompleted());
          assertTrue(batch2.isSuccess());
          assertNull(batch2.getError());
          assertNull(batch2.getRemainingItems());
  
          File file = File.createTempFile("bundletest", ".jar");
          file.deleteOnExit();
          Manifest man = new Manifest();
          man.getMainAttributes().putValue("Manifest-Version", "1.0");
          JarOutputStream jaros = new JarOutputStream(new FileOutputStream(file), man);
          jaros.flush();
          jaros.close();
  
          long bundleId = 0;
          try {
              bundleId = framework.installBundleFromURL(file.getAbsolutePath(), file.toURI().toString());
          } catch (Exception e) {
              fail("Installation of test bundle shouldn't fail");
          }
  
          try{
              framework.uninstallBundle(bundleId);
          } catch (Exception e) {
              fail("Uninstallation of test bundle shouldn't fail");
          }
      }
  }