blob: 27cb748bea5db0cd8513360b723242b1e4eb769b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009,2021 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.mat.dtfj;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.ContributorFactoryOSGi;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IContributor;
import org.eclipse.core.runtime.ICoreRunnable;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionDelta;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IRegistryChangeEvent;
import org.eclipse.core.runtime.IRegistryChangeListener;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.preference.PreferenceStore;
import org.eclipse.ui.preferences.ScopedPreferenceStore;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
/**
* Controls the loading of this plugin and finds the available DTFJ
* implementations.
*
* @author ajohnson
*/
public class InitDTFJ extends Plugin implements IRegistryChangeListener
{
private static final String DTFJ_NAMESPACE = "com.ibm.dtfj.api"; //$NON-NLS-1$
private static final String DTFJ_IMAGEFACTORY = "imagefactory"; //$NON-NLS-1$
private static final Map<String, Map<String, String>> allexts = new HashMap<String, Map<String, String>>();
private static int updateCount = 0;
private static InitDTFJ plugin;
/**
* Start the bundle - find DTFJ implementations and convert to parsers.
* Register listener for new DTFJ implementations.
*/
public void start(BundleContext context) throws Exception
{
super.start(context);
plugin = this;
IExtensionRegistry reg = Platform.getExtensionRegistry();
if (reg != null)
{
reg.addRegistryChangeListener(this, DTFJ_NAMESPACE);
registerFileExtensions(reg);
}
checkDTFJInterface();
}
/**
* Stop the bundle, deregister parsers associated with DTFJ. Deregister
* listener for new DTFJ implementations.
*/
public void stop(BundleContext context) throws Exception
{
IExtensionRegistry reg = Platform.getExtensionRegistry();
if (reg != null)
{
removeAllExtensions(reg);
reg.removeRegistryChangeListener(this);
}
plugin = null;
super.stop(context);
}
/**
* DTFJ implementation added/removed.
*/
public void registryChanged(IRegistryChangeEvent event)
{
IExtensionRegistry reg = Platform.getExtensionRegistry();
if (reg != null)
{
IContributor cont = getContributor(reg);
// Find the standard Eclipse content types extension point
IExtensionPoint contentPoint = contentExtensionPoint(reg);
for (IExtensionDelta delta : event.getExtensionDeltas(DTFJ_NAMESPACE, DTFJ_IMAGEFACTORY))
{
IExtension dtfjExtension = delta.getExtension();
switch (delta.getKind())
{
case IExtensionDelta.ADDED:
try
{
contributeParserExtension(reg, cont, contentPoint, dtfjExtension);
}
catch (UnsupportedEncodingException e2)
{}
break;
case IExtensionDelta.REMOVED:
removeParserExtension(reg, cont, dtfjExtension);
break;
default:
break;
}
}
checkDTFJInterface();
}
}
/**
* With new loading of DTFJ via Import-Package, sometimes the o.e.mat.dtfj bundle
* is not re-resolved properly.
* Try a update() on the bundle (stop->re-resolve->start).
*/
void checkDTFJInterface()
{
if (allexts.size() > 0 && ++updateCount <= 1)
{
// There is some DTFJ, so check it is usable.
try
{
new DTFJIndexBuilder();
}
catch (NoClassDefFoundError e)
{
log(IStatus.WARNING, Messages.InitDTFJ_FailedToCreate, e);
Job j = Job.create(Messages.InitDTFJ_UpdatingBundle, new ICoreRunnable() {
public void run(IProgressMonitor monitor) throws CoreException
{
try
{
// Re-resolve this bundle to get com.ibm.dtfj.api resolved
plugin.getBundle().update();
log(IStatus.INFO, Messages.InitDTFJ_UpdatedBundle, null);
}
catch (BundleException e)
{
throw new CoreException(new Status(IStatus.ERROR, plugin.getBundle().getSymbolicName(), "Error updating bundle", e));
}
}});
j.schedule();
}
}
}
/**
* Dynamically remove a plugin from the system
*
* @param reg
* @param cont
* @param dtfjExtension
*/
private void removeParserExtension(IExtensionRegistry reg, IContributor cont, IExtension dtfjExtension)
{
for (IConfigurationElement el : dtfjExtension.getConfigurationElements())
{
if (el.getName().equals("factory")) //$NON-NLS-1$
{
String id = el.getAttribute("id"); //$NON-NLS-1$
String fullid = cont.getName() + "." + id; //$NON-NLS-1$
allexts.remove(fullid);
}
}
}
/**
* Remove all the DTFJ parsers.
*
* @param reg
* the extension registry
*/
private void removeAllExtensions(IExtensionRegistry reg)
{
IContributor cont = getContributor(reg);
IExtensionPoint dtfjPoint = dtfjExtensionPoint(reg);
if (dtfjPoint != null)
{
// Look through each DTFJ implementation
for (IExtension ex : dtfjPoint.getExtensions())
{
removeParserExtension(reg, cont, ex);
}
try
{
// Only clear cached dumps if there was an extension point present, so we had the DTFJ interface present
DTFJIndexBuilder.DumpCache.clearCachedDumps();
}
catch (NoClassDefFoundError e)
{
// If com.ibm.dtfj.api doesn't get resolved properly
if (isDebugging())
{
// No need for message normally
log(IStatus.WARNING, Messages.InitDTFJ_ErrorClearDumps, e);
}
}
}
allexts.clear();
}
private void log(int level, String msg, Throwable e)
{
getLog().log(new Status(level, getBundle().getSymbolicName(), msg, e));
}
/**
* Convert DTFJ extensions into MAT parser extensions
*
* @param reg
* the extension registry
*/
void registerFileExtensions(IExtensionRegistry reg)
{
try
{
IContributor cont = getContributor(reg);
IExtensionPoint dtfjPoint = dtfjExtensionPoint(reg);
// Find the standard Eclipse content types extension point
IExtensionPoint contentPoint = contentExtensionPoint(reg);
if (dtfjPoint != null)
{
// Look through each DTFJ implementation
for (IExtension ex : dtfjPoint.getExtensions())
{
contributeParserExtension(reg, cont, contentPoint, ex);
}
}
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
}
private IExtensionPoint contentExtensionPoint(IExtensionRegistry reg)
{
return reg.getExtensionPoint("org.eclipse.core.contenttype", "contentTypes"); //$NON-NLS-1$ //$NON-NLS-2$
}
private IExtensionPoint dtfjExtensionPoint(IExtensionRegistry reg)
{
// Find the DTFJ extension point
IExtensionPoint dtfjPoint = reg.getExtensionPoint(DTFJ_NAMESPACE, DTFJ_IMAGEFACTORY);
return dtfjPoint;
}
/**
* Helper method - find the contributor for this bundle.
*
* @param reg
* @return
*/
private IContributor getContributor(IExtensionRegistry reg)
{
// Find a predefined DTFJ Adapter Plugin extension - use this to set
// the provider for the new plugin
IExtensionPoint extensionPoint = reg.getExtensionPoint("org.eclipse.mat.parser.parser"); //$NON-NLS-1$
if (extensionPoint != null)
{
for (IExtension es1 : extensionPoint.getExtensions())
{
if ("dynamic-dtfj".equals(es1.getSimpleIdentifier())) //$NON-NLS-1$
{
return es1.getContributor();
}
}
}
IContributor cont = ContributorFactoryOSGi.createContributor(InitDTFJ.getDefault().getBundle());
return cont;
}
/**
* Convert a DTFJ image factory to a parser extension.
*
* @param reg
* @param cont
* @param contentPoint
* @param dtfjExtension
* @throws UnsupportedEncodingException
*/
private void contributeParserExtension(IExtensionRegistry reg, IContributor cont, IExtensionPoint contentPoint,
IExtension dtfjExtension) throws UnsupportedEncodingException
{
for (IConfigurationElement el : dtfjExtension.getConfigurationElements())
{
if (el.getName().equals("factory")) //$NON-NLS-1$
{
// List of possible file types
Set<String> done = new HashSet<String>();
// Later used as $heapFormat so we can tell which
// DTFJ to use for a dump
String id = el.getAttribute("id"); //$NON-NLS-1$
String name = el.getAttribute("label"); //$NON-NLS-1$
String exts = null;
String contentTypes = null;
for (IConfigurationElement el2 : el.getChildren())
{
String ref = el2.getAttribute("dump-type"); //$NON-NLS-1$
if (ref != null && done.add(ref))
{
exts = addExtension(exts, genParser(ref, contentPoint));
if (contentTypes == null)
contentTypes = ref;
else
contentTypes = contentTypes + "," + ref; //$NON-NLS-1$
}
ref = el2.getAttribute("meta-type"); //$NON-NLS-1$
if (ref != null && done.add(ref))
{
exts = addExtension(exts, genParser(ref, contentPoint));
if (contentTypes == null)
contentTypes = ref;
else
contentTypes = contentTypes + "," + ref; //$NON-NLS-1$
}
}
Map<String, String> vals = new HashMap<String, String>();
vals.put("id", id); //$NON-NLS-1$
vals.put("name", name); //$NON-NLS-1$
vals.put("fileExtension", exts); //$NON-NLS-1$
if (contentTypes != null)
vals.put("contentTypeBinding", contentTypes); //$NON-NLS-1$
String fullid = cont.getName() + "." + id; //$NON-NLS-1$
allexts.put(fullid, vals);
}
}
}
/**
* Extract the description and the file extensions of a content type.
*
* @param ref
* @param point2
* @param ow
* @return extensions
*/
private static String genParser(String ref, IExtensionPoint point2)
{
IContentType ct = Platform.getContentTypeManager().getContentType(ref);
if (ct == null)
{
// System.out.println("Missing content-type "+ref);
return null;
}
String exts = null;
// Generate parser references for each content subtype
for (IContentType ct1 : Platform.getContentTypeManager().getAllContentTypes())
{
if (ct1.isKindOf(ct))
{
String s1 = genParser(ct1);
exts = addExtension(exts, s1);
}
}
return exts;
}
private static String addExtension(String exts, String ext)
{
if (exts == null)
exts = ext;
else if (ext != null)
exts += "," + ext; //$NON-NLS-1$
return exts;
}
private static String genParser(IContentType ct)
{
String label = ct.getName();
String exts = null;
if (label != null)
{
String s[] = ct.getFileSpecs(IContentType.FILE_EXTENSION_SPEC);
if (s.length > 0)
{
// Build a comma separated extensions list
// MAT copes with comma separated list of extensions from the
// content-type list
for (String s1 : s)
{
exts = addExtension(exts, s1);
}
}
}
return exts;
}
/**
* This is created and called from the MAT parser handling code
* It provides a list of parsers
* E.g.
* org.eclipse.mat.dtfj.DTFJ-J9
* id = "DTFJ-J9"
* name = "IBM SDK for Java (J9) system dump"
* exts = "zip,dmp,xml"
*
*
*/
public static class DynamicInfo extends HashMap<String, Map<String, String>>
{
/**
*
*/
private static final long serialVersionUID = -5291159195829859576L;
{
super.putAll(allexts);
}
}
/**
* Storage for preferences.
* Use Object instead of IPreferenceStore to avoid a hard dependency on org.eclipse.jface
*/
private Object preferenceStore;
public Object getPreferenceStore() {
// Copied from AbstractUIPlugin, so that InitDTFJ doesn't have a hard dependency
// on org.eclipse.ui
// Create the preference store lazily.
if (preferenceStore == null)
{
try
{
preferenceStore = new ScopedPreferenceStore(InstanceScope.INSTANCE, getBundle().getSymbolicName());
}
catch (LinkageError e)
{
preferenceStore = new PreferenceStore();
}
}
return preferenceStore;
}
static InitDTFJ getDefault()
{
return plugin;
}
}