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.codec;
  
  import static org.apache.aries.jmx.util.FrameworkUtils.getBundleDependencies;
  import static org.apache.aries.jmx.util.FrameworkUtils.getBundleExportedPackages;
  import static org.apache.aries.jmx.util.FrameworkUtils.getBundleImportedPackages;
  import static org.apache.aries.jmx.util.FrameworkUtils.getBundleState;
  import static org.apache.aries.jmx.util.FrameworkUtils.getDependentBundles;
  import static org.apache.aries.jmx.util.FrameworkUtils.getFragmentIds;
  import static org.apache.aries.jmx.util.FrameworkUtils.getHostIds;
  import static org.apache.aries.jmx.util.FrameworkUtils.getRegisteredServiceIds;
  import static org.apache.aries.jmx.util.FrameworkUtils.getServicesInUseByBundle;
  import static org.apache.aries.jmx.util.FrameworkUtils.isBundlePendingRemoval;
  import static org.apache.aries.jmx.util.FrameworkUtils.isBundleRequiredByOthers;
  import static org.apache.aries.jmx.util.TypeUtils.toLong;
  import static org.apache.aries.jmx.util.TypeUtils.toPrimitive;
  import static org.osgi.jmx.framework.BundleStateMBean.BUNDLE_TYPE;
  import static org.osgi.jmx.framework.BundleStateMBean.EXPORTED_PACKAGES;
  import static org.osgi.jmx.framework.BundleStateMBean.FRAGMENT;
  import static org.osgi.jmx.framework.BundleStateMBean.FRAGMENTS;
  import static org.osgi.jmx.framework.BundleStateMBean.HEADERS;
  import static org.osgi.jmx.framework.BundleStateMBean.HEADERS_TYPE;
  import static org.osgi.jmx.framework.BundleStateMBean.HEADER_TYPE;
  import static org.osgi.jmx.framework.BundleStateMBean.HOSTS;
  import static org.osgi.jmx.framework.BundleStateMBean.IDENTIFIER;
  import static org.osgi.jmx.framework.BundleStateMBean.IMPORTED_PACKAGES;
  import static org.osgi.jmx.framework.BundleStateMBean.KEY;
  import static org.osgi.jmx.framework.BundleStateMBean.LAST_MODIFIED;
  import static org.osgi.jmx.framework.BundleStateMBean.LOCATION;
  import static org.osgi.jmx.framework.BundleStateMBean.PERSISTENTLY_STARTED;
  import static org.osgi.jmx.framework.BundleStateMBean.REGISTERED_SERVICES;
  import static org.osgi.jmx.framework.BundleStateMBean.REMOVAL_PENDING;
  import static org.osgi.jmx.framework.BundleStateMBean.REQUIRED;
  import static org.osgi.jmx.framework.BundleStateMBean.REQUIRED_BUNDLES;
  import static org.osgi.jmx.framework.BundleStateMBean.REQUIRING_BUNDLES;
  import static org.osgi.jmx.framework.BundleStateMBean.SERVICES_IN_USE;
  import static org.osgi.jmx.framework.BundleStateMBean.START_LEVEL;
  import static org.osgi.jmx.framework.BundleStateMBean.STATE;
  import static org.osgi.jmx.framework.BundleStateMBean.SYMBOLIC_NAME;
  import static org.osgi.jmx.framework.BundleStateMBean.VALUE;
  import static org.osgi.jmx.framework.BundleStateMBean.VERSION;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Dictionary;
  import java.util.Enumeration;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import javax.management.JMRuntimeException;
  import javax.management.openmbean.CompositeData;
  import javax.management.openmbean.CompositeDataSupport;
  import javax.management.openmbean.OpenDataException;
  import javax.management.openmbean.TabularData;
  import javax.management.openmbean.TabularDataSupport;
  
  import org.osgi.framework.Bundle;
  import org.osgi.framework.BundleContext;
  import org.osgi.jmx.framework.BundleStateMBean;
  import org.osgi.service.packageadmin.PackageAdmin;
  import org.osgi.service.startlevel.StartLevel;
  
  /**
   * <p>
   * <tt>BundleData</tt> represents BundleData Type @see {@link BundleStateMBean#BUNDLE_TYPE}. It is a codec for the
   * <code>CompositeData</code> representing an OSGi BundleData.
   * </p>
   *
   * @version $Rev$ $Date$
   */
  @SuppressWarnings("deprecation")
  public class BundleData {
  
      /**
       * @see BundleStateMBean#EXPORTED_PACKAGES_ITEM
       */
      private String[] exportedPackages;
  
      /**
       * @see BundleStateMBean#FRAGMENT_ITEM
       */
      private boolean fragment;
  
      /**
       * @see BundleStateMBean#FRAGMENTS_ITEM
       */
      private long[] fragments;
  
      /**
       * @see BundleStateMBean#HEADER_TYPE
       */
      private List<Header> headers = new ArrayList<Header>();
  
      /**
       * @see BundleStateMBean#HOSTS_ITEM
       */
      private long[] hosts;
  
      /**
       * @see BundleStateMBean#IDENTIFIER_ITEM
       */
      private long identifier;
  
      /**
       * @see BundleStateMBean#IMPORTED_PACKAGES_ITEM
       */
      private String[] importedPackages;
  
      /**
       * @see BundleStateMBean#LAST_MODIFIED_ITEM
       */
      private long lastModified;
  
      /**
       * @see BundleStateMBean#LOCATION_ITEM
       */
      private String location;
  
      /**
       * @see BundleStateMBean#PERSISTENTLY_STARTED_ITEM
       */
      private boolean persistentlyStarted;
  
      /**
       * @see BundleStateMBean#REGISTERED_SERVICES_ITEM
       */
      private long[] registeredServices;
  
      /**
       * @see BundleStateMBean#REMOVAL_PENDING_ITEM
       */
      private boolean removalPending;
  
      /**
       * @see BundleStateMBean#REQUIRED_ITEM
       */
      private boolean required;
  
      /**
       * @see BundleStateMBean#REQUIRED_BUNDLES_ITEM
       */
      private long[] requiredBundles;
  
      /**
       * @see BundleStateMBean#REQUIRING_BUNDLES_ITEM
       */
      private long[] requiringBundles;
  
      /**
       * @see BundleStateMBean#SERVICES_IN_USE_ITEM
       */
      private long[] servicesInUse;
  
      /**
       * @see BundleStateMBean#START_LEVEL_ITEM
       */
      private int bundleStartLevel;
  
      /**
       * @see BundleStateMBean#STATE_ITEM
       */
      private String state;
  
      /**
       * @see BundleStateMBean#SYMBOLIC_NAME_ITEM
       */
      private String symbolicName;
  
      /**
       * @see BundleStateMBean#VERSION_ITEM
       */
      private String version;
  
      private BundleData() {
          super();
      }
  
      public BundleData(BundleContext localBundleContext, Bundle bundle, PackageAdmin packageAdmin, StartLevel startLevel) {
          if (bundle == null) {
              throw new IllegalArgumentException("Argument bundle cannot be null");
          }
          if (packageAdmin == null || startLevel == null) {
              throw new IllegalArgumentException("Arguments PackageAdmin / startLevel cannot be null");
          }
          this.exportedPackages = getBundleExportedPackages(bundle, packageAdmin);
          this.fragment = (PackageAdmin.BUNDLE_TYPE_FRAGMENT == packageAdmin.getBundleType(bundle));
          this.fragments = getFragmentIds(bundle, packageAdmin);
          Dictionary<String, String> bundleHeaders = bundle.getHeaders();
          Enumeration<String> keys = bundleHeaders.keys();
          while (keys.hasMoreElements()) {
              String key = keys.nextElement();
              headers.add(new Header(key, bundleHeaders.get(key)));
          }
          this.hosts = getHostIds(bundle, packageAdmin);
          this.identifier = bundle.getBundleId();
          this.importedPackages = getBundleImportedPackages(localBundleContext, bundle, packageAdmin);
          this.lastModified = bundle.getLastModified();
          this.location = bundle.getLocation();
          this.persistentlyStarted = startLevel.isBundlePersistentlyStarted(bundle);
          this.registeredServices = getRegisteredServiceIds(bundle);
          this.removalPending = isBundlePendingRemoval(bundle, packageAdmin);
          this.required = isBundleRequiredByOthers(bundle, packageAdmin);
          this.requiredBundles = getBundleDependencies(localBundleContext, bundle, packageAdmin);
          this.requiringBundles = getDependentBundles(bundle, packageAdmin);
          this.servicesInUse = getServicesInUseByBundle(bundle);
          this.bundleStartLevel = startLevel.getBundleStartLevel(bundle);
          this.state = getBundleState(bundle);
          this.symbolicName = bundle.getSymbolicName();
          this.version = bundle.getVersion().toString();
      }
  
      /**
       * Returns CompositeData representing a BundleData complete state typed by {@link BundleStateMBean#BUNDLE_TYPE}
       *
       * @return
       */
      public CompositeData toCompositeData() {
          return toCompositeData(BundleStateMBean.BUNDLE_TYPE.keySet());
      }
  
      public CompositeData toCompositeData(Collection<String> itemNames) {
          Map<String, Object> items = new HashMap<String, Object>();
          items.put(IDENTIFIER, this.identifier);
  
          if (itemNames.contains(EXPORTED_PACKAGES))
              items.put(EXPORTED_PACKAGES, this.exportedPackages);
  
          if (itemNames.contains(FRAGMENT))
              items.put(FRAGMENT, this.fragment);
  
          if (itemNames.contains(FRAGMENTS))
              items.put(FRAGMENTS, toLong(this.fragments));
  
          if (itemNames.contains(HOSTS))
              items.put(HOSTS, toLong(this.hosts));
  
          if (itemNames.contains(IMPORTED_PACKAGES))
              items.put(IMPORTED_PACKAGES, this.importedPackages);
  
          if (itemNames.contains(LAST_MODIFIED))
              items.put(LAST_MODIFIED, this.lastModified);
  
          if (itemNames.contains(LOCATION))
              items.put(LOCATION, this.location);
  
          if (itemNames.contains(PERSISTENTLY_STARTED))
              items.put(PERSISTENTLY_STARTED, this.persistentlyStarted);
  
          if (itemNames.contains(REGISTERED_SERVICES))
              items.put(REGISTERED_SERVICES, toLong(this.registeredServices));
  
          if (itemNames.contains(REMOVAL_PENDING))
              items.put(REMOVAL_PENDING, this.removalPending);
  
          if (itemNames.contains(REQUIRED))
              items.put(REQUIRED, this.required);
  
          if (itemNames.contains(REQUIRED_BUNDLES))
              items.put(REQUIRED_BUNDLES, toLong(this.requiredBundles));
  
          if (itemNames.contains(REQUIRING_BUNDLES))
              items.put(REQUIRING_BUNDLES, toLong(this.requiringBundles));
  
          if (itemNames.contains(SERVICES_IN_USE))
              items.put(SERVICES_IN_USE, toLong(this.servicesInUse));
  
          if (itemNames.contains(START_LEVEL))
              items.put(START_LEVEL, this.bundleStartLevel);
  
          if (itemNames.contains(STATE))
              items.put(STATE, this.state);
  
          if (itemNames.contains(SYMBOLIC_NAME))
              items.put(SYMBOLIC_NAME, this.symbolicName);
  
          if (itemNames.contains(VERSION))
              items.put(VERSION, this.version);
  
          if (itemNames.contains(HEADERS)) {
              TabularData headerTable = new TabularDataSupport(HEADERS_TYPE);
              for (Header header : this.headers) {
                  headerTable.put(header.toCompositeData());
              }
              items.put(HEADERS, headerTable);
          }
  
          String[] allItemNames = BUNDLE_TYPE.keySet().toArray(new String [] {});
          Object[] itemValues = new Object[allItemNames.length];
          for (int i=0; i < allItemNames.length; i++) {
              itemValues[i] = items.get(allItemNames[i]);
          }
  
          try {
              return new CompositeDataSupport(BUNDLE_TYPE, allItemNames, itemValues);
          } catch (OpenDataException e) {
              throw new IllegalStateException("Failed to create CompositeData for BundleData [" + this.identifier
                      + "]", e);
          }
      }
  
      /**
       * Constructs a <code>BundleData</code> object from the given <code>CompositeData</code>
       *
       * @param compositeData
       * @return
       * @throws IlleglArgumentException
       *             if compositeData is null or not of type {@link BundleStateMBean#BUNDLE_TYPE}
       */
      @SuppressWarnings("unchecked")
      public static BundleData from(CompositeData compositeData) throws IllegalArgumentException {
          if (compositeData == null) {
              throw new IllegalArgumentException("Argument compositeData cannot be null");
          }
          if (!compositeData.getCompositeType().equals(BUNDLE_TYPE)) {
              throw new IllegalArgumentException("Invalid CompositeType [" + compositeData.getCompositeType() + "]");
          }
          BundleData bundleData = new BundleData();
          bundleData.exportedPackages = (String[]) compositeData.get(EXPORTED_PACKAGES);
          bundleData.fragment = (Boolean) compositeData.get(FRAGMENT);
          bundleData.fragments = toPrimitive((Long[]) compositeData.get(FRAGMENTS));
          bundleData.hosts = toPrimitive((Long[]) compositeData.get(HOSTS));
          bundleData.identifier = (Long) compositeData.get(IDENTIFIER);
          bundleData.importedPackages = (String[]) compositeData.get(IMPORTED_PACKAGES);
          bundleData.lastModified = (Long) compositeData.get(LAST_MODIFIED);
          bundleData.location = (String) compositeData.get(LOCATION);
          bundleData.persistentlyStarted = (Boolean) compositeData.get(PERSISTENTLY_STARTED);
          bundleData.registeredServices = toPrimitive((Long[]) compositeData.get(REGISTERED_SERVICES));
          bundleData.removalPending = (Boolean) compositeData.get(REMOVAL_PENDING);
          bundleData.required = (Boolean) compositeData.get(REQUIRED);
          bundleData.requiredBundles = toPrimitive((Long[]) compositeData.get(REQUIRED_BUNDLES));
          bundleData.requiringBundles = toPrimitive((Long[]) compositeData.get(REQUIRING_BUNDLES));
          bundleData.servicesInUse = toPrimitive((Long[]) compositeData.get(SERVICES_IN_USE));
          bundleData.bundleStartLevel = (Integer) compositeData.get(START_LEVEL);
          bundleData.state = (String) compositeData.get(STATE);
          bundleData.symbolicName = (String) compositeData.get(SYMBOLIC_NAME);
          bundleData.version = (String) compositeData.get(VERSION);
          TabularData headerTable = (TabularData) compositeData.get(HEADERS);
          Collection<CompositeData> headerData = (Collection<CompositeData>) headerTable.values();
          for (CompositeData headerRow : headerData) {
              bundleData.headers.add(Header.from(headerRow));
          }
          return bundleData;
      }
  
      public String[] getExportedPackages() {
          return exportedPackages;
      }
  
      public boolean isFragment() {
          return fragment;
      }
  
      public long[] getFragments() {
          return fragments;
      }
  
      public List<Header> getHeaders() {
          return headers;
      }
  
      public long[] getHosts() {
          return hosts;
      }
  
      public long getIdentifier() {
          return identifier;
      }
  
      public String[] getImportedPackages() {
          return importedPackages;
      }
  
      public long getLastModified() {
          return lastModified;
      }
  
      public String getLocation() {
          return location;
      }
  
      public boolean isPersistentlyStarted() {
          return persistentlyStarted;
      }
  
      public long[] getRegisteredServices() {
          return registeredServices;
      }
  
      public boolean isRemovalPending() {
          return removalPending;
      }
  
      public boolean isRequired() {
          return required;
      }
  
      public long[] getRequiredBundles() {
          return requiredBundles;
      }
  
      public long[] getRequiringBundles() {
          return requiringBundles;
      }
  
      public long[] getServicesInUse() {
          return servicesInUse;
      }
  
      public int getBundleStartLevel() {
          return bundleStartLevel;
      }
  
      public String getState() {
          return state;
      }
  
      public String getSymbolicName() {
          return symbolicName;
      }
  
      public String getVersion() {
          return version;
      }
  
      /*
       * Represents key/value pair in BundleData headers
       */
      public static class Header {
  
          private String key;
          private String value;
  
          public String getKey() {
              return key;
          }
  
          public String getValue() {
              return value;
          }
  
          private Header() {
              super();
          }
  
          public Header(String key, String value) {
              this.key = key;
              this.value = value;
          }
  
          public CompositeData toCompositeData() throws JMRuntimeException {
              CompositeData result = null;
              Map<String, Object> items = new HashMap<String, Object>();
              items.put(KEY, key);
              items.put(VALUE, value);
              try {
                  result = new CompositeDataSupport(HEADER_TYPE, items);
              } catch (OpenDataException e) {
                  throw new JMRuntimeException("Failed to create CompositeData for header [" + key + ":" + value + "] - "
                          + e.getMessage());
              }
              return result;
          }
  
          public static Header from(CompositeData compositeData) {
              if (compositeData == null) {
                  throw new IllegalArgumentException("Argument compositeData cannot be null");
              }
              if (!compositeData.getCompositeType().equals(HEADER_TYPE)) {
                  throw new IllegalArgumentException("Invalid CompositeType [" + compositeData.getCompositeType() + "]");
              }
              Header header = new Header();
              header.key = (String) compositeData.get(KEY);
              header.value = (String) compositeData.get(VALUE);
              return header;
          }
      }
  }
  