package org.rubypeople.rdt.internal.launching;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.osgi.service.environment.Constants;
import org.osgi.framework.Bundle;
import org.rubypeople.rdt.launching.AbstractVMInstallType;
import org.rubypeople.rdt.launching.IVMInstall;
public class JRubyVMType extends AbstractVMInstallType {
/**
* Map of the install path for which we were unable to generate
* the library info during this session.
*/
private static Map<String, LibraryInfo> fgFailedInstallPath= new HashMap<String, LibraryInfo>();
/**
* Convenience handle to the system-specific file separator character
*/
private static final char fgSeparator = File.separatorChar;
/**
* The list of locations in which to look for the ruby executable in candidate
* VM install locations, relative to the VM install location.
*/
private static final String[] fgCandidateRubyFiles = {"jrubyw", "jrubyw.bat", "jruby", "jruby.bat"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
private static final String[] fgCandidateRubyLocations = {"", "bin" + fgSeparator}; //$NON-NLS-1$ //$NON-NLS-2$
@Override
protected IVMInstall doCreateVMInstall(String id) {
return new JRubyVM(this, id);
}
public File detectInstallLocation() {
File rubyExecutable = null;
if (Platform.getOS().equals(Constants.OS_WIN32)) {
String winPath = System.getenv("Path"); // iterate through system path and try to find jruby.bat
String[] paths = winPath.split(";");
for (int i = 0; i < paths.length; i++) {
String possibleExecutablePath = paths[i] + File.separator + "jruby.bat";
File possible = new File(possibleExecutablePath);
if (possible.exists()) {
rubyExecutable = possible;
break;
}
}
} else { // Mac, Linux - so let's just run 'which jruby' and parse out the result
String[] cmdLine = new String[] { "which", "jruby" }; //$NON-NLS-1$ //$NON-NLS-2$
rubyExecutable = parseRubyExecutableLocation(executeAndRead(cmdLine));
}
File location = tryLocation(rubyExecutable);
if (location != null) return location;
return tryIncludedJRuby();
}
private File tryIncludedJRuby() {
try {
Bundle bundle = Platform.getBundle("org.jruby");
URL url = FileLocator.find(bundle, new Path(""), null);
url = FileLocator.toFileURL(url);
String fileName = url.getFile();
String executable = fileName + "bin" + File.separator + "jruby";
if (Platform.getOS().equals(Constants.OS_WIN32)) {
executable += ".bat";
}
return tryLocation(new File(executable));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
private File tryLocation(File rubyExecutable) {
if (rubyExecutable == null) {
return null;
}
File bin = rubyExecutable.getParentFile();
if (!bin.exists()) return null;
File rubyHome = bin.getParentFile();
if (!rubyHome.exists()) return null;
if (!canDetectDefaultSystemLibraries(rubyHome, rubyExecutable)) {
return null;
}
return rubyHome;
}
public IPath[] getDefaultLibraryLocations(File installLocation) {
File rubyExecutable = findRubyExecutable(installLocation);
LibraryInfo info;
if (rubyExecutable == null) {
info = getDefaultLibraryInfo(installLocation);
} else {
info = getLibraryInfo(installLocation, rubyExecutable);
}
String[] loadpath = info.getBootpath();
IPath[] paths = new IPath[loadpath.length];
for (int i = 0; i < loadpath.length; i++) {
paths[i] = new Path(loadpath[i]);
}
return paths;
}
public String getName() {
return "JRuby VM";
}
public IStatus validateInstallLocation(File rubyHome) {
IStatus status = null;
File rubyExecutable = findRubyExecutable(rubyHome);
if (rubyExecutable == null) {
status = new Status(IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), 0, LaunchingMessages.StandardVMType_Not_a_JDK_Root__Java_executable_was_not_found_1, null);
} else {
if (canDetectDefaultSystemLibraries(rubyHome, rubyExecutable)) {
status = new Status(IStatus.OK, LaunchingPlugin.getUniqueIdentifier(), 0, LaunchingMessages.StandardVMType_ok_2, null);
} else {
status = new Status(IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), 0, LaunchingMessages.StandardVMType_Not_a_JDK_root__System_library_was_not_found__1, null);
}
}
return status;
}
/**
* Starting in the specified VM install location, attempt to find the 'jruby' executable
* file. If found, return the corresponding <code>File</code> object, otherwise return
* <code>null</code>.
*/
public static File findRubyExecutable(File vmInstallLocation) {
// Try each candidate in order. The first one found wins. Thus, the order
// of fgCandidateRubyLocations and fgCandidateRubyFiles is significant.
for (int i = 0; i < fgCandidateRubyFiles.length; i++) {
for (int j = 0; j < fgCandidateRubyLocations.length; j++) {
File rubyFile = new File(vmInstallLocation, fgCandidateRubyLocations[j] + fgCandidateRubyFiles[i]);
if (rubyFile.isFile() && isPlatformProper(rubyFile)) { // Only check for .bat on win32 and others on other platforms
return rubyFile;
}
}
}
return null;
}
private static boolean isPlatformProper(File rubyFile) {
if (Platform.getOS().equals(Platform.OS_WIN32)) {
return rubyFile.getName().endsWith(".bat");
} else {
return !rubyFile.getName().endsWith(".bat");
}
}
/**
* Return <code>true</code> if the appropriate system libraries can be found for the
* specified ruby executable, <code>false</code> otherwise.
*/
protected boolean canDetectDefaultSystemLibraries(File rubyHome, File rubyExecutable) {
IPath[] locations = getDefaultLibraryLocations(rubyHome);
return locations.length > 0;
}
/**
* Returns default library info for the given install location.
*
* @param installLocation
* @return LibraryInfo
*/
protected LibraryInfo getDefaultLibraryInfo(File installLocation) {
IPath[] dflts = getDefaultSystemLibrary(installLocation);
String[] strings = new String[dflts.length];
for (int i = 0; i < dflts.length; i++) {
strings[i] = dflts[i].toOSString();
}
return new LibraryInfo("1.8.5", strings); //$NON-NLS-1$
}
/**
* Return an <code>IPath</code> corresponding to the single library file containing the
* standard Ruby classes for VMs version 1.8.x.
*/
protected IPath[] getDefaultSystemLibrary(File rubyHome) {
String stdPath = rubyHome.getAbsolutePath() + fgSeparator + "lib" + fgSeparator + "ruby" + fgSeparator + "1.8";
String sitePath = rubyHome.getAbsolutePath() + fgSeparator + "lib" + fgSeparator + "ruby" + fgSeparator + "site_ruby" + fgSeparator + "1.8";
IPath[] paths = new IPath[2];
paths[0] = new Path(sitePath);
paths[1] = new Path(stdPath);
return paths;
}
/**
* Return library information corresponding to the specified install
* location. If the info does not exist, create it using the given JRuby
* executable.
*/
protected synchronized LibraryInfo getLibraryInfo(File rubyHome, File rubyExecutable) {
// See if we already know the info for the requested VM. If not, generate it.
String installPath = rubyHome.getAbsolutePath();
LibraryInfo info = LaunchingPlugin.getLibraryInfo(this, installPath);
if (info == null) {
info= fgFailedInstallPath.get(installPath);
if (info == null) {
info = generateLibraryInfo(rubyHome, rubyExecutable);
if (info == null) {
info = getDefaultLibraryInfo(rubyHome);
fgFailedInstallPath.put(installPath, info);
} else {
LaunchingPlugin.setLibraryInfo(this, installPath, info);
}
}
}
return info;
}
public File findExecutable(File installLocation) {
return findRubyExecutable(installLocation);
}
public String getVMPlatform(File installLocation, File executable) {
return "jruby";
}
}