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.wiring;
  
  import java.io.IOException;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import javax.management.openmbean.CompositeData;
  import javax.management.openmbean.TabularData;
  import javax.management.openmbean.TabularDataSupport;
  
  import org.apache.aries.jmx.Logger;
  import org.apache.aries.jmx.codec.BundleWiringData;
  import org.apache.aries.jmx.util.FrameworkUtils;
  import org.osgi.framework.Bundle;
  import org.osgi.framework.BundleContext;
  import org.osgi.framework.wiring.BundleCapability;
  import org.osgi.framework.wiring.BundleRequirement;
  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.jmx.framework.wiring.BundleWiringStateMBean;
  
  public class BundleWiringState implements BundleWiringStateMBean {
      private final BundleContext bundleContext;
      private final Logger logger;
  
  
      public BundleWiringState(BundleContext bundleContext, Logger logger) {
          this.bundleContext = bundleContext;
          this.logger = logger;
      }
  
      /* (non-Javadoc)
       * @see org.osgi.jmx.framework.BundleRevisionsStateMBean#getCurrentRevisionDeclaredRequirements(long, java.lang.String)
       */
      public CompositeData[] getCurrentRevisionDeclaredRequirements(long bundleId, String namespace) throws IOException {
          Bundle bundle = FrameworkUtils.resolveBundle(bundleContext, bundleId);
          BundleRevision revision = bundle.adapt(BundleRevision.class);
  
          return BundleWiringData.getRequirementsCompositeData(revision.getDeclaredRequirements(namespace));
      }
  
      /* (non-Javadoc)
       * @see org.osgi.jmx.framework.BundleRevisionsStateMBean#getCurrentRevisionDeclaredCapabilities(long, java.lang.String)
       */
      public CompositeData[] getCurrentRevisionDeclaredCapabilities(long bundleId, String namespace) throws IOException {
          Bundle bundle = FrameworkUtils.resolveBundle(bundleContext, bundleId);
          BundleRevision revision = bundle.adapt(BundleRevision.class);
  
          return BundleWiringData.getCapabilitiesCompositeData(revision.getDeclaredCapabilities(namespace));
      }
  
      /* (non-Javadoc)
       * @see org.osgi.jmx.framework.BundleRevisionsStateMBean#getCurrentWiring(long, java.lang.String)
       */
      public CompositeData getCurrentWiring(long bundleId, String namespace) throws IOException {
          Bundle bundle = FrameworkUtils.resolveBundle(bundleContext, bundleId);
          BundleRevision currentRevision = bundle.adapt(BundleRevision.class);
          Map<BundleRevision, Integer> revisionIDMap = getCurrentRevisionTransitiveRevisionsClosure(bundleId, namespace);
          return getRevisionWiring(currentRevision, 0, namespace, revisionIDMap);
      }
  
      /* (non-Javadoc)
       * @see org.osgi.jmx.framework.BundleRevisionsStateMBean#getCurrentWiringClosure(long)
       */
      public TabularData getCurrentWiringClosure(long rootBundleId, String namespace) throws IOException {
          Map<BundleRevision, Integer> revisionIDMap = getCurrentRevisionTransitiveRevisionsClosure(rootBundleId, namespace);
  
          TabularData td = new TabularDataSupport(BundleWiringStateMBean.BUNDLES_WIRING_TYPE);
          for (Map.Entry<BundleRevision, Integer> entry : revisionIDMap.entrySet()) {
              td.put(getRevisionWiring(entry.getKey(), entry.getValue(), namespace, revisionIDMap));
          }
  
          return td;
      }
  
      // The current revision being passed in always gets assigned revision ID 0
      // All the other revision IDs unique, but don't increase monotonous.
      private Map<BundleRevision, Integer> getCurrentRevisionTransitiveRevisionsClosure(long rootBundleId, String namespace) throws IOException {
          Bundle rootBundle = FrameworkUtils.resolveBundle(bundleContext, rootBundleId);
          BundleRevision rootRevision = rootBundle.adapt(BundleRevision.class);
          return getRevisionTransitiveClosure(rootRevision, namespace);
      }
  
      private Map<BundleRevision, Integer> getRevisionTransitiveClosure(BundleRevision rootRevision, String namespace) {
          Map<BundleRevision, Integer> revisionIDMap = new HashMap<BundleRevision, Integer>();
          populateTransitiveRevisions(namespace, rootRevision, revisionIDMap);
  
          // Set the root revision ID to 0,
          // TODO check if there is already a revision with ID 0 and if so swap them. Quite a small chance that this will be needed
          revisionIDMap.put(rootRevision, 0);
          return revisionIDMap;
      }
  
      private void populateTransitiveRevisions(String namespace, BundleRevision rootRevision, Map<BundleRevision, Integer> allRevisions) {
          allRevisions.put(rootRevision, System.identityHashCode(rootRevision));
          BundleWiring wiring = rootRevision.getWiring();
          if (wiring == null)
              return;
  
          List<BundleWire> requiredWires = wiring.getRequiredWires(namespace);
          for (BundleWire wire : requiredWires) {
              BundleRevision revision = wire.getCapability().getRevision();
              if (!allRevisions.containsKey(revision)) {
                  populateTransitiveRevisions(namespace, revision, allRevisions);
              }
          }
  
          List<BundleWire> providedWires = wiring.getProvidedWires(namespace);
          for (BundleWire wire : providedWires) {
              BundleRevision revision = wire.getRequirement().getRevision();
              if (!allRevisions.containsKey(revision)) {
                  populateTransitiveRevisions(namespace, revision, allRevisions);
              }
          }
      }
  
      private CompositeData getRevisionWiring(BundleRevision revision, int revisionID, String namespace, Map<BundleRevision, Integer> revisionIDMap) {
          BundleWiring wiring = revision.getWiring();
          List<BundleCapability> capabilities = wiring.getCapabilities(namespace);
          List<BundleRequirement> requirements = wiring.getRequirements(namespace);
          List<BundleWire> providedWires = wiring.getProvidedWires(namespace);
          List<BundleWire> requiredWires = wiring.getRequiredWires(namespace);
  
          BundleWiringData data = new BundleWiringData(wiring.getBundle().getBundleId(), revisionID, capabilities, requirements, providedWires, requiredWires, revisionIDMap);
          return data.toCompositeData();
      }
  
      /* (non-Javadoc)
       * @see org.osgi.jmx.framework.BundleRevisionsStateMBean#getRevisionsDeclaredRequirements(long, java.lang.String, boolean)
       */
      public TabularData getRevisionsDeclaredRequirements(long bundleId, String namespace) throws IOException {
          Bundle bundle = FrameworkUtils.resolveBundle(bundleContext, bundleId);
          BundleRevisions revisions = bundle.adapt(BundleRevisions.class);
  
          TabularData td = new TabularDataSupport(BundleWiringStateMBean.REVISIONS_REQUIREMENTS_TYPE);
          for (BundleRevision revision : revisions.getRevisions()) {
              td.put(BundleWiringData.getRevisionRequirements(
                      System.identityHashCode(revision),
                      revision.getDeclaredRequirements(namespace)));
          }
          return td;
      }
  
      /* (non-Javadoc)
       * @see org.osgi.jmx.framework.BundleRevisionsStateMBean#getRevisionsDeclaredCapabilities(long, java.lang.String, boolean)
       */
      public TabularData getRevisionsDeclaredCapabilities(long bundleId, String namespace) throws IOException {
          Bundle bundle = FrameworkUtils.resolveBundle(bundleContext, bundleId);
          BundleRevisions revisions = bundle.adapt(BundleRevisions.class);
  
          TabularData td = new TabularDataSupport(BundleWiringStateMBean.REVISIONS_CAPABILITIES_TYPE);
          for (BundleRevision revision : revisions.getRevisions()) {
              td.put(BundleWiringData.getRevisionCapabilities(
                      System.identityHashCode(revision),
                      revision.getDeclaredCapabilities(namespace)));
          }
          return td;
      }
  
      /* (non-Javadoc)
       * @see org.osgi.jmx.framework.BundleRevisionsStateMBean#getRevisionsWiring(long, java.lang.String)
       */
      public TabularData getRevisionsWiring(long bundleId, String namespace) throws IOException {
          Bundle bundle = FrameworkUtils.resolveBundle(bundleContext, bundleId);
          BundleRevisions revisions = bundle.adapt(BundleRevisions.class);
  
          TabularData td = new TabularDataSupport(BundleWiringStateMBean.BUNDLES_WIRING_TYPE);
          for (BundleRevision revision : revisions.getRevisions()) {
              Map<BundleRevision, Integer> revisionIDMap = getRevisionTransitiveClosure(revision, namespace);
              td.put(getRevisionWiring(revision, System.identityHashCode(revision), namespace, revisionIDMap));
          }
          return td;
      }
  
      /* (non-Javadoc)
       * @see org.osgi.jmx.framework.BundleRevisionsStateMBean#getWiringClosure(long, java.lang.String)
       */
      public TabularData getRevisionsWiringClosure(long rootBundleId, String namespace) throws IOException {
          Bundle bundle = FrameworkUtils.resolveBundle(bundleContext, rootBundleId);
          BundleRevisions revisions = bundle.adapt(BundleRevisions.class);
  
          Map<BundleRevision, Integer> revisionIDMap = new HashMap<BundleRevision, Integer>();
          for (BundleRevision revision : revisions.getRevisions()) {
              populateTransitiveRevisions(namespace, revision, revisionIDMap);
          }
  
          // Set the root current revision ID to 0,
          // TODO check if there is already a revision with ID 0 and if so swap them. Quite a small chance that this will be needed
          BundleRevision revision = bundle.adapt(BundleRevision.class);
          revisionIDMap.put(revision, 0);
  
          TabularData td = new TabularDataSupport(BundleWiringStateMBean.BUNDLES_WIRING_TYPE);
          for (Map.Entry<BundleRevision, Integer> entry : revisionIDMap.entrySet()) {
              td.put(getRevisionWiring(entry.getKey(), entry.getValue(), namespace, revisionIDMap));
          }
  
          return td;
      }
  }
  