/******************************************************************************* * Copyright (c) 2000, 2006 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.rubypeople.rdt.launching; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExecutableExtension; import org.eclipse.core.runtime.Path; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.Launch; import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.core.model.IStreamsProxy; import org.rubypeople.rdt.internal.launching.LaunchingMessages; import org.rubypeople.rdt.internal.launching.LaunchingPlugin; import org.rubypeople.rdt.internal.launching.LibraryInfo; /** * Abstract implementation of a VM install type. Subclasses should implement * <ul> * <li><code>IVMInstall doCreateVMInstall(String id)</code></li> * <li><code>String getName()</code></li> * <li><code>IStatus validateInstallLocation(File installLocation)</code></li> * </ul> * <p> * Clients implementing VM install types should subclass this class. * </p> */ public abstract class AbstractVMInstallType implements IVMInstallType, IExecutableExtension { private List <IVMInstall> fVMs; private String fId; /** * Constructs a new VM install type. */ protected AbstractVMInstallType() { fVMs = new ArrayList<IVMInstall>(10); } /* * (non-Javadoc) Subclasses should not override this method. * * @see IVMType#getVMs() */ public IVMInstall[] getVMInstalls() { IVMInstall[] vms = new IVMInstall[fVMs.size()]; return (IVMInstall[]) fVMs.toArray(vms); } /* * (non-Javadoc) Subclasses should not override this method. * * @see IVMType#disposeVM(String) */ public void disposeVMInstall(String id) { for (int i = 0; i < fVMs.size(); i++) { IVMInstall vm = (IVMInstall) fVMs.get(i); if (vm.getId().equals(id)) { fVMs.remove(i); RubyRuntime.fireVMRemoved(vm); return; } } } /* * (non-Javadoc) Subclasses should not override this method. * * @see IVMType#getVM(String) */ public IVMInstall findVMInstall(String id) { for (int i = 0; i < fVMs.size(); i++) { IVMInstall vm = (IVMInstall) fVMs.get(i); if (vm.getId().equals(id)) { return vm; } } return null; } /* * (non-Javadoc) Subclasses should not override this method. * * @see IVMType#createVM(String) */ public IVMInstall createVMInstall(String id) throws IllegalArgumentException { if (findVMInstall(id) != null) { String format = LaunchingMessages.vmInstallType_duplicateVM; throw new IllegalArgumentException(MessageFormat.format(format, id)); } IVMInstall vm = doCreateVMInstall(id); fVMs.add(vm); return vm; } /** * Subclasses should return a new instance of the appropriate * <code>IVMInstall</code> subclass from this method. * * @param id * The vm's id. The <code>IVMInstall</code> instance that is * created must return <code>id</code> from its * <code>getId()</code> method. Must not be <code>null</code>. * @return the newly created IVMInstall instance. Must not return * <code>null</code>. */ protected abstract IVMInstall doCreateVMInstall(String id); /* * (non-Javadoc) * * @see * org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org * .eclipse.core.runtime.IConfigurationElement, java.lang.String, * java.lang.Object) */ /** * Initializes the id parameter from the "id" attribute in the configuration * markup. Subclasses should not override this method. * * @param config * the configuration element used to trigger this execution. It * can be queried by the executable extension for specific * configuration properties * @param propertyName * the name of an attribute of the configuration element used on * the <code>createExecutableExtension(String)</code> call. This * argument can be used in the cases where a single configuration * element is used to define multiple executable extensions. * @param data * adapter data in the form of a <code>String</code>, a * <code>Hashtable</code>, or <code>null</code>. * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, * java.lang.String, java.lang.Object) */ public void setInitializationData(IConfigurationElement config, String propertyName, Object data) { fId = config.getAttribute("id"); //$NON-NLS-1$ } /* * (non-Javadoc) Subclasses should not override this method. * * @see IVMType#getId() */ public String getId() { return fId; } /* * (non-Javadoc) * * @see * org.eclipse.jdt.launching.IVMInstallType#findVMInstallByName(java.lang * .String) */ public IVMInstall findVMInstallByName(String name) { for (int i = 0; i < fVMs.size(); i++) { IVMInstall vm = (IVMInstall) fVMs.get(i); if (vm.getName().equals(name)) { return vm; } } return null; } /** * Parses the output from 'LibraryDetector'. */ protected LibraryInfo parseLibraryInfo(IProcess process) { IStreamsProxy streamsProxy = process.getStreamsProxy(); if (streamsProxy == null) return null; String text = streamsProxy.getOutputStreamMonitor().getContents(); BufferedReader reader = new BufferedReader(new StringReader(text)); List<String> lines = new ArrayList<String>(); try { String line = null; while ((line = reader.readLine()) != null) { lines.add(line); } } catch (IOException e) { LaunchingPlugin.log(e); } if (lines.size() > 0) { String version = lines.remove(0); removeNotExistingLibs(lines); if (lines.size() > 0) { String[] loadpath = lines.toArray(new String[lines.size()]); return new LibraryInfo(version, loadpath); } } return null; } protected LibraryInfo generateLibraryInfo(File rubyHome, File rubyExecutable) { LibraryInfo info = null; // locate the script to grab us our loadpaths File file = getLibraryInfoGeneratorPath(); //$NON-NLS-1$ if (file.exists()) { String rubyExecutablePath = rubyExecutable.getAbsolutePath(); String[] cmdLine = new String[] { rubyExecutablePath, file.getAbsolutePath() }; //$NON-NLS-1$ Process p = null; try { p = Runtime.getRuntime().exec(cmdLine); IProcess process = DebugPlugin.newProcess(new Launch(null, ILaunchManager.RUN_MODE, null), p, "Library Detection"); //$NON-NLS-1$ for (int i = 0; i < 200; i++) { // Wait no more than 10 seconds (200 * 50 mils) if (process.isTerminated()) { break; } try { Thread.sleep(50); } catch (InterruptedException e) { } } info = parseLibraryInfo(process); } catch (IOException ioe) { LaunchingPlugin.log(ioe); } finally { if (p != null) { p.destroy(); } } } if (info == null) { // log error that we were unable to generate library info - see bug // 70011 LaunchingPlugin .log(MessageFormat .format( "Failed to retrieve default libraries for {0}", rubyHome.getAbsolutePath())); //$NON-NLS-1$ } return info; } protected File getLibraryInfoGeneratorPath() { return LaunchingPlugin.getFileInPlugin(new Path("ruby").append( "standard_vm_type").append("loadpath.rb")); } /** * Do not consider libraries which does not exist. * * @param libraries */ private void removeNotExistingLibs(List<String> libraries) { List<String> toRemove = new ArrayList<String>(); for (String path : libraries) { File file = new File(path); if (!file.exists()) toRemove.add(path); } libraries.removeAll(toRemove); } private List<String> readOutput(IProcess process) { IStreamsProxy streamsProxy = process.getStreamsProxy(); if (streamsProxy == null) return Collections.emptyList(); String text = streamsProxy.getOutputStreamMonitor().getContents(); BufferedReader reader = new BufferedReader(new StringReader(text)); List<String> lines = new ArrayList<String>(); try { String line = null; while ((line = reader.readLine()) != null) { lines.add(line); } } catch (IOException e) { LaunchingPlugin.log(e); } return lines; } protected List<String> executeAndRead(String[] cmdLine) { Process p = null; try { p = Runtime.getRuntime().exec(cmdLine); IProcess process = DebugPlugin.newProcess(new Launch(null, ILaunchManager.RUN_MODE, null), p, "Library Detection"); //$NON-NLS-1$ for (int i= 0; i < 200; i++) { // Wait no more than 10 seconds (200 * 50 mils) if (process.isTerminated()) { break; } try { Thread.sleep(50); } catch (InterruptedException e) { } } return readOutput(process); } catch (IOException ioe) { LaunchingPlugin.log(ioe); } finally { if (p != null) { p.destroy(); } } return null; } protected File parseRubyExecutableLocation(List<String> lines) { if (lines == null || lines.isEmpty()) { return null; } String location = lines.remove(0); File executable = new File(location); if (executable.isFile() && executable.exists()) return executable; return null; } protected abstract LibraryInfo getLibraryInfo(File rubyHome, File rubyExecutable); public String getVMVersion(File installLocation, File executable) { LibraryInfo info = getLibraryInfo(installLocation, executable); return info.getVersion(); } }