package org.rubypeople.rdt.internal.launching;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.Preferences.IPropertyChangeListener;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IDebugEventSetListener;
import org.eclipse.debug.core.model.IProcess;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import org.rubypeople.rdt.core.ILoadpathEntry;
import org.rubypeople.rdt.core.IRubyInformation;
import org.rubypeople.rdt.core.IRubyProject;
import org.rubypeople.rdt.core.RubyCore;
import org.rubypeople.rdt.launching.IRubyLaunchConfigurationConstants;
import org.rubypeople.rdt.launching.IRuntimeLoadpathEntry2;
import org.rubypeople.rdt.launching.IVMConnector;
import org.rubypeople.rdt.launching.IVMInstall;
import org.rubypeople.rdt.launching.IVMInstallChangedListener;
import org.rubypeople.rdt.launching.IVMInstallType;
import org.rubypeople.rdt.launching.PropertyChangeEvent;
import org.rubypeople.rdt.launching.RubyRuntime;
import org.rubypeople.rdt.launching.VMStandin;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class LaunchingPlugin extends Plugin implements IVMInstallChangedListener, IPropertyChangeListener,
IDebugEventSetListener
{
public static final String PLUGIN_ID = "org.rubypeople.rdt.launching"; //$NON-NLS-1$
/**
* Preference key for boolean flag indicating whether or not user has chosen to use the included JRuby (so we
* shouldn't prompt them to install a ruby VM).
* <p>
* Value is of type <code>Boolean</code>.
* </p>
*
* @since 1.0.0
*/
public static final String USING_INCLUDED_JRUBY = PLUGIN_ID + ".us.included.jruby"; //$NON-NLS-1$
/**
* Runtime classpath extensions
*/
private HashMap<String, IConfigurationElement> fClasspathEntryExtensions = null;
/**
* Identifier for 'runtimeLoadpathEntries' extension point
*/
public static final String ID_EXTENSION_POINT_RUNTIME_CLASSPATH_ENTRIES = "runtimeLoadpathEntries"; //$NON-NLS-1$
/**
* Identifier for 'vmConnectors' extension point
*/
public static final String ID_EXTENSION_POINT_VM_CONNECTORS = "vmConnectors"; //$NON-NLS-1$
/**
* Mapping of top-level VM installation directories to library info for that VM.
*/
private static Map<String, LibraryInfo> fgLibraryInfoMap = null;
/**
* Whether changes in VM preferences are being batched. When being batched the plug-in can ignore processing and
* changes.
*/
private boolean fBatchingChanges = false;
private boolean fIgnoreVMDefPropertyChangeEvents = false;
private String fOldVMPrefString = EMPTY_STRING;
protected static LaunchingPlugin plugin;
private static DocumentBuilder fgXMLParser;
private ServiceTracker fRITracker;
private LaunchCleaner fLaunchCleaner;
private HashMap<String, IVMConnector> fVMConnectors;
private static final String EMPTY_STRING = ""; //$NON-NLS-1$
public static String osDependentPath(String aPath)
{
if (Platform.getOS().equals(Platform.OS_WIN32))
{
if (aPath.startsWith(File.separator))
{
aPath = aPath.substring(1);
}
}
return aPath;
}
public LaunchingPlugin()
{
super();
}
public static LaunchingPlugin getDefault()
{
return plugin;
}
public static IWorkspace getWorkspace()
{
return RubyCore.getWorkspace();
}
public static void log(IStatus status)
{
getDefault().getLog().log(status);
}
public static void log(Throwable e)
{
log(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.ERROR,
LaunchingMessages.RdtLaunchingPlugin_internalErrorOccurred, e));
}
public static void debug(String message)
{
if (getDefault().isDebugging())
{
System.out.println(message);
}
}
@Override
public void start(BundleContext context) throws Exception
{
plugin = this;
super.start(context);
fRITracker = new ServiceTracker(context, IRubyInformation.class.getName(), null);
fRITracker.open();
getPluginPreferences().addPropertyChangeListener(this);
RubyRuntime.addVMInstallChangedListener(this);
DebugPlugin.getDefault().addDebugEventListener(this);
fLaunchCleaner = new LaunchCleaner();
ResourcesPlugin.getWorkspace().addResourceChangeListener(fLaunchCleaner);
}
@Override
public void stop(BundleContext context) throws Exception
{
try
{
fRITracker.close();
ResourcesPlugin.getWorkspace().removeResourceChangeListener(fLaunchCleaner);
DebugPlugin.getDefault().removeDebugEventListener(this);
getPluginPreferences().removePropertyChangeListener(this);
RubyRuntime.removeVMInstallChangedListener(this);
RubyRuntime.saveVMConfiguration();
savePluginPreferences();
fgXMLParser = null;
}
finally
{
super.stop(context);
}
}
public static String getUniqueIdentifier()
{
return PLUGIN_ID;
}
/**
* Save preferences whenever the connect timeout changes. Process changes to the list of installed JREs.
*
* @see org.eclipse.core.runtime.Preferences.IPropertyChangeListener#propertyChange(PropertyChangeEvent)
*/
public void propertyChange(org.eclipse.core.runtime.Preferences.PropertyChangeEvent event)
{
String property = event.getProperty();
// if (property.equals(RubyRuntime.PREF_CONNECT_TIMEOUT)) {
// savePluginPreferences();
// } else
if (property.equals(RubyRuntime.PREF_VM_XML))
{
if (!isIgnoreVMDefPropertyChangeEvents())
{
processVMPrefsChanged((String) event.getOldValue(), (String) event.getNewValue());
}
}
}
public void setIgnoreVMDefPropertyChangeEvents(boolean ignore)
{
fIgnoreVMDefPropertyChangeEvents = ignore;
}
public boolean isIgnoreVMDefPropertyChangeEvents()
{
return fIgnoreVMDefPropertyChangeEvents;
}
/**
* Check for differences between the old & new sets of installed JREs. Differences may include additions, deletions
* and changes. Take appropriate action for each type of difference. When importing preferences, TWO propertyChange
* events are fired. The first has an old value but an empty new value. The second has a new value, but an empty old
* value. Normal user changes to the preferences result in a single propertyChange event, with both old and new
* values populated. This method handles both types of notification.
*/
protected void processVMPrefsChanged(String oldValue, String newValue)
{
// batch changes
fBatchingChanges = true;
VMChanges vmChanges = null;
try
{
String oldPrefString;
String newPrefString;
// If empty new value, save the old value and wait for 2nd propertyChange notification
if (newValue == null || newValue.equals(EMPTY_STRING))
{
fOldVMPrefString = oldValue;
return;
}
// An empty old value signals the second notification in the import preferences
// sequence. Now that we have both old & new prefs, we can parse and compare them.
else if (oldValue == null || oldValue.equals(EMPTY_STRING))
{
oldPrefString = fOldVMPrefString;
newPrefString = newValue;
}
// If both old & new values are present, this is a normal user change
else
{
oldPrefString = oldValue;
newPrefString = newValue;
}
vmChanges = new VMChanges();
RubyRuntime.addVMInstallChangedListener(vmChanges);
// Generate the previous VMs
VMDefinitionsContainer oldResults = getVMDefinitions(oldPrefString);
// Generate the current
VMDefinitionsContainer newResults = getVMDefinitions(newPrefString);
// Determine the deleted VMs
List<IVMInstall> deleted = oldResults.getVMList();
List<IVMInstall> current = newResults.getValidVMList();
deleted.removeAll(current);
// Dispose deleted VMs. The 'disposeVMInstall' method fires notification of the
// deletion.
Iterator deletedIterator = deleted.iterator();
while (deletedIterator.hasNext())
{
VMStandin deletedVMStandin = (VMStandin) deletedIterator.next();
deletedVMStandin.getVMInstallType().disposeVMInstall(deletedVMStandin.getId());
}
// Fire change notification for added and changed VMs. The 'convertToRealVM'
// fires the appropriate notification.
Iterator iter = current.iterator();
while (iter.hasNext())
{
VMStandin standin = (VMStandin) iter.next();
standin.convertToRealVM();
}
// set the new default VM install. This will fire a 'defaultVMChanged',
// if it in fact changed
String newDefaultId = newResults.getDefaultVMInstallCompositeID();
if (newDefaultId != null)
{
IVMInstall newDefaultVM = RubyRuntime.getVMFromCompositeId(newDefaultId);
if (newDefaultVM != null)
{
try
{
RubyRuntime.setDefaultVMInstall(newDefaultVM, null, false);
}
catch (CoreException ce)
{
log(ce);
}
}
}
}
finally
{
// stop batch changes
fBatchingChanges = false;
if (vmChanges != null)
{
RubyRuntime.removeVMInstallChangedListener(vmChanges);
try
{
vmChanges.process();
}
catch (CoreException e)
{
log(e);
}
}
}
}
/**
* Parse the given xml into a VM definitions container, returning an empty container if an exception occurs.
*
* @param xml
* @return VMDefinitionsContainer
*/
private VMDefinitionsContainer getVMDefinitions(String xml)
{
if (xml.length() > 0)
{
try
{
ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes("UTF8")); //$NON-NLS-1$
return VMDefinitionsContainer.parseXMLIntoContainer(stream);
}
catch (IOException e)
{
LaunchingPlugin.log(e);
}
}
return new VMDefinitionsContainer();
}
/**
* Returns a Document that can be used to build a DOM tree
*
* @return the Document
* @throws ParserConfigurationException
* if an exception occurs creating the document builder
*/
public static Document getDocument() throws ParserConfigurationException
{
DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
return doc;
}
/**
* Serializes a XML document into a string - encoded in UTF8 format, with platform line separators.
*
* @param doc
* document to serialize
* @return the document as a string
*/
public static String serializeDocument(Document doc) throws IOException, TransformerException
{
ByteArrayOutputStream s = new ByteArrayOutputStream();
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
DOMSource source = new DOMSource(doc);
StreamResult outputTarget = new StreamResult(s);
transformer.transform(source, outputTarget);
return s.toString("UTF8"); //$NON-NLS-1$
}
public static void log(String message)
{
log(new Status(IStatus.ERROR, getUniqueIdentifier(), IStatus.ERROR, message, null));
}
/**
* Returns a shared XML parser.
*
* @return an XML parser
* @throws CoreException
* if unable to create a parser
* @since 3.0
*/
public static DocumentBuilder getParser() throws CoreException
{
if (fgXMLParser == null)
{
try
{
fgXMLParser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
fgXMLParser.setErrorHandler(new DefaultHandler());
}
catch (ParserConfigurationException e)
{
abort(LaunchingMessages.LaunchingPlugin_33, e);
}
catch (FactoryConfigurationError e)
{
abort(LaunchingMessages.LaunchingPlugin_34, e);
}
}
return fgXMLParser;
}
/**
* Throws an exception with the given message and underlying exception.
*
* @param message
* error message
* @param exception
* underlying exception or <code>null</code> if none
* @throws CoreException
*/
protected static void abort(String message, Throwable exception) throws CoreException
{
IStatus status = new Status(IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), 0, message, exception);
throw new CoreException(status);
}
/**
* Sets the library info that corresponds to the specified Ruby VM install path.
*
* @param installPath
* home location for a Ruby VM
* @param info
* the library information, or <code>null</code> to remove
*/
public static void setLibraryInfo(IVMInstallType type, String installPath, LibraryInfo info)
{
if (fgLibraryInfoMap == null)
{
restoreLibraryInfo();
}
if (info == null)
{
fgLibraryInfoMap.remove(buildLibraryInfoKey(type, installPath));
}
else
{
fgLibraryInfoMap.put(buildLibraryInfoKey(type, installPath), info);
}
saveLibraryInfo();
}
/**
* Saves the library info in a local workspace state location
*/
private static void saveLibraryInfo()
{
FileOutputStream stream = null;
try
{
String xml = getLibraryInfoAsXML();
IPath libPath = getDefault().getStateLocation();
libPath = libPath.append("libraryInfos.xml"); //$NON-NLS-1$
File file = libPath.toFile();
if (!file.exists())
{
file.createNewFile();
}
stream = new FileOutputStream(file);
stream.write(xml.getBytes("UTF8")); //$NON-NLS-1$
}
catch (IOException e)
{
log(e);
}
catch (ParserConfigurationException e)
{
log(e);
}
catch (TransformerException e)
{
log(e);
}
finally
{
if (stream != null)
{
try
{
stream.close();
}
catch (IOException e1)
{
}
}
}
}
/**
* Return the VM definitions contained in this object as a String of XML. The String is suitable for storing in the
* workbench preferences.
* <p>
* The resulting XML is compatible with the static method <code>parseXMLIntoContainer</code>.
* </p>
*
* @return String the results of flattening this object into XML
* @throws IOException
* if this method fails. Reasons include:
* <ul>
* <li>serialization of the XML document failed</li>
* </ul>
*/
private static String getLibraryInfoAsXML() throws ParserConfigurationException, IOException, TransformerException
{
Document doc = getDocument();
Element config = doc.createElement("libraryInfos"); //$NON-NLS-1$
doc.appendChild(config);
// Create a node for each info in the table
Iterator locations = fgLibraryInfoMap.keySet().iterator();
while (locations.hasNext())
{
String raw = (String) locations.next();
int index = raw.indexOf(";");
String home = raw.substring(index + 1);
String vmTypeId = "org.rubypeople.rdt.launching.StandardVMType"; //$NON-NLS-1$ // Default to standard VM type...
if (index != -1)
{
vmTypeId = raw.substring(0, index);
}
LibraryInfo info = fgLibraryInfoMap.get(raw);
Element locationElemnet = infoAsElement(doc, info);
locationElemnet.setAttribute("home", home); //$NON-NLS-1$
locationElemnet.setAttribute("vmTypeId", vmTypeId);//$NON-NLS-1$
config.appendChild(locationElemnet);
}
// Serialize the Document and return the resulting String
return LaunchingPlugin.serializeDocument(doc);
}
/**
* Creates an XML element for the given info.
*
* @param doc
* @param info
* @return Element
*/
private static Element infoAsElement(Document doc, LibraryInfo info)
{
Element libraryElement = doc.createElement("libraryInfo"); //$NON-NLS-1$
libraryElement.setAttribute("version", info.getVersion()); //$NON-NLS-1$
appendPathElements(doc, "bootpath", libraryElement, info.getBootpath()); //$NON-NLS-1$
return libraryElement;
}
/**
* Appends path elements to the given library element, rooted by an element of the given type.
*
* @param doc
* @param elementType
* @param libraryElement
* @param paths
*/
private static void appendPathElements(Document doc, String elementType, Element libraryElement, String[] paths)
{
if (paths.length > 0)
{
Element child = doc.createElement(elementType);
libraryElement.appendChild(child);
for (int i = 0; i < paths.length; i++)
{
String path = paths[i];
Element entry = doc.createElement("entry"); //$NON-NLS-1$
child.appendChild(entry);
entry.setAttribute("path", path); //$NON-NLS-1$
}
}
}
/**
* Returns the library info that corresponds to the specified RubyVM install path, or <code>null</code> if none.
*
* @param type
* @return the library info that corresponds to the specified RubyVM install path, or <code>null</code> if none
*/
public static LibraryInfo getLibraryInfo(IVMInstallType type, String vmInstallPath)
{
if (fgLibraryInfoMap == null)
{
restoreLibraryInfo();
}
return fgLibraryInfoMap.get(buildLibraryInfoKey(type, vmInstallPath));
}
private static String buildLibraryInfoKey(IVMInstallType type, String vmInstallPath)
{
return buildLibraryInfoKey(type.getId(), vmInstallPath);
}
private static String buildLibraryInfoKey(String vmTypeId, String vmInstallPath)
{
return vmTypeId + ";" + vmInstallPath;
}
/**
* Restores library information for VMs
*/
private static void restoreLibraryInfo()
{
fgLibraryInfoMap = new HashMap<String, LibraryInfo>(10);
IPath libPath = getDefault().getStateLocation();
libPath = libPath.append("libraryInfos.xml"); //$NON-NLS-1$
File file = libPath.toFile();
if (file.exists())
{
try
{
InputStream stream = new FileInputStream(file);
DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
parser.setErrorHandler(new DefaultHandler());
Element root = parser.parse(new InputSource(stream)).getDocumentElement();
if (!root.getNodeName().equals("libraryInfos")) { //$NON-NLS-1$
return;
}
NodeList list = root.getChildNodes();
int length = list.getLength();
for (int i = 0; i < length; ++i)
{
Node node = list.item(i);
short type = node.getNodeType();
if (type == Node.ELEMENT_NODE)
{
Element element = (Element) node;
String nodeName = element.getNodeName();
if (nodeName.equalsIgnoreCase("libraryInfo")) { //$NON-NLS-1$
String version = element.getAttribute("version"); //$NON-NLS-1$
String location = element.getAttribute("home"); //$NON-NLS-1$
String vmTypeId = element.getAttribute("vmTypeId"); //$NON-NLS-1$
String[] bootpath = getPathsFromXML(element, "bootpath"); //$NON-NLS-1$
if (location != null)
{
LibraryInfo info = new LibraryInfo(version, bootpath);
fgLibraryInfoMap.put(buildLibraryInfoKey(vmTypeId, location), info);
}
}
}
}
}
catch (IOException e)
{
log(e);
}
catch (ParserConfigurationException e)
{
log(e);
}
catch (SAXException e)
{
log(e);
}
}
}
private static String[] getPathsFromXML(Element lib, String pathType)
{
List<String> paths = new ArrayList<String>();
NodeList list = lib.getChildNodes();
int length = list.getLength();
for (int i = 0; i < length; ++i)
{
Node node = list.item(i);
short type = node.getNodeType();
if (type == Node.ELEMENT_NODE)
{
Element element = (Element) node;
String nodeName = element.getNodeName();
if (nodeName.equalsIgnoreCase(pathType))
{
NodeList entries = element.getChildNodes();
int numEntries = entries.getLength();
for (int j = 0; j < numEntries; j++)
{
Node n = entries.item(j);
short t = n.getNodeType();
if (t == Node.ELEMENT_NODE)
{
Element entryElement = (Element) n;
String name = entryElement.getNodeName();
if (name.equals("entry")) { //$NON-NLS-1$
String path = entryElement.getAttribute("path"); //$NON-NLS-1$
if (path != null && path.length() > 0)
{
paths.add(path);
}
}
}
}
}
}
}
return paths.toArray(new String[paths.size()]);
}
/**
* Returns a new runtime classpath entry of the specified type.
*
* @param id
* extension type id
* @return new uninitialized runtime classpath entry
* @throws CoreException
* if unable to create an entry
*/
public IRuntimeLoadpathEntry2 newRuntimeLoadpathEntry(String id) throws CoreException
{
if (fClasspathEntryExtensions == null)
{
initializeRuntimeLoadpathExtensions();
}
IConfigurationElement config = fClasspathEntryExtensions.get(id);
if (config == null)
{
abort(MessageFormat.format(LaunchingMessages.LaunchingPlugin_32, id), null);
}
return (IRuntimeLoadpathEntry2) config.createExecutableExtension("class"); //$NON-NLS-1$
}
/**
* Loads runtime classpath extensions
*/
private void initializeRuntimeLoadpathExtensions()
{
IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(LaunchingPlugin.PLUGIN_ID,
ID_EXTENSION_POINT_RUNTIME_CLASSPATH_ENTRIES);
IConfigurationElement[] configs = extensionPoint.getConfigurationElements();
fClasspathEntryExtensions = new HashMap<String, IConfigurationElement>(configs.length);
for (int i = 0; i < configs.length; i++)
{
fClasspathEntryExtensions.put(configs[i].getAttribute("id"), configs[i]); //$NON-NLS-1$
}
}
public void defaultVMInstallChanged(IVMInstall previous, IVMInstall current)
{
if (!fBatchingChanges)
{
try
{
VMChanges changes = new VMChanges();
changes.defaultVMInstallChanged(previous, current);
changes.process();
}
catch (CoreException e)
{
log(e);
}
}
}
public void vmAdded(IVMInstall newVm)
{
}
public void vmChanged(PropertyChangeEvent event)
{
if (!fBatchingChanges)
{
try
{
VMChanges changes = new VMChanges();
changes.vmChanged(event);
changes.process();
}
catch (CoreException e)
{
log(e);
}
}
}
public void vmRemoved(IVMInstall vm)
{
removeCoreStubs(vm);
if (!fBatchingChanges)
{
try
{
VMChanges changes = new VMChanges();
changes.vmRemoved(vm);
changes.process();
}
catch (CoreException e)
{
log(e);
}
}
}
private void removeCoreStubs(IVMInstall vm)
{
IPath coreStubPath = getStateLocation().append(vm.getId());
deleteRecursively(coreStubPath.toFile());
}
private void deleteRecursively(File file)
{
if (file.isDirectory())
{
File[] children = file.listFiles();
for (int i = 0; i < children.length; i++)
{
deleteRecursively(children[i]);
}
}
if (!file.delete())
file.deleteOnExit();
}
/**
* Stores VM changes resulting from a JRE preference change.
*/
class VMChanges implements IVMInstallChangedListener
{
// true if the default VM changes
private boolean fDefaultChanged = false;
// old container ids to new
private HashMap<IPath, IPath> fRenamedContainerIds = new HashMap<IPath, IPath>();
/**
* Returns the JRE container id that the given VM would map to, or <code>null</code> if none.
*
* @param vm
* @return container id or <code>null</code>
*/
private IPath getContainerId(IVMInstall vm)
{
if (vm != null)
{
String name = vm.getName();
if (name != null)
{
IPath path = new Path(RubyRuntime.RUBY_CONTAINER);
path = path.append(new Path(vm.getVMInstallType().getId()));
path = path.append(new Path(name));
return path;
}
}
return null;
}
/**
* @see org.rubypeople.rdt.launching.IVMInstallChangedListener#defaultVMInstallChanged(org.rubypeople.rdt.launching.IVMInstall,
* org.rubypeople.rdt.launching.IVMInstall)
*/
public void defaultVMInstallChanged(IVMInstall previous, IVMInstall current)
{
fDefaultChanged = true;
}
/**
* @see org.rubypeople.rdt.launching.IVMInstallChangedListener#vmAdded(org.rubypeople.rdt.launching.IVMInstall)
*/
public void vmAdded(IVMInstall vm)
{
}
/**
* @see org.rubypeople.rdt.launching.IVMInstallChangedListener#vmChanged(org.rubypeople.rdt.launching.PropertyChangeEvent)
*/
public void vmChanged(org.rubypeople.rdt.launching.PropertyChangeEvent event)
{
String property = event.getProperty();
IVMInstall vm = (IVMInstall) event.getSource();
if (property.equals(IVMInstallChangedListener.PROPERTY_NAME))
{
IPath newId = getContainerId(vm);
IPath oldId = new Path(RubyRuntime.RUBY_CONTAINER);
oldId = oldId.append(vm.getVMInstallType().getId());
String oldName = (String) event.getOldValue();
// bug 33746 - if there is no old name, then this is not a re-name.
if (oldName != null)
{
oldId = oldId.append(oldName);
fRenamedContainerIds.put(oldId, newId);
}
}
}
/**
* @see org.rubypeople.rdt.launching.IVMInstallChangedListener#vmRemoved(org.rubypeople.rdt.launching.IVMInstall)
*/
public void vmRemoved(IVMInstall vm)
{
}
/**
* Re-bind loadpath variables and containers affected by the JRE changes.
*/
public void process() throws CoreException
{
RubyVMUpdateJob job = new RubyVMUpdateJob(this);
job.schedule();
}
protected void doit(IProgressMonitor monitor) throws CoreException
{
IWorkspaceRunnable runnable = new IWorkspaceRunnable()
{
public void run(IProgressMonitor monitor1) throws CoreException
{
IRubyProject[] projects = RubyCore.create(ResourcesPlugin.getWorkspace().getRoot())
.getRubyProjects();
monitor1.beginTask(LaunchingMessages.LaunchingPlugin_0, projects.length + 1);
rebind(monitor1, projects);
monitor1.done();
}
};
RubyCore.run(runnable, null, monitor);
}
/**
* Re-bind loadpath variables and containers affected by the Ruby VM changes.
*
* @param monitor
*/
private void rebind(IProgressMonitor monitor, IRubyProject[] projects) throws CoreException
{
if (fDefaultChanged)
{
// re-bind RUBYLIB if the default VM changed
RubyLoadpathVariablesInitializer initializer = new RubyLoadpathVariablesInitializer();
initializer.initialize(RubyRuntime.RUBYLIB_VARIABLE);
}
monitor.worked(1);
// re-bind all container entries
for (int i = 0; i < projects.length; i++)
{
IRubyProject project = projects[i];
ILoadpathEntry[] entries = project.getRawLoadpath();
boolean replace = false;
for (int j = 0; j < entries.length; j++)
{
ILoadpathEntry entry = entries[j];
switch (entry.getEntryKind())
{
case ILoadpathEntry.CPE_CONTAINER:
IPath reference = entry.getPath();
IPath newBinding = null;
String firstSegment = reference.segment(0);
if (RubyRuntime.RUBY_CONTAINER.equals(firstSegment))
{
if (reference.segmentCount() > 1)
{
IPath renamed = fRenamedContainerIds.get(reference);
if (renamed != null)
{
// The JRE was re-named. This changes the identifier of
// the container entry.
newBinding = renamed;
}
}
RubyContainerInitializer initializer = new RubyContainerInitializer();
if (newBinding == null)
{
// rebind old path
initializer.initialize(reference, project);
}
else
{
// replace old cp entry with a new one
ILoadpathEntry newEntry = RubyCore
.newContainerEntry(newBinding, entry.isExported());
entries[j] = newEntry;
replace = true;
}
}
break;
default:
break;
}
}
if (replace)
{
project.setRawLoadpath(entries, null);
}
monitor.worked(1);
}
}
}
class RubyVMUpdateJob extends Job
{
private VMChanges fChanges;
public RubyVMUpdateJob(VMChanges changes)
{
super(LaunchingMessages.LaunchingPlugin_1);
fChanges = changes;
setSystem(true);
}
/*
* (non-Javadoc)
* @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
*/
protected IStatus run(IProgressMonitor monitor)
{
try
{
fChanges.doit(monitor);
}
catch (CoreException e)
{
return e.getStatus();
}
return Status.OK_STATUS;
}
}
/**
* Return a <code>java.io.File</code> object that corresponds to the specified <code>IPath</code> in the plugin
* directory.
*/
public static File getFileInPlugin(IPath path)
{
return RubyCore.copyToStateLocation(getDefault(), path);
}
public static void info(String string)
{
log(new Status(IStatus.INFO, PLUGIN_ID, -1, string, null));
}
public void handleDebugEvents(DebugEvent[] events)
{
for (int i = 0; i < events.length; i++)
{
// Watch for termination of processes that modify the filesystem
// and refresh the modified project
int kind = events[i].getKind();
Object source = events[i].getSource();
if ((kind == DebugEvent.TERMINATE) && (source instanceof IProcess))
{
IProcess iProc = (IProcess) source;
String refresh = iProc.getAttribute(IRubyLaunchConfigurationConstants.ATTR_REQUIRES_REFRESH);
if ((refresh != null) && refresh.equals("true"))
{
String projName = iProc.getAttribute(IRubyLaunchConfigurationConstants.ATTR_PROJECT_NAME);
IProject proj = ResourcesPlugin.getWorkspace().getRoot().getProject(projName);
try
{
proj.refreshLocal(IResource.DEPTH_INFINITE, null);
}
catch (CoreException e)
{
log(e);
}
}
}
}
}
public static IRubyInformation getRubyInformation()
{
return (IRubyInformation) getDefault().fRITracker.getService();
}
/**
* Returns the VM connector with the specified id, or <code>null</code> if none.
*
* @param id
* connector identifier
* @return VM connector
*/
public IVMConnector getVMConnector(String id)
{
if (fVMConnectors == null)
{
initializeVMConnectors();
}
return (IVMConnector) fVMConnectors.get(id);
}
/**
* Returns all VM connector extensions.
*
* @return VM connectors
*/
public IVMConnector[] getVMConnectors()
{
if (fVMConnectors == null)
{
initializeVMConnectors();
}
return (IVMConnector[]) fVMConnectors.values().toArray(new IVMConnector[fVMConnectors.size()]);
}
/**
* Loads VM connector extensions
*/
private void initializeVMConnectors()
{
IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(PLUGIN_ID,
ID_EXTENSION_POINT_VM_CONNECTORS);
IConfigurationElement[] configs = extensionPoint.getConfigurationElements();
MultiStatus status = new MultiStatus(getUniqueIdentifier(), IStatus.OK,
"Exception occurred reading vmConnectors extensions.", null); //$NON-NLS-1$
fVMConnectors = new HashMap<String, IVMConnector>(configs.length);
for (int i = 0; i < configs.length; i++)
{
try
{
IVMConnector vmConnector = (IVMConnector) configs[i].createExecutableExtension("class"); //$NON-NLS-1$
fVMConnectors.put(vmConnector.getIdentifier(), vmConnector);
}
catch (CoreException e)
{
status.add(e.getStatus());
}
}
if (!status.isOK())
{
LaunchingPlugin.log(status);
}
}
public static void logInfo(String msg)
{
LaunchingPlugin.log(new Status(IStatus.INFO, LaunchingPlugin.PLUGIN_ID, -1, msg, null));
}
}