/******************************************************************************* | |
* Copyright (c) 2008, 2017 SAP AG, IBM Corporation and others | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: | |
* SAP AG - initial API and implementation | |
* James Livingston - expose collection utils as API | |
* Andrew Johnson - new Equinox implementation since Kepler/Luna | |
*******************************************************************************/ | |
package org.eclipse.mat.inspections.osgi.model; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.HashMap; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Map.Entry; | |
import java.util.Set; | |
import org.eclipse.mat.SnapshotException; | |
import org.eclipse.mat.inspections.ReferenceQuery; | |
import org.eclipse.mat.inspections.collectionextract.CollectionExtractionUtils; | |
import org.eclipse.mat.inspections.collectionextract.ExtractedCollection; | |
import org.eclipse.mat.inspections.collectionextract.ExtractedMap; | |
import org.eclipse.mat.inspections.osgi.model.BundleDescriptor.Type; | |
import org.eclipse.mat.inspections.osgi.model.eclipse.ConfigurationElement; | |
import org.eclipse.mat.inspections.osgi.model.eclipse.Extension; | |
import org.eclipse.mat.inspections.osgi.model.eclipse.ExtensionPoint; | |
import org.eclipse.mat.internal.MATPlugin; | |
import org.eclipse.mat.internal.Messages; | |
import org.eclipse.mat.snapshot.ISnapshot; | |
import org.eclipse.mat.snapshot.model.Field; | |
import org.eclipse.mat.snapshot.model.IClass; | |
import org.eclipse.mat.snapshot.model.IInstance; | |
import org.eclipse.mat.snapshot.model.IObject; | |
import org.eclipse.mat.snapshot.model.IObjectArray; | |
import org.eclipse.mat.snapshot.model.ObjectReference; | |
import org.eclipse.mat.util.IProgressListener; | |
import org.eclipse.mat.util.MessageUtil; | |
public class EquinoxBundleReader2 implements IBundleReader | |
{ | |
private Map<String, Bundle> bundles = new HashMap<String, Bundle>(); | |
private ISnapshot snapshot; | |
private Map<BundleDescriptor, List<Service>> registeredServices = new HashMap<BundleDescriptor, List<Service>>(); | |
private Map<BundleDescriptor, List<Service>> usedServices = new HashMap<BundleDescriptor, List<Service>>(); | |
private Map<Long, BundleDescriptor> bundleDescriptors = new HashMap<Long, BundleDescriptor>(); | |
// bundle name -> ExtensionPoints | |
private Map<BundleDescriptor, List<ExtensionPoint>> extensionPointsByBundle = new HashMap<BundleDescriptor, List<ExtensionPoint>>(); | |
// bundle name -> Extensions | |
private Map<BundleDescriptor, List<Extension>> extensionsByBundle = new HashMap<BundleDescriptor, List<Extension>>(); | |
// bundle name -> dependencies | |
private Map<BundleDescriptor, List<BundleDescriptor>> bundleDependencies = new HashMap<BundleDescriptor, List<BundleDescriptor>>(); | |
// bundle name -> dependents | |
private Map<BundleDescriptor, List<BundleDescriptor>> bundleDependents = new HashMap<BundleDescriptor, List<BundleDescriptor>>(); | |
private static final int STEPS = 1000000; | |
private int maxWarnings = 100; | |
public EquinoxBundleReader2(ISnapshot snapshot) | |
{ | |
this.snapshot = snapshot; | |
} | |
private enum BundleState | |
{ | |
ACTIVE(Messages.EquinoxBundleReader_State_Active, 4), // | |
INSTALLED(Messages.EquinoxBundleReader_State_Installed, 0), // | |
RESOLVED(Messages.EquinoxBundleReader_State_Resolved, 1), // | |
LAZY_STARTING(Messages.EquinoxBundleReader_State_LazyStarting, 2), // | |
STARTING(Messages.EquinoxBundleReader_State_Starting, 3), // | |
STOPPING(Messages.EquinoxBundleReader_State_Stopping, 5), // | |
UNINSTALLED(Messages.EquinoxBundleReader_State_Uninstalled, -1);// | |
private String label; | |
private int value; | |
private BundleState(String label, int value) | |
{ | |
this.label = label; | |
this.value = value; | |
} | |
public String getLabel() | |
{ | |
return label; | |
} | |
public int getValue() | |
{ | |
return value; | |
} | |
} | |
public OSGiModel readOSGiModel(IProgressListener listener) throws SnapshotException | |
{ | |
listener.beginTask(Messages.EquinoxBundleReader_ProcessListenerBundles, STEPS * 4); | |
List<BundleDescriptor> descriptors = getBundleDescriptors(listener); | |
collectDependencies(listener); | |
List<Service> services = collectServiceInfo(listener); | |
List<ExtensionPoint> extensionPoints = collectExtensionsInfo(listener); | |
OSGiModel model = new OSGiModel(this, descriptors, services, extensionPoints); | |
listener.done(); | |
return model; | |
} | |
private List<BundleDescriptor> getBundleDescriptors(IProgressListener listener) throws SnapshotException | |
{ | |
listener.subTask(Messages.EquinoxBundleReader_ReadingBundles); | |
Collection<IClass> classes = snapshot.getClassesByName( | |
"org.eclipse.osgi.internal.framework.EquinoxBundle", true); //$NON-NLS-1$ | |
List<BundleDescriptor> bundleDescriptors = new ArrayList<BundleDescriptor>(); | |
if (classes == null || classes.isEmpty()) | |
return bundleDescriptors; | |
int nobjs = 0; | |
for (IClass clazz : classes) | |
nobjs += clazz.getNumberOfObjects(); | |
for (IClass clazz : classes) | |
{ | |
int[] objs = clazz.getObjectIds(); | |
for (int i = 0; i < objs.length; i++) | |
{ | |
IInstance obj = (IInstance) snapshot.getObject(objs[i]); | |
if (listener.isCanceled()) | |
throw new IProgressListener.OperationCanceledException(); | |
IObject bundleObject = obj; | |
BundleDescriptor.Type type = BundleDescriptor.Type.BUNDLE; | |
if (isFragment(bundleObject)) | |
{ | |
type = BundleDescriptor.Type.FRAGMENT; | |
} | |
BundleDescriptor descriptor = getBundleDescriptor(bundleObject, type); | |
bundleDescriptors.add(descriptor); | |
listener.worked(STEPS / nobjs); | |
} | |
} | |
return bundleDescriptors; | |
} | |
private boolean isFragment(IObject bundleHostObject) throws SnapshotException | |
{ | |
IObject revs = (IObject) bundleHostObject.resolveValue("module.revisions.revisions");//$NON-NLS-1$ | |
if (revs == null) | |
return false; | |
Iterator<IObject> it1 = CollectionExtractionUtils.extractList(revs).iterator(); | |
if (!it1.hasNext()) | |
return false; | |
IObject rev = it1.next(); | |
IObject caps = (IObject) rev.resolveValue("capabilities");//$NON-NLS-1$ | |
if (caps == null) | |
return false; | |
for (IObject o : CollectionExtractionUtils.extractList(caps)) | |
{ | |
if ("equinox.fragment".equals(((IObject)(o.resolveValue("namespace"))).getClassSpecificName()))//$NON-NLS-1$ //$NON-NLS-2$ | |
{ | |
return true; | |
} | |
} | |
return false; | |
} | |
private List<BundleDescriptor> getBundleFragments(IObject bundleHostObject) throws SnapshotException | |
{ | |
List<BundleDescriptor> fragments = new ArrayList<BundleDescriptor>(); | |
for (BundleDescriptor bd : bundleDescriptors.values()) | |
{ | |
if (bd.getType().equals(Type.FRAGMENT)) | |
{ | |
BundleDescriptor bd2 = getFragmentHost(snapshot.getObject(bd.getObjectId())); | |
if (bd2.getObjectId() == bundleHostObject.getObjectId()) | |
{ | |
fragments.add(bd); | |
} | |
} | |
} | |
return fragments; | |
} | |
private List<Service> collectServiceInfo(IProgressListener listener) throws SnapshotException | |
{ | |
listener.subTask(Messages.EquinoxBundleReader_ReadingServices); | |
Collection<IClass> classes = snapshot.getClassesByName( // 3.5 | |
"org.eclipse.osgi.internal.serviceregistry.ServiceRegistry", false); //$NON-NLS-1$ | |
if (classes == null || classes.isEmpty()) | |
classes = snapshot.getClassesByName( // 3.4 | |
"org.eclipse.osgi.framework.internal.core.ServiceRegistryImpl", false); //$NON-NLS-1$ | |
List<Service> services = new ArrayList<Service>(); | |
if (classes == null || classes.isEmpty()) | |
return services; | |
int nobjs = 0; | |
for (IClass clazz : classes) | |
nobjs += clazz.getNumberOfObjects(); | |
for (IClass clazz : classes) | |
{ | |
int[] objs = clazz.getObjectIds(); | |
for (int i = 0; i < objs.length; i++) | |
{ | |
IObject obj = snapshot.getObject(objs[i]); | |
IObject publishedServices = (IObject) obj.resolveValue("allPublishedServices");//$NON-NLS-1$ | |
for (IObject serviceInstance : CollectionExtractionUtils.extractList(publishedServices)) | |
{ | |
if (listener.isCanceled()) | |
throw new IProgressListener.OperationCanceledException(); | |
IObject bundleObj = (IObject) serviceInstance.resolveValue("bundle"); //$NON-NLS-1$ | |
BundleDescriptor bundleDescriptor = getBundleDescriptor(bundleObj, Type.BUNDLE); | |
List<BundleDescriptor> bundlesUsing = null; | |
IObject bundlesList = (IObject) serviceInstance.resolveValue("contextsUsing");//$NON-NLS-1$ | |
ExtractedCollection bunds = CollectionExtractionUtils.extractList(bundlesList); | |
bundlesUsing = new ArrayList<BundleDescriptor>(bunds.size()); | |
for (IObject bundleInstance : bunds) | |
{ | |
IObject bundleObject = (IObject) bundleInstance.resolveValue("bundle");//$NON-NLS-1$ | |
if (bundleObject == null) | |
continue; | |
BundleDescriptor usingBundleDescriptor = getBundleDescriptor(bundleObject, Type.BUNDLE); | |
bundlesUsing.add(usingBundleDescriptor); | |
} | |
// get service name | |
IObjectArray clazzes = (IObjectArray) serviceInstance.resolveValue("clazzes");//$NON-NLS-1$ | |
Iterator<IObject> it = CollectionExtractionUtils.extractList(clazzes).iterator(); | |
String serviceName = it.next().getClassSpecificName(); | |
// get properties | |
IObject propertiesObject = (IObject) serviceInstance.resolveValue("properties");//$NON-NLS-1$ | |
String[] keys = null; | |
String[] values = null; | |
if (propertiesObject != null) | |
{ | |
ExtractedMap em = CollectionExtractionUtils.extractMap(propertiesObject); | |
if (em != null && em.hasSize()) | |
{ | |
// From Oxygen onwards | |
keys = new String[em.size()]; | |
values = new String[em.size()]; | |
int i1 = 0; | |
for (Entry<IObject,IObject> en : em) | |
{ | |
keys[i1] = ((IObject)en.getKey().resolveValue("key")).getClassSpecificName(); //$NON-NLS-1$ | |
values[i1] = en.getValue().getClassSpecificName(); | |
++i1; | |
} | |
} | |
else | |
{ | |
IObjectArray keysArray = (IObjectArray) propertiesObject.resolveValue("headers"); //$NON-NLS-1$ | |
if (keysArray != null) | |
{ | |
long[] keyAddresses = keysArray.getReferenceArray(); | |
if (keyAddresses != null) | |
{ | |
keys = getServiceProperties(new String[keyAddresses.length], keyAddresses); | |
} | |
} | |
IObjectArray valuesArray = (IObjectArray) propertiesObject.resolveValue("values"); //$NON-NLS-1$ | |
if (valuesArray != null) | |
{ | |
long[] valueAddresses = valuesArray.getReferenceArray(); | |
if (valueAddresses != null) | |
{ | |
values = getServiceProperties(new String[valueAddresses.length], valueAddresses); | |
} | |
} | |
} | |
} | |
services.add(new Service(serviceName, serviceInstance.getObjectId(), bundleDescriptor, | |
bundlesUsing, keys, values)); | |
} | |
listener.worked(STEPS / nobjs); | |
} | |
} | |
if (services.size() > 0) | |
updateServiceMap(services); | |
return services; | |
} | |
private String[] getServiceProperties(String[] values, long[] valueAddresses) | |
{ | |
for (int j = 0; j < valueAddresses.length; j++) | |
{ | |
if (valueAddresses[j] == 0) | |
continue; | |
try | |
{ | |
int valueId = snapshot.mapAddressToId(valueAddresses[j]); | |
IObject valueObject = snapshot.getObject(valueId); | |
if (valueObject == null) | |
continue; | |
if (valueObject.getClazz().isArrayType()) | |
{ | |
long[] addresses = ((IObjectArray) valueObject).getReferenceArray(); | |
for (int k = 0; k < addresses.length; k++) | |
{ | |
if (valueAddresses[k] == 0) | |
continue; | |
int id = snapshot.mapAddressToId(addresses[k]); | |
IObject object = snapshot.getObject(id); | |
if (object == null) | |
continue; | |
values[j] = object.getClassSpecificName(); | |
break; // is more than one element possible in that | |
// array? | |
} | |
} | |
else | |
{ | |
values[j] = valueObject.getClassSpecificName(); | |
} | |
} | |
catch (SnapshotException e) | |
{ | |
values[j] = null; | |
MATPlugin.log(e, | |
MessageUtil.format(Messages.EquinoxBundleReader_ErrorMsg_ServiceProperty, | |
Long.toHexString(valueAddresses[j]))); | |
} | |
} | |
return values; | |
} | |
private void updateServiceMap(List<Service> services) | |
{ | |
for (Service service : services) | |
{ | |
BundleDescriptor contributedBy = service.getBundleDescriptor(); | |
// update registered services | |
doUpdate(service, contributedBy, registeredServices); | |
// update used services | |
List<BundleDescriptor> bundlesUsing = service.getBundlesUsing(); | |
if (bundlesUsing != null) | |
for (BundleDescriptor descriptor : bundlesUsing) | |
{ | |
doUpdate(service, descriptor, usedServices); | |
} | |
} | |
} | |
private void doUpdate(Service service, BundleDescriptor bundleDescriptor, | |
Map<BundleDescriptor, List<Service>> serviceMap) | |
{ | |
List<Service> listOfServices = serviceMap.get(bundleDescriptor); | |
if (listOfServices == null) | |
{ | |
List<Service> bundleServices = new ArrayList<Service>(1); | |
bundleServices.add(service); | |
serviceMap.put(bundleDescriptor, bundleServices); | |
} | |
else if (!listOfServices.contains(service)) | |
{ | |
listOfServices.add(service); | |
} | |
} | |
private BundleDescriptor getBundleDescriptor(IObject bundleHostObject, BundleDescriptor.Type type) | |
throws SnapshotException | |
{ | |
IInstance bundleData = (IInstance) bundleHostObject.resolveValue("module");//$NON-NLS-1$ | |
IInstance bundleData2 = (IInstance) bundleData.resolveValue("id");//$NON-NLS-1$ | |
Field idField = bundleData2.getField("value"); //$NON-NLS-1$ | |
Long id = null; | |
if (idField != null) | |
{ | |
id = (Long) idField.getValue(); | |
} | |
// check whether this bundle descriptor is already in the map | |
BundleDescriptor bundleDescriptor = bundleDescriptors.get(id); | |
if (bundleDescriptor != null) | |
return bundleDescriptor; | |
String bundleName = extractBundleName(bundleData); | |
String state = getState(bundleHostObject); | |
BundleDescriptor descriptor = new BundleDescriptor(bundleHostObject.getObjectId(), id, bundleName, state, type); | |
// add new bundle name to the map. Key is bundleId | |
bundleDescriptors.put(id, descriptor); | |
return descriptor; | |
} | |
private String extractBundleName(IObject bundleData) throws SnapshotException | |
{ | |
IObject revs = (IObject) bundleData.resolveValue("revisions.revisions");//$NON-NLS-1$ | |
IObject rev = CollectionExtractionUtils.extractList(revs).iterator().next(); | |
IObject name = (IObject) rev.resolveValue("symbolicName");//$NON-NLS-1$ | |
String symbolicName = name.getClassSpecificName(); | |
IObject versionObj = (IObject) rev.resolveValue("version.qualifier");//$NON-NLS-1$ | |
String version = null; | |
if (versionObj != null) | |
{ | |
version = rev.resolveValue("version.major") + //$NON-NLS-1$ | |
"." + //$NON-NLS-1$ | |
rev.resolveValue("version.minor") + //$NON-NLS-1$ | |
"." + //$NON-NLS-1$ | |
rev.resolveValue("version.micro") + //$NON-NLS-1$ | |
"." + //$NON-NLS-1$ | |
versionObj.getClassSpecificName(); | |
} | |
String bundleName = version == null || version.equals("") ? symbolicName : symbolicName + " (" + version + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | |
return bundleName; | |
} | |
public Bundle getBundle(BundleDescriptor descriptor) throws SnapshotException | |
{ | |
Bundle bundle = bundles.get(descriptor.getBundleName()); | |
if (bundle == null) | |
bundle = load(descriptor); | |
return bundle; | |
} | |
private Bundle load(BundleDescriptor descriptor) throws SnapshotException | |
{ | |
int objectId = descriptor.getObjectId(); | |
IInstance obj = (IInstance) snapshot.getObject(objectId); | |
IObject locationObj = (IObject) obj.resolveValue("module.location");//$NON-NLS-1$ | |
String location = null; | |
if (locationObj != null) | |
{ | |
location = locationObj.getClassSpecificName(); | |
} | |
if (descriptor.getType().equals(BundleDescriptor.Type.FRAGMENT)) | |
{ | |
BundleDescriptor host = getFragmentHost(obj); | |
return new BundleFragment(descriptor, location, host); | |
} | |
List<BundleDescriptor> dependencies = null; | |
List<BundleDescriptor> dependents = null; | |
dependencies = bundleDependencies.get(descriptor); | |
dependents = bundleDependents.get(descriptor); | |
List<Extension> extensions = extensionsByBundle.get(descriptor); | |
List<ExtensionPoint> extensionPoints = extensionPointsByBundle.get(descriptor); | |
List<Service> registeredServices = this.registeredServices.get(descriptor); | |
List<Service> usedServices = this.usedServices.get(descriptor); | |
List<BundleDescriptor> fragments = getBundleFragments(obj); | |
return new Bundle(descriptor, location, dependencies, dependents, extensionPoints, extensions, | |
registeredServices, usedServices, fragments); | |
} | |
private BundleDescriptor getFragmentHost(IObject bundleFragmentObject) throws SnapshotException | |
{ | |
IObject revs = (IObject) bundleFragmentObject.resolveValue("module.revisions.revisions");//$NON-NLS-1$ | |
IObject rev = CollectionExtractionUtils.extractList(revs).iterator().next(); | |
IObject caps = (IObject) rev.resolveValue("capabilities");//$NON-NLS-1$ | |
for (IObject o : CollectionExtractionUtils.extractList(caps)) | |
{ | |
if ("equinox.fragment".equals(((IObject)(o.resolveValue("namespace"))).getClassSpecificName())) //$NON-NLS-1$ //$NON-NLS-2$ | |
{ | |
IObject attribs = (IObject)o.resolveValue("attributes"); //$NON-NLS-1$ | |
ExtractedMap kvs = CollectionExtractionUtils.extractMap(attribs); | |
for (Entry<IObject,IObject> et : kvs) | |
{ | |
if ("equinox.fragment".equals(et.getKey().getClassSpecificName())) //$NON-NLS-1$ | |
{ | |
String hostName = et.getValue().getClassSpecificName(); | |
if (hostName != null) | |
{ | |
for (BundleDescriptor bd : bundleDescriptors.values()) | |
{ | |
if (bd.getBundleName().equals(hostName) || bd.getBundleName().startsWith(hostName + " (")) //$NON-NLS-1$ | |
{ | |
return bd; | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
MATPlugin.log(MessageUtil.format(Messages.EquinoxBundleReader_ErrorMsg_BundleNotFound, | |
Long.toHexString(bundleFragmentObject.getObjectAddress()))); | |
return null; | |
} | |
private String getState(IObject obj) throws SnapshotException | |
{ | |
IInstance bundleHostObject = (IInstance) obj.resolveValue("module.state");//$NON-NLS-1$ | |
Field stateField = bundleHostObject.getField("ordinal");//$NON-NLS-1$ | |
if (stateField == null) | |
return Messages.EquinoxBundleReader_NotApplicable; | |
int state = ((Integer) stateField.getValue()).intValue(); | |
for (BundleState stateType : BundleState.values()) | |
{ | |
if (stateType.getValue() == state) | |
return stateType.getLabel(); | |
} | |
return Messages.EquinoxBundleReader_NotApplicable; | |
} | |
private List<BundleDescriptor> getDependencies(ISnapshot snapshot, IObject resolvedValue) throws SnapshotException | |
{ | |
List<BundleDescriptor> dependencyDescriptors = null; | |
ExtractedCollection coll = CollectionExtractionUtils.extractList(resolvedValue); | |
if (coll != null) | |
{ | |
dependencyDescriptors = new ArrayList<BundleDescriptor>(coll.size()); | |
for (IObject bundleDescriptionObject : coll) | |
{ | |
IObject bundleHostObject = (IObject) bundleDescriptionObject.resolveValue("userObject.bundle");//$NON-NLS-1$ | |
if (bundleHostObject == null) | |
continue; | |
BundleDescriptor.Type type = BundleDescriptor.Type.BUNDLE; | |
if (bundleHostObject.getClazz().getName() | |
.equals("org.eclipse.osgi.framework.internal.core.BundleFragment"))//$NON-NLS-1$ | |
type = BundleDescriptor.Type.FRAGMENT; | |
BundleDescriptor descriptor = getBundleDescriptor(bundleHostObject, type); | |
dependencyDescriptors.add(descriptor); | |
} | |
} | |
return dependencyDescriptors; | |
} | |
private void collectDependencies(IProgressListener listener) throws SnapshotException | |
{ | |
listener.subTask(Messages.EquinoxBundleReader_ReadingDependencies); | |
Collection<IClass>classes = snapshot.getClassesByName("org.eclipse.osgi.container.ModuleWire", false); //$NON-NLS-1$ | |
if (classes != null) | |
{ | |
int nobjs = 0; | |
for (IClass clazz : classes) | |
nobjs += clazz.getNumberOfObjects(); | |
for (IClass clazz : classes) | |
{ | |
int[] objs = clazz.getObjectIds(); | |
for (int i = 0; i < objs.length; i++) | |
{ | |
IInstance obj = (IInstance) snapshot.getObject(objs[i]); | |
IObject providerBundleHostObject = (IObject) obj.resolveValue("hostingProvider.revisions.module.this$0");//$NON-NLS-1$ | |
if (providerBundleHostObject == null) | |
continue; | |
BundleDescriptor.Type providerType = BundleDescriptor.Type.BUNDLE; | |
if (isFragment(providerBundleHostObject)) | |
providerType = BundleDescriptor.Type.FRAGMENT; | |
BundleDescriptor providerDescriptor = getBundleDescriptor(providerBundleHostObject, providerType); | |
IObject requirerBundleHostObject = (IObject) obj.resolveValue("hostingRequirer.revisions.module.this$0");//$NON-NLS-1$ | |
if (requirerBundleHostObject == null) | |
continue; | |
BundleDescriptor.Type requirerType = BundleDescriptor.Type.BUNDLE; | |
if (isFragment(providerBundleHostObject)) | |
requirerType = BundleDescriptor.Type.FRAGMENT; | |
BundleDescriptor requirerDescriptor = getBundleDescriptor(requirerBundleHostObject, requirerType); | |
List<BundleDescriptor> dependents = bundleDependents.get(providerDescriptor); | |
if (dependents == null) | |
{ | |
dependents = new ArrayList<BundleDescriptor>(); | |
dependents.add(requirerDescriptor); | |
bundleDependents.put(providerDescriptor, dependents); | |
} | |
else | |
{ | |
if (!dependents.contains(requirerDescriptor)) | |
{ | |
dependents.add(requirerDescriptor); | |
} | |
} | |
List<BundleDescriptor> dependencies = bundleDependencies.get(requirerDescriptor); | |
if (dependencies == null) | |
{ | |
dependencies = new ArrayList<BundleDescriptor>(); | |
dependencies.add(providerDescriptor); | |
bundleDependencies.put(requirerDescriptor, dependencies); | |
} | |
else | |
{ | |
if (!dependencies.contains(providerDescriptor)) | |
{ | |
dependencies.add(providerDescriptor); | |
} | |
} | |
listener.worked(STEPS / nobjs); | |
} | |
} | |
} | |
} | |
private List<ExtensionPoint> collectExtensionsInfo(IProgressListener listener) throws SnapshotException | |
{ | |
listener.subTask(Messages.EquinoxBundleReader_ReadingExtensions); | |
Collection<IClass> classes = snapshot.getClassesByName( | |
"org.eclipse.core.internal.registry.ExtensionRegistry", false); //$NON-NLS-1$ | |
if (classes == null || classes.isEmpty()) | |
return null; | |
Map<String, ExtensionPoint> extensionPoints = new HashMap<String, ExtensionPoint>(); | |
Map<Integer, Extension> extensions = new HashMap<Integer, Extension>(); | |
Map<Integer, ConfigurationElement> configElements = new HashMap<Integer, ConfigurationElement>(); | |
int nobjs = 0; | |
for (IClass clazz : classes) | |
nobjs += clazz.getNumberOfObjects(); | |
for (IClass clazz : classes) | |
{ | |
int[] objs = clazz.getObjectIds(); | |
for (int i = 0; i < objs.length; i++) | |
{ | |
int work = STEPS / nobjs; | |
IInstance obj = (IInstance) snapshot.getObject(objs[i]); | |
IObjectArray heldObjectsArray = (IObjectArray) obj.resolveValue("registryObjects.heldObjects.elements");//$NON-NLS-1$ | |
ExtractedCollection heldObjects = CollectionExtractionUtils.extractList(heldObjectsArray); | |
if (heldObjects != null) | |
{ | |
Integer size1 = heldObjects.size(); | |
for (IObject instance : heldObjects) | |
{ | |
if (listener.isCanceled()) | |
throw new IProgressListener.OperationCanceledException(); | |
extractElements(instance, extensionPoints, extensions, configElements, listener); | |
listener.worked(work / size1 / 2); | |
} | |
} | |
IObjectArray cachedObjectsArray = (IObjectArray) obj.resolveValue("registryObjects.cache.table");//$NON-NLS-1$ | |
// The cached objects can find extras, but causes duplicate | |
// extensions, extension points etc. | |
boolean useCachedObjects = true; | |
if (useCachedObjects && cachedObjectsArray != null) | |
{ | |
ExtractedCollection refList = CollectionExtractionUtils.extractList(cachedObjectsArray); | |
int size2 = refList.size() / 2; | |
for (IObject instance : refList) | |
{ | |
if (listener.isCanceled()) | |
throw new IProgressListener.OperationCanceledException(); | |
ObjectReference ref = ReferenceQuery.getReferent((IInstance) instance); | |
if (ref != null) | |
{ | |
instance = ref.getObject(); | |
} | |
extractElements(instance, extensionPoints, extensions, configElements, listener); | |
listener.worked(work / size2); | |
} | |
} | |
} | |
} | |
// add ConfigurationElements to corresponding Extensions or | |
// ConfigurationElements | |
Set<Entry<Integer, ConfigurationElement>> configElementSet = configElements.entrySet(); | |
for (Entry<Integer, ConfigurationElement> entry : configElementSet) | |
{ | |
if (listener.isCanceled()) | |
throw new IProgressListener.OperationCanceledException(); | |
ConfigurationElement element = entry.getValue(); | |
Extension extension = extensions.get(element.getParentId()); | |
if (extension == null) | |
{ | |
ConfigurationElement configElement = configElements.get(element.getParentId()); | |
if (configElement == null) | |
continue; | |
configElement.addConfigurationElement(element); | |
continue; | |
} | |
extension.addConfigurationElement(element); | |
} | |
// add Extensions to corresponding ExtensionPoints | |
Set<Entry<Integer, Extension>> set = extensions.entrySet(); | |
for (Entry<Integer, Extension> entry : set) | |
{ | |
if (listener.isCanceled()) | |
throw new IProgressListener.OperationCanceledException(); | |
Extension extension = entry.getValue(); | |
String name = extension.getName(); | |
ExtensionPoint extensionPoint = extensionPoints.get(name); | |
if (extensionPoint == null) | |
continue; | |
extensionPoint.addExtension(extension); | |
} | |
// fill maps extensionsByBundle, ExtensionPointsByBundle | |
Set<Entry<Integer, Extension>> extensionsSet = extensions.entrySet(); | |
for (Entry<Integer, Extension> entry : extensionsSet) | |
{ | |
if (listener.isCanceled()) | |
throw new IProgressListener.OperationCanceledException(); | |
Extension extension = entry.getValue(); | |
BundleDescriptor bundleDescriptor = extension.getContributedBy(); | |
List<Extension> listOfExtensions = extensionsByBundle.get(bundleDescriptor); | |
if (listOfExtensions == null) | |
{ | |
List<Extension> extensionList = new ArrayList<Extension>(1); | |
extensionList.add(extension); | |
extensionsByBundle.put(bundleDescriptor, extensionList); | |
} | |
else if (!listOfExtensions.contains(extension)) | |
{ | |
listOfExtensions.add(extension); | |
} | |
} | |
Set<Entry<String, ExtensionPoint>> pointsSet = extensionPoints.entrySet(); | |
for (Entry<String, ExtensionPoint> entry : pointsSet) | |
{ | |
if (listener.isCanceled()) | |
throw new IProgressListener.OperationCanceledException(); | |
ExtensionPoint point = entry.getValue(); | |
BundleDescriptor bundleDescriptor = point.getContributedBy(); | |
List<ExtensionPoint> listOfExtensions = extensionPointsByBundle.get(bundleDescriptor); | |
if (listOfExtensions == null) | |
{ | |
List<ExtensionPoint> bundleExtensions = new ArrayList<ExtensionPoint>(1); | |
bundleExtensions.add(point); | |
extensionPointsByBundle.put(bundleDescriptor, bundleExtensions); | |
} | |
else if (!listOfExtensions.contains(point)) | |
{ | |
listOfExtensions.add(point); | |
} | |
} | |
// return a list of extension points | |
Set<Entry<BundleDescriptor, List<ExtensionPoint>>> extensionPointSet = extensionPointsByBundle.entrySet(); | |
List<ExtensionPoint> points = new ArrayList<ExtensionPoint>(); | |
for (Entry<BundleDescriptor, List<ExtensionPoint>> entry : extensionPointSet) | |
{ | |
if (listener.isCanceled()) | |
throw new IProgressListener.OperationCanceledException(); | |
List<ExtensionPoint> list = entry.getValue(); | |
for (ExtensionPoint extensionPoint : list) | |
{ | |
if (!points.contains(extensionPoint)) | |
points.add(extensionPoint); | |
} | |
} | |
return points; | |
} | |
private void extractElements(IObject instance, Map<String, ExtensionPoint> extensionPoints, | |
Map<Integer, Extension> extensions, Map<Integer, ConfigurationElement> configElements, | |
IProgressListener listener) throws SnapshotException | |
{ | |
// get type of object (Extension, ExtensionPoint, | |
// ConfigurationElement) | |
String className = instance.getClazz().getName(); | |
if (className.equals("org.eclipse.core.internal.registry.ExtensionPoint")) //$NON-NLS-1$ | |
{ | |
ExtensionPoint extensionPoint = extractExtensionPointInfo(instance); | |
if (extensionPoint != null && !extensionPoints.containsValue(extensionPoint)) | |
extensionPoints.put(extensionPoint.getName(), extensionPoint); | |
else if (extensionPoint != null && instance.getObjectId() != extensionPoint.getObjectId()) | |
MATPlugin.log(MessageUtil.format(Messages.EquinoxBundleReader_ErrorMsg_DuplicateExtensionPoint, | |
extensionPoint.getName(), | |
Long.toHexString(snapshot.mapIdToAddress(extensionPoint.getObjectId())), | |
Long.toHexString(instance.getObjectAddress()))); | |
} | |
else if (className.equals("org.eclipse.core.internal.registry.ConfigurationElement")) //$NON-NLS-1$ | |
{ | |
ConfigurationElement configElement = extractConfigurationElementInfo(instance, listener); | |
if (configElement != null && !configElements.containsValue(configElement)) | |
configElements.put(configElement.getElementId(), configElement); | |
else if (configElement != null && instance.getObjectId() != configElement.getObjectId()) | |
MATPlugin.log(MessageUtil.format(Messages.EquinoxBundleReader_ErrorMsg_DuplicateConfigurationElement, | |
configElement.getName(), | |
Long.toHexString(snapshot.mapIdToAddress(configElement.getObjectId())), | |
Long.toHexString(instance.getObjectAddress()))); | |
} | |
else if (className.equals("org.eclipse.core.internal.registry.Extension")) //$NON-NLS-1$ | |
{ | |
Extension extension = extractExtensionInfo(instance); | |
if (extension != null && !extensions.containsValue(extension)) | |
extensions.put(extension.getExtensionId(), extension); | |
else if (extension != null && instance.getObjectId() != extension.getObjectId()) | |
MATPlugin.log(MessageUtil.format(Messages.EquinoxBundleReader_ErrorMsg_DuplicateExtension, | |
extension.getName(), | |
Long.toHexString(snapshot.mapIdToAddress(extension.getObjectId())), | |
Long.toHexString(instance.getObjectAddress()))); | |
} | |
else | |
{ | |
MATPlugin.log(MessageUtil.format(Messages.EquinoxBundleReader_ErrorMsg_UnknownElementType, | |
Long.toHexString(instance.getObjectAddress()), instance.getClazz().getName())); | |
} | |
} | |
private ConfigurationElement extractConfigurationElementInfo(IObject instance, IProgressListener listener) | |
throws SnapshotException | |
{ | |
Field idField = ((IInstance) instance).getField("parentId"); //$NON-NLS-1$ | |
if (idField == null) | |
{ | |
MATPlugin.log(MessageUtil.format(Messages.EquinoxBundleReader_ErrorMsg_ExpectedFieldParent, | |
Long.toHexString(instance.getObjectAddress()))); | |
return null; | |
} | |
Integer parentId = (Integer) idField.getValue(); | |
Field objectIdField = ((IInstance) instance).getField("objectId"); //$NON-NLS-1$ | |
if (objectIdField == null) | |
{ | |
MATPlugin.log(MessageUtil.format(Messages.EquinoxBundleReader_ErrorMsg_ExpectedFieldObjectId, | |
Long.toHexString(instance.getObjectAddress()))); | |
return null; | |
} | |
Integer objectId = (Integer) objectIdField.getValue(); | |
IObject contributorObject = (IObject) instance.resolveValue("contributorId");//$NON-NLS-1$ | |
if (contributorObject == null) | |
{ | |
MATPlugin.log(MessageUtil.format(Messages.EquinoxBundleReader_ExpectedFieldContributorId, | |
Long.toHexString(instance.getObjectAddress()))); | |
return null; | |
} | |
/* | |
* in some heap dumps the the contributorID was a fully qualified name | |
* instead of a number. The following lines are an attempt to read the | |
* data as number | |
*/ | |
Long contributorId = null; | |
BundleDescriptor contributedBy = null; | |
String contributorIdString = contributorObject.getClassSpecificName(); | |
try | |
{ | |
contributorId = Long.valueOf(contributorIdString); | |
} | |
catch (NumberFormatException e) | |
{ | |
MATPlugin.log(MessageUtil.format(Messages.EquinoxBundleReader_CannotFindContributorID, contributorIdString)); | |
} | |
if (contributorId != null) | |
{ | |
contributedBy = bundleDescriptors.get(contributorId); | |
} | |
IObject nameObject = (IObject) instance.resolveValue("name");//$NON-NLS-1$ | |
if (nameObject == null) | |
{ | |
// some configuration elements contain only description. In that | |
// case attribute name is not available. | |
return null; | |
} | |
String name = nameObject.getClassSpecificName(); | |
IObject propertiesObject = (IObject) instance.resolveValue("propertiesAndValue");//$NON-NLS-1$ | |
if (propertiesObject == null) | |
{ | |
MATPlugin.log(MessageUtil.format(Messages.EquinoxBundleReader_ExpectedFieldPropertiesAndValues, | |
Long.toHexString(instance.getObjectAddress()))); | |
} | |
else if (propertiesObject.getClazz().isArrayType()) | |
{ | |
long[] addresses = ((IObjectArray) propertiesObject).getReferenceArray(); | |
String[] propertiesAndValues = new String[addresses.length]; | |
for (int i = 0; i < addresses.length; i++) | |
{ | |
if (listener.isCanceled()) | |
throw new IProgressListener.OperationCanceledException(); | |
if (addresses[i] == 0) | |
continue; | |
try | |
{ | |
int id = snapshot.mapAddressToId(addresses[i]); | |
IObject object = snapshot.getObject(id); | |
propertiesAndValues[i] = object.getClassSpecificName(); | |
} | |
catch (SnapshotException e) | |
{ | |
// Some HPROF dumps have String arrays with invalid entries | |
// Generating 10,000 messages takes too long | |
if (maxWarnings-- > 0) | |
MATPlugin.log(e, | |
MessageUtil.format(Messages.EquinoxBundleReader_ErrorMsg_ReadingProperty, | |
Long.toHexString(addresses[i]))); | |
propertiesAndValues[i] = null; | |
} | |
} | |
return new ConfigurationElement(instance.getObjectId(), name, parentId, objectId, contributedBy, | |
propertiesAndValues); | |
} | |
else | |
{ | |
MATPlugin.log(MessageUtil.format(Messages.EquinoxBundleReader_ErrorMsg_ExpectedStringArray, | |
Long.toHexString(instance.getObjectAddress()))); | |
} | |
return new ConfigurationElement(instance.getObjectId(), name, parentId, objectId, contributedBy, null); | |
} | |
private Extension extractExtensionInfo(IObject instance) throws SnapshotException | |
{ | |
// Expect at least 3 properties | |
String[] properties = getExtensionProperties(instance); | |
if (properties == null) | |
return null; | |
Field id = ((IInstance) instance).getField("objectId"); //$NON-NLS-1$ | |
if (id == null) | |
{ | |
MATPlugin.log(MessageUtil.format(Messages.EquinoxBundleReader_ErrorMsg_ExpectedFieldObjectId, | |
Long.toHexString(instance.getObjectAddress()))); | |
return null; | |
} | |
Integer extensionId = (Integer) id.getValue(); | |
Extension extension = new Extension(instance.getObjectId(), extensionId, properties); | |
/* | |
* in some heap dumps the the contributorID was a fully qualified name | |
* instead of a number. The following lines are an attempt to read the | |
* data as number | |
*/ | |
Long contributorId = null; | |
String contributorIdString = extension.getContributorId(); | |
try | |
{ | |
contributorId = Long.valueOf(contributorIdString); | |
} | |
catch (NumberFormatException e) | |
{ | |
MATPlugin.log(MessageUtil.format(Messages.EquinoxBundleReader_CannotFindContributorID, contributorIdString)); | |
} | |
if (contributorId != null) | |
{ | |
BundleDescriptor contributedBy = bundleDescriptors.get(contributorId); | |
extension.setContributedBy(contributedBy); | |
} | |
return extension; | |
} | |
private String[] getExtensionProperties(IObject instance) throws SnapshotException | |
{ | |
IObject extraInfoObject = (IObject) instance.resolveValue("extraInformation");//$NON-NLS-1$ | |
if (extraInfoObject == null) | |
{ | |
MATPlugin.log(MessageUtil.format(Messages.EquinoxBundleReader_ErrorMsg_ExpectedFieldExtraInformation, | |
Long.toHexString(instance.getObjectAddress()))); | |
return null; | |
} | |
if (extraInfoObject instanceof IInstance) | |
{ | |
// Handle soft references | |
ObjectReference ref = ReferenceQuery.getReferent((IInstance) extraInfoObject); | |
if (ref != null) | |
{ | |
extraInfoObject = ref.getObject(); | |
} | |
} | |
if (extraInfoObject.getClazz().isArrayType()) | |
{ | |
long[] addresses = ((IObjectArray) extraInfoObject).getReferenceArray(); | |
String[] properties = new String[addresses.length]; | |
for (int i = 0; i < addresses.length; i++) | |
{ | |
if (addresses[i] == 0) | |
continue; | |
int id = snapshot.mapAddressToId(addresses[i]); | |
IObject object = snapshot.getObject(id); | |
properties[i] = object.getClassSpecificName(); | |
} | |
return properties; | |
} | |
else | |
{ | |
// TODO SoftReferences are not handled, as referents were always | |
// null. Log, if otherwise. | |
IObject referentObject = (IObject) extraInfoObject.resolveValue("referent"); //$NON-NLS-1$ | |
if (referentObject != null) | |
MATPlugin.log(MessageUtil.format(Messages.EquinoxBundleReader_ErrorMsg_SoftReferencesNotHandled, | |
Long.toHexString(instance.getObjectAddress()))); | |
return null; | |
} | |
} | |
private ExtensionPoint extractExtensionPointInfo(IObject instance) throws SnapshotException | |
{ | |
// Expect at least 5 properties | |
String[] properties = getExtensionProperties(instance); | |
if (properties == null) | |
return null; | |
Field id = ((IInstance) instance).getField("objectId"); //$NON-NLS-1$ | |
if (id == null) | |
{ | |
MATPlugin.log(MessageUtil.format(Messages.EquinoxBundleReader_ErrorMsg_ExpectedFieldObjectId, | |
Long.toHexString(instance.getObjectAddress()))); | |
return null; | |
} | |
Integer extensionPointId = (Integer) id.getValue(); | |
ExtensionPoint extensionPoint = new ExtensionPoint(instance.getObjectId(), extensionPointId, properties); | |
/* | |
* in some heap dumps the the contributorID was a fully qualified name | |
* instead of a number. The following lines are an attempt to read the | |
* data as number | |
*/ | |
Long contributorId = null; | |
String contributorIdString = extensionPoint.getContributorId(); | |
try | |
{ | |
contributorId = Long.valueOf(contributorIdString); | |
} | |
catch (NumberFormatException e) | |
{ | |
MATPlugin.log(MessageUtil.format(Messages.EquinoxBundleReader_CannotFindContributorID, contributorIdString)); | |
} | |
if (contributorId != null) | |
{ | |
BundleDescriptor contributedBy = bundleDescriptors.get(contributorId); | |
extensionPoint.setContributedBy(contributedBy); | |
} | |
return extensionPoint; | |
} | |
} |