blob: b452e5acbd65bab16a9211b72d5ac6ebb08ee147 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 Oracle.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* Hal Hildebrand - Initial JMX support
******************************************************************************/
package org.eclipse.gemini.mgmt.framework.codec;
import static org.eclipse.gemini.mgmt.codec.Util.LongArrayFrom;
import static org.eclipse.gemini.mgmt.codec.Util.getBundleExportedPackages;
import static org.eclipse.gemini.mgmt.codec.Util.getBundleFragments;
import static org.eclipse.gemini.mgmt.codec.Util.getBundleHeaders;
import static org.eclipse.gemini.mgmt.codec.Util.getBundleImportedPackages;
import static org.eclipse.gemini.mgmt.codec.Util.getBundleState;
import static org.eclipse.gemini.mgmt.codec.Util.isBundleFragment;
import static org.eclipse.gemini.mgmt.codec.Util.isBundlePersistentlyStarted;
import static org.eclipse.gemini.mgmt.codec.Util.longArrayFrom;
import static org.eclipse.gemini.mgmt.codec.Util.serviceIds;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.openmbean.CompositeType;
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.Item;
import org.osgi.jmx.framework.BundleStateMBean;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.service.startlevel.StartLevel;
import org.eclipse.gemini.mgmt.codec.Util;
import org.eclipse.gemini.mgmt.framework.CustomBundleStateMBean;
/**
* <p>
* This class represents the CODEC for the composite data representing a single
* OSGi <link>Bundle</link>.
* <p>
* It serves as both the documentation of the type structure and as the
* codification of the mechanism to convert to/from the CompositeData.
* <p>
* The structure of the composite data is:
* <table border="1">
* <tr>
* <td>Location</td>
* <td>String</td>
* </tr>
* <tr>
* <td>Identifier</td>
* <td>long</td>
* </tr>
* <tr>
* <td>SymbolicName</td>
* <td>String</td>
* </tr>
* <tr>
* <td>StartLevel</td>
* <td>int</td>
* </tr>
* <tr>
* <td>State</td>
* <td>String</td>
* </tr>
* <tr>
* <td>LastModified</td>
* <td>long</td>
* </tr>
* <tr>
* <td>PersistentlyStarted</td>
* <td>boolean</td>
* </tr>
* <tr>
* <td>RemovalPending</td>
* <td>boolean</td>
* </tr>
* <tr>
* <td>Required</td>
* <td>boolean</td>
* </tr>
* <tr>
* <td>Fragment</td>
* <td>boolean</td>
* </tr>
* <tr>
* <td>RegisteredServices</td>
* <td>Array of long</td>
* </tr>
* <tr>
* <td>ServicesInUse</td>
* <td>Array of long</td>
* </tr>
* <tr>
* <td>Headers</td>
* <td>TabularData of Key/Value String pairs</td>
* </tr>
* <tr>
* <td>ExportedPackages</td>
* <td>Array of String</td>
* </tr>
* <tr>
* <td>ImportedPackages</td>
* <td>Array of String</td>
* </tr>
* <tr>
* <td>Fragments</td>
* <td>Array of long</td>
* </tr>
* <tr>
* <td>Hosts</td>
* <td>Array of long</td>
* </tr>
* <tr>
* <td>RequiredBundles</td>
* <td>Array of long</td>
* </tr>
* <tr>
* <td>RequiringBundles</td>
* <td>Array of long</td>
* </tr>
* </table>
*/
public class OSGiBundle {
private String[] exportedPackages;
private Boolean fragment;
private long[] fragments;
private Map<String, String> headers;
private long[] hosts;
private long identifier;
private String[] importedPackages;
private long lastModified;
private String location;
private Boolean persistentlyStarted;
private long[] registeredServices;
private Boolean removalPending;
private Boolean required;
private long[] requiredBundles;
private long[] requiringBundles;
private long[] servicesInUse;
private int startLevel;
private String state;
private String symbolicName;
private String version;
private static final String VALUE = "Value";
private static final String KEY = "Key";
private static final String[] HEADER_PROPERTY_ITEM_NAMES = new String[] {KEY, VALUE };
private BundleContext bundleContext;
private PackageAdmin admin;
private StartLevel sl;
private Bundle bundle;
/**
* Construct an OSGiBundle from the encoded CompositeData
*
* @param data
* - the encoded representation of the bundle
*/
@SuppressWarnings("boxing")
public OSGiBundle(CompositeData data) {
this((String) data.get(BundleStateMBean.LOCATION),
((Long) data.get(BundleStateMBean.IDENTIFIER)).longValue(),
(String) data.get(BundleStateMBean.SYMBOLIC_NAME),
(String) data.get(BundleStateMBean.VERSION),
((Integer) data.get(BundleStateMBean.START_LEVEL)).intValue(),
(String) data.get(BundleStateMBean.STATE),
((Long) data.get(BundleStateMBean.LAST_MODIFIED)).longValue(),
(Boolean) data.get(BundleStateMBean.PERSISTENTLY_STARTED),
(Boolean) data.get(BundleStateMBean.REMOVAL_PENDING),
(Boolean) data.get(BundleStateMBean.REQUIRED),
(Boolean) data.get(BundleStateMBean.FRAGMENT),
longArrayFrom((Long[]) data.get(BundleStateMBean.REGISTERED_SERVICES)),
longArrayFrom((Long[]) data.get(BundleStateMBean.SERVICES_IN_USE)),
mapFrom((TabularData) data.get(BundleStateMBean.HEADERS)),
(String[]) data.get(BundleStateMBean.EXPORTED_PACKAGES),
(String[]) data.get(BundleStateMBean.IMPORTED_PACKAGES),
longArrayFrom((Long[]) data.get(BundleStateMBean.FRAGMENTS)),
longArrayFrom((Long[]) data.get(BundleStateMBean.HOSTS)),
longArrayFrom((Long[]) data.get(BundleStateMBean.REQUIRED_BUNDLES)),
longArrayFrom((Long[]) data.get(BundleStateMBean.REQUIRING_BUNDLES)));
}
/**
* Construct an OSGiBundle representation
*
* @param bc
* - the BundleContext to be used.
* @param admin
* - the PackageAdmin service
* @param sl
* - the StartLevel service
* @param b
* - the Bundle to represent
*/
public OSGiBundle(BundleContext bc, PackageAdmin admin, StartLevel sl, Bundle b) {
this.bundleContext = bc;
this.admin = admin;
this.sl = sl;
this.bundle = b;
}
/**
* Construct and OSGiBundle
*
* @param location
* @param identifier
* @param symbolicName
* @param version
* @param startLevel
* @param state
* @param lastModified
* @param persistentlyStarted
* @param removalPending
* @param required
* @param fragment
* @param registeredServices
* @param servicesInUse
* @param headers
* @param exportedPackages
* @param importedPackages
* @param fragments
* @param hosts
* @param requiredBundles
* @param requiringBundles
*/
public OSGiBundle(String location, long identifier, String symbolicName,
String version, int startLevel, String state, long lastModified,
boolean persistentlyStarted, boolean removalPending,
boolean required, boolean fragment, long[] registeredServices,
long[] servicesInUse, Map<String, String> headers,
String[] exportedPackages, String[] importedPackages,
long[] fragments, long[] hosts, long[] requiredBundles,
long[] requiringBundles) {
this.location = location;
this.identifier = identifier;
this.symbolicName = symbolicName;
this.version = version;
this.startLevel = startLevel;
this.state = state;
this.lastModified = lastModified;
this.persistentlyStarted = persistentlyStarted;
this.removalPending = removalPending;
this.required = required;
this.fragment = fragment;
this.registeredServices = registeredServices;
this.servicesInUse = servicesInUse;
this.headers = headers;
this.exportedPackages = exportedPackages;
this.importedPackages = importedPackages;
this.fragments = fragments;
this.hosts = hosts;
this.requiredBundles = requiredBundles;
this.requiringBundles = requiringBundles;
}
/**
* Answer the TabularData representing the list of OSGiBundle state
*
* @param bundles
* - the list of bundles to represent
* @param mask
*
* @return the Tabular data which represents the list of bundles
* @throws IOException
*/
public static TabularData tableFrom(ArrayList<OSGiBundle> bundles, int mask) throws IOException {
TabularDataSupport table = new TabularDataSupport(Item.tabularType("BUNDLES", "A list of bundles",
OSGiBundle.computeBundleType(mask), new String[] { BundleStateMBean.IDENTIFIER }));
for (OSGiBundle bundle : bundles) {
table.put(bundle.asCompositeData(mask));
}
return table;
}
private static CompositeType computeBundleType(int mask) {
List<Item> bundleTypes = new ArrayList<Item>();
if((mask | CustomBundleStateMBean.LOCATION) == mask) {
bundleTypes.add(BundleStateMBean.LOCATION_ITEM);
}
// if((mask | IDENTIFIER) == mask) {
bundleTypes.add(BundleStateMBean.IDENTIFIER_ITEM);
// }
if((mask | CustomBundleStateMBean.SYMBOLIC_NAME) == mask) {
bundleTypes.add(BundleStateMBean.SYMBOLIC_NAME_ITEM);
}
if((mask | CustomBundleStateMBean.VERSION) == mask) {
bundleTypes.add(BundleStateMBean.VERSION_ITEM);
}
if((mask | CustomBundleStateMBean.START_LEVEL) == mask) {
bundleTypes.add(BundleStateMBean.START_LEVEL_ITEM);
}
if((mask | CustomBundleStateMBean.STATE) == mask) {
bundleTypes.add(BundleStateMBean.STATE_ITEM);
}
if((mask | CustomBundleStateMBean.LAST_MODIFIED) == mask) {
bundleTypes.add(BundleStateMBean.LAST_MODIFIED_ITEM);
}
if((mask | CustomBundleStateMBean.PERSISTENTLY_STARTED) == mask) {
bundleTypes.add(BundleStateMBean.PERSISTENTLY_STARTED_ITEM);
}
if((mask | CustomBundleStateMBean.REMOVAL_PENDING) == mask) {
bundleTypes.add(BundleStateMBean.REMOVAL_PENDING_ITEM);
}
if((mask | CustomBundleStateMBean.REQUIRED) == mask) {
bundleTypes.add(BundleStateMBean.REQUIRED_ITEM);
}
if((mask | CustomBundleStateMBean.FRAGMENT) == mask) {
bundleTypes.add(BundleStateMBean.FRAGMENT_ITEM);
}
if((mask | CustomBundleStateMBean.REGISTERED_SERVICES) == mask) {
bundleTypes.add(BundleStateMBean.REGISTERED_SERVICES_ITEM);
}
if((mask | CustomBundleStateMBean.SERVICES_IN_USE) == mask) {
bundleTypes.add(BundleStateMBean.SERVICES_IN_USE_ITEM);
}
if((mask | CustomBundleStateMBean.HEADERS) == mask) {
bundleTypes.add(BundleStateMBean.HEADERS_ITEM);
}
if((mask | CustomBundleStateMBean.EXPORTED_PACKAGES) == mask) {
bundleTypes.add(BundleStateMBean.EXPORTED_PACKAGES_ITEM);
}
if((mask | CustomBundleStateMBean.IMPORTED_PACKAGES) == mask) {
bundleTypes.add(BundleStateMBean.IMPORTED_PACKAGES_ITEM);
}
if((mask | CustomBundleStateMBean.FRAGMENTS) == mask) {
bundleTypes.add(BundleStateMBean.FRAGMENTS_ITEM);
}
if((mask | CustomBundleStateMBean.HOSTS) == mask) {
bundleTypes.add(BundleStateMBean.HOSTS_ITEM);
}
if((mask | CustomBundleStateMBean.REQUIRING_BUNDLES) == mask) {
bundleTypes.add(BundleStateMBean.REQUIRING_BUNDLES_ITEM);
}
if((mask | CustomBundleStateMBean.REQUIRED_BUNDLES) == mask) {
bundleTypes.add(BundleStateMBean.REQUIRED_BUNDLES_ITEM);
}
CompositeType currentCompositeType = Item.compositeType("BUNDLE", "This type encapsulates OSGi bundles",bundleTypes.toArray(new Item[]{}));
return currentCompositeType;
}
/**
* Answer the TabularData representing the list of bundle headers for a
* bundle
*
* @param b
* @return the bundle headers
*/
public static TabularData headerTable(Bundle b) {
TabularDataSupport table = new TabularDataSupport(BundleStateMBean.HEADERS_TYPE);
Dictionary<String, String> map = b.getHeaders();
for (Enumeration<String> headers = map.keys(); headers.hasMoreElements();) {
String key = (String) headers.nextElement();
table.put(headerData(key, (String) map.get(key)));
}
return table;
}
/**
* Answer the TabularData representing the supplied map of bundle headers
*
* @param headers
* @return the bundle headers
*/
public static TabularData headerTable(Map<String, String> headers) {
TabularDataSupport table = new TabularDataSupport(BundleStateMBean.HEADERS_TYPE);
for (Map.Entry<String, String> entry : headers.entrySet()) {
table.put(headerData(entry.getKey(), entry.getValue()));
}
return table;
}
private static CompositeData headerData(String key, String value) {
Object[] itemValues = new Object[HEADER_PROPERTY_ITEM_NAMES.length];
itemValues[0] = key;
itemValues[1] = value;
try {
return new CompositeDataSupport(BundleStateMBean.HEADER_TYPE, HEADER_PROPERTY_ITEM_NAMES, itemValues);
} catch (OpenDataException e) {
throw new IllegalStateException("Cannot form bundle header open data", e);
}
}
/**
* Answer the receiver encoded as CompositeData
* @param mask
*
* @return the CompositeData encoding of the receiver.
* @throws IOException
*/
public CompositeData asCompositeData(int mask) throws IOException {
Map<String, Object> items = new HashMap<String, Object>();
if((mask | CustomBundleStateMBean.LOCATION) == mask) {
items.put(BundleStateMBean.LOCATION, getLocation());
}
// if((mask | IDENTIFIER) == mask) {
items.put(BundleStateMBean.IDENTIFIER, getIdentifier());
// }
if((mask | CustomBundleStateMBean.SYMBOLIC_NAME) == mask) {
items.put(BundleStateMBean.SYMBOLIC_NAME, getSymbolicName());
}
if((mask | CustomBundleStateMBean.VERSION) == mask) {
items.put(BundleStateMBean.VERSION, getVersion());
}
if((mask | CustomBundleStateMBean.START_LEVEL) == mask) {
items.put(BundleStateMBean.START_LEVEL, getStartLevel());
}
if((mask | CustomBundleStateMBean.STATE) == mask) {
items.put(BundleStateMBean.STATE, getState());
}
if((mask | CustomBundleStateMBean.LAST_MODIFIED) == mask) {
items.put(BundleStateMBean.LAST_MODIFIED, getLastModified());
}
if((mask | CustomBundleStateMBean.PERSISTENTLY_STARTED) == mask) {
items.put(BundleStateMBean.PERSISTENTLY_STARTED, isPersistentlyStarted());
}
if((mask | CustomBundleStateMBean.REMOVAL_PENDING) == mask) {
items.put(BundleStateMBean.REMOVAL_PENDING, isRemovalPending());
}
if((mask | CustomBundleStateMBean.REQUIRED) == mask) {
items.put(BundleStateMBean.REQUIRED, isRequired());
}
if((mask | CustomBundleStateMBean.FRAGMENT) == mask) {
items.put(BundleStateMBean.FRAGMENT, isFragment());
}
if((mask | CustomBundleStateMBean.REGISTERED_SERVICES) == mask) {
items.put(BundleStateMBean.REGISTERED_SERVICES, LongArrayFrom(getRegisteredServices()));
}
if((mask | CustomBundleStateMBean.SERVICES_IN_USE) == mask) {
items.put(BundleStateMBean.SERVICES_IN_USE, LongArrayFrom(getServicesInUse()));
}
if((mask | CustomBundleStateMBean.HEADERS) == mask) {
items.put(BundleStateMBean.HEADERS, headerTable(getHeaders()));
}
if((mask | CustomBundleStateMBean.EXPORTED_PACKAGES) == mask) {
items.put(BundleStateMBean.EXPORTED_PACKAGES, getExportedPackages());
}
if((mask | CustomBundleStateMBean.IMPORTED_PACKAGES) == mask) {
items.put(BundleStateMBean.IMPORTED_PACKAGES, getImportedPackages());
}
if((mask | CustomBundleStateMBean.FRAGMENTS) == mask) {
items.put(BundleStateMBean.FRAGMENTS, LongArrayFrom(getFragments()));
}
if((mask | CustomBundleStateMBean.HOSTS) == mask) {
items.put(BundleStateMBean.HOSTS, LongArrayFrom(getHosts()));
}
if((mask | CustomBundleStateMBean.REQUIRING_BUNDLES) == mask) {
items.put(BundleStateMBean.REQUIRING_BUNDLES, LongArrayFrom(getRequiringBundles()));
}
if((mask | CustomBundleStateMBean.REQUIRED_BUNDLES) == mask) {
items.put(BundleStateMBean.REQUIRED_BUNDLES, LongArrayFrom(getRequiredBundles()));
}
try {
return new CompositeDataSupport(OSGiBundle.computeBundleType(mask), items);
} catch (OpenDataException e) {
throw new IllegalStateException("Cannot form bundle open data", e);
}
}
/**
* @return The list of exported packages by this bundle, in the form of
* <packageName>;<version>
*
*/
public String[] getExportedPackages() {
if (exportedPackages == null) {
exportedPackages = getBundleExportedPackages(bundle, admin);
}
return exportedPackages;
}
/**
* @return the list of identifiers of the bundle fragments which use this
* bundle as a host
*/
public long[] getFragments() {
if (fragments == null) {
fragments = getBundleFragments(bundle, admin);
}
return fragments;
}
/**
* @return the map of headers for this bundle
*/
public Map<String, String> getHeaders() {
if (headers == null) {
headers = getBundleHeaders(bundle);
}
return headers;
}
/**
* @return list of identifiers of the bundles which host this fragment
*/
public long[] getHosts() {
if (hosts == null) {
hosts = Util.bundleIds(admin.getHosts(bundle));
}
return hosts;
}
/**
* @return the identifier of this bundle
*/
public long getIdentifier() {
if (identifier == 0) {
identifier = bundle.getBundleId();
}
return identifier;
}
/**
* @return The list of imported packages by this bundle, in the form of
* <packageName>;<version>
*/
public String[] getImportedPackages() {
if (importedPackages == null) {
importedPackages = getBundleImportedPackages(bundle, bundleContext, admin);
}
return importedPackages;
}
/**
* @return the last modified time of this bundle
*/
public long getLastModified() {
if (lastModified == 0) {
lastModified = bundle.getLastModified();
}
return lastModified;
}
/**
* @return the name of this bundle
*/
public String getLocation() {
if (location == null) {
location = bundle.getLocation();
}
return location;
}
/**
* @return the list of identifiers of the services registered by this bundle
*/
public long[] getRegisteredServices() {
if (registeredServices == null) {
registeredServices = serviceIds(bundle.getRegisteredServices());
}
return registeredServices;
}
/**
* @return the list of identifiers of bundles required by this bundle
* @throws IOException
*/
public long[] getRequiredBundles() throws IOException {
if (requiredBundles == null) {
requiredBundles = Util.getRequiredBundles(bundle.getBundleId() ,bundleContext);
}
return requiredBundles;
}
/**
* @return the list of identifiers of bundles which require this bundle
* @throws IOException
*/
public long[] getRequiringBundles() throws IOException {
if (requiringBundles == null) {
requiringBundles = Util.getRequiringBundles(bundle.getBundleId(), bundleContext);
}
return requiringBundles;
}
/**
* @return the list of identifiers of services in use by this bundle
*/
public long[] getServicesInUse() {
if (servicesInUse == null) {
servicesInUse = serviceIds(bundle.getServicesInUse());
}
return servicesInUse;
}
/**
* @return the start level of this bundle
*/
public int getStartLevel() {
if (startLevel == 0) {
startLevel = sl.getBundleStartLevel(bundle);
}
return startLevel;
}
/**
* @return the state of this bundle
*/
public String getState() {
if (state == null) {
state = getBundleState(bundle);
}
return state;
}
/**
* @return the symbolic name of this bundle
*/
public String getSymbolicName() {
if (symbolicName == null) {
symbolicName = bundle.getSymbolicName();
}
return symbolicName;
}
/**
* @return the version of this bundle
*/
public String getVersion() {
if (version == null) {
version = bundle.getVersion().toString();
}
return version;
}
/**
* @return true if this bundle represents a fragment
*/
public boolean isFragment() {
if (fragment == null) {
fragment = isBundleFragment(bundle, admin);
}
return fragment;
}
/**
* @return true if this bundle is persistently started
*/
public boolean isPersistentlyStarted() {
if (persistentlyStarted == null) {
persistentlyStarted = isBundlePersistentlyStarted(bundle, sl);
}
return persistentlyStarted;
}
/**
* @return true if this bundle is pending removal
*/
public boolean isRemovalPending() {
if (removalPending == null) {
removalPending = Util.isRemovalPending(bundle.getBundleId(), bundleContext);
}
return removalPending;
}
/**
* @return true if this bundle is required
*/
public boolean isRequired() {
if (required == null) {
required = Util.isRequired(bundle.getBundleId(), bundleContext);
}
return required;
}
@SuppressWarnings( { "unchecked", "cast" })
private static Map<String, String> mapFrom(TabularData data) {
Map<String, String> headers = new HashMap<String, String>();
Set<List<?>> keySet = (Set<List<?>>) data.keySet();
for (List<?> key : keySet) {
headers.put((String) key.get(0), (String) data.get(new Object[] { key.get(0) }).get(VALUE));
}
return headers;
}
}