package com.aptana.radrails.cloud.internal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.radrails.rails.internal.ui.console.IRailsShellConstants;
import org.radrails.rails.ui.console.RailsShellCommandProvider;
import org.rubypeople.rdt.launching.IRubyLaunchConfigurationConstants;
import org.rubypeople.rdt.launching.RubyRuntime;
import com.aptana.ide.core.model.user.AptanaUser;
import com.aptana.ide.core.model.user.User;
import com.aptana.ide.server.cloud.services.model.studio.StudioSite;
import com.aptana.radrails.cloud.Activator;
import com.aptana.rdt.AptanaRDTPlugin;
import com.aptana.rdt.core.gems.Gem;
import com.aptana.rdt.core.gems.IGemManager;
import com.aptana.rdt.core.gems.Version;
public abstract class CloudUtil
{
private static final String APTANA_SITE_ENV = "APTANA_SITE";
private static final String APTANA_PASSWORD_ENV = "APTANA_PASSWORD";
private static final String APTANA_ID_ENV = "APTANA_ID";
private static final String MINIMUM_RUBYGEMS_VERSION = "1.3.1";
private static final String MINIMUM_APCLOUD_GEM_VERSION = "1.0.8";
private static final String ADD_APTANA_GEMS_SOURCE_COMMAND = "sources -a http://gems.aptana.com";
private static final String APCLOUD_GEM_NAME = "aptana_cloud";
public static final String CAPFILE = "Capfile";
/**
* Checks if the required cloud gem is installed. If not it then tries to install it. This method will block until
* the gem is installed! RUN INSIDE A JOB!
*
* @param monitor
* the progress monitor to use for reporting progress to the user. It is the caller's responsibility to
* call done() on the given monitor. Accepts <code>null</code>, indicating that no progress should be
* reported and that the operation cannot be cancelled.
* @return IStatus indicating if gem is present (OK), CANCEL if monitor is canceled, or not OK if we were unable to
* determine if gem was installed, or were unable to install it.
*/
public static IStatus installCloudGemIfNecessary(IProgressMonitor monitor)
{
if (monitor == null)
monitor = new NullProgressMonitor();
monitor.beginTask("Installing aptana_cloud gem", 100);
IGemManager gemManager = AptanaRDTPlugin.getDefault().getGemManager();
if (gemManager == null)
return new Status(IStatus.ERROR, Activator.PLUGIN_ID, -1, "Unable to find a gem manager.", null);
monitor.worked(5);
if (gemManager.gemInstalled(APCLOUD_GEM_NAME))
{
return updateApcloudGem(gemManager, monitor);
}
monitor.worked(10);
IStatus result = null;
// Detect rubygems version. If older than 1.3.1 upgrade rubygems
if (rubygemsOutOfDate(gemManager))
{
result = upgradeRubyGems(gemManager, monitor);
}
monitor.worked(35);
if (result != null && !result.isOK())
return result;
IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 5);
result = addAptanaSource(gemManager, monitor);
subMonitor.done();
if (!result.isOK())
return result;
subMonitor = new SubProgressMonitor(monitor, 45);
result = gemManager.installGem(new Gem(APCLOUD_GEM_NAME, Gem.ANY_VERSION, null), subMonitor);
subMonitor.done();
return result;
}
private static boolean rubygemsOutOfDate(IGemManager gemManager)
{
Version rubygemsVersion = gemManager.getVersion();
if (rubygemsVersion == null || rubygemsVersion.isLessThan(MINIMUM_RUBYGEMS_VERSION))
{
return true;
}
return false;
}
private static IStatus upgradeRubyGems(IGemManager gemManager, IProgressMonitor monitor)
{
return gemManager.updateSystem(monitor);
}
private static IStatus updateApcloudGem(IGemManager gemManager, IProgressMonitor monitor)
{
if (minimumApcloudVersionInstalled(gemManager))
return Status.OK_STATUS;
// Upgrade it!
IStatus result = gemManager.update(new Gem(APCLOUD_GEM_NAME, Gem.ANY_VERSION, null), monitor);
if (result != null && !result.isOK())
return result;
// Check that it really was successful
if (!minimumApcloudVersionInstalled(gemManager))
return new Status(IStatus.ERROR, Activator.PLUGIN_ID, -1, "Failed to update aptana_cloud gem to "
+ MINIMUM_APCLOUD_GEM_VERSION, null);
return Status.OK_STATUS;
}
private static boolean minimumApcloudVersionInstalled(IGemManager gemManager)
{
List<Version> versions = gemManager.getVersions(APCLOUD_GEM_NAME);
for (Version version : versions)
{
if (version.isGreaterThanOrEqualTo(MINIMUM_APCLOUD_GEM_VERSION))
{
return true;
}
}
return false;
}
/**
* Adds gems.aptana.com as a gem source.
*
* @param gemManager
* @param monitor
* @return
*/
private static IStatus addAptanaSource(IGemManager gemManager, IProgressMonitor monitor)
{
// TODO Add this to IGemManager!
try
{
ILaunchConfiguration config = gemManager.run(ADD_APTANA_GEMS_SOURCE_COMMAND);
ILaunchConfigurationWorkingCopy wc = config.getWorkingCopy();
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_USE_TERMINAL, IRailsShellConstants.TERMINAL_ID);
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_TERMINAL_COMMAND, ADD_APTANA_GEMS_SOURCE_COMMAND);
config = wc.doSave();
final ILaunch launch = config.launch(ILaunchManager.RUN_MODE, new NullProgressMonitor());
while (!launch.isTerminated())
{
if (monitor.isCanceled())
{
launch.terminate();
return Status.CANCEL_STATUS;
}
Thread.yield();
}
// Check exit status
if (launch.getProcesses() != null && launch.getProcesses()[0] != null
&& launch.getProcesses()[0].getExitValue() != 0)
return new Status(IStatus.ERROR, Activator.PLUGIN_ID, -1,
"Adding http://gems.aptana.com as a gem source failed", null);
}
catch (CoreException e)
{
return e.getStatus();
}
return Status.OK_STATUS;
}
/**
* Creates an environment variable map to use in launch configurations for cloud related commands to pre-populate
* the username, password and site id.
*
* @param site
* @return
*/
public static Map<String, String> getEnvMap(StudioSite site)
{
Map<String, String> envMap = new HashMap<String, String>();
User user = AptanaUser.getSignedInUser();
if (user != null)
{
envMap.put(APTANA_ID_ENV, user.getUsername());
envMap.put(APTANA_PASSWORD_ENV, user.getPassword());
}
if (site != null)
{
envMap.put(APTANA_SITE_ENV, site.getId());
}
return envMap;
}
public static ILaunch run(String command, String args, IProject project, Map<String, String> envMap,
boolean forceRefresh) throws CoreException
{
return run(command, args, project, envMap, forceRefresh, false);
}
public static ILaunch run(String command, String args, IProject project, Map<String, String> envMap,
boolean forceRefresh, boolean isSudo) throws CoreException
{
String commandLine = command + ' ' + args;
String workingDirectory = null;
if (project != null)
{
workingDirectory = project.getLocation().toFile().getAbsolutePath();
}
String fileName = RailsShellCommandProvider.getFileIfExists(command, workingDirectory, project);
ILaunchConfigurationType configType = getRubyApplicationConfigType();
ILaunchConfigurationWorkingCopy wc = configType.newInstance(null, RubyRuntime
.generateUniqueLaunchConfigurationNameFrom(commandLine));
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_FILE_NAME, fileName);
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_VM_INSTALL_NAME, RubyRuntime.getDefaultVMInstall()
.getName());
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_VM_INSTALL_TYPE, RubyRuntime.getDefaultVMInstall()
.getVMInstallType().getId());
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, args);
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, ""); //$NON-NLS-1$
if (envMap != null && !envMap.isEmpty())
{
wc.setAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true);
wc.setAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, envMap);
}
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_TERMINAL_COMMAND, commandLine);
if (project != null)
{
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_PROJECT_NAME, project.getName());
}
if (workingDirectory != null)
{
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, workingDirectory);
}
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_IS_SUDO, false);
wc.setAttribute(IDebugUIConstants.ATTR_PRIVATE, true);
wc.setAttribute(IDebugUIConstants.ATTR_LAUNCH_IN_BACKGROUND, false);
if (forceRefresh)
{
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_REQUIRES_REFRESH, true);
}
wc.setAttribute(IRubyLaunchConfigurationConstants.ATTR_USE_TERMINAL, IRailsShellConstants.TERMINAL_ID);
ILaunchConfiguration config = wc.doSave();
return config.launch(ILaunchManager.RUN_MODE, new NullProgressMonitor());
}
private static ILaunchConfigurationType getRubyApplicationConfigType()
{
return getLaunchManager().getLaunchConfigurationType(IRubyLaunchConfigurationConstants.ID_RUBY_APPLICATION);
}
private static ILaunchManager getLaunchManager()
{
return DebugPlugin.getDefault().getLaunchManager();
}
}