blob: f8c3bc9f9eac2db3224920764cb8e91118287053 [file] [log] [blame]
package org.eclipse.jem.internal.proxy.remote;
/***************************************************************************************************
* Copyright (c) 2001, 2003 IBM Corporation and others. All rights reserved. This program and the
* accompanying materials are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors: IBM Corporation - initial API and implementation
**************************************************************************************************/
/*
* $RCSfile: ProxyRegistration.java,v $ $Revision: 1.5 $ $Date: 2004/02/14 18:37:14 $
*/
import java.io.IOException;
import java.net.*;
import java.text.MessageFormat;
import java.util.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.*;
import org.eclipse.debug.core.model.*;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.launching.StandardVMRunner;
import org.eclipse.jdt.launching.*;
import org.eclipse.jem.internal.proxy.core.*;
import org.eclipse.jem.internal.proxy.remote.awt.REMRegisterAWT;
/**
* This is the registration class for starting a Remote VM.
*/
public class ProxyRegistration implements IRegistration {
/**
* This will create a remote VM and return an initialized REMProxyFactoryRegistry. Passed in
* are: project: The project this is being started on. Must not be null and must be a
* JavaProject. attachAWT: Should AWT be attached to this implementation. contributors:
* Contributors to the configuration. Can be null. pm: ProgressMonitor to use. Must not be
* null. vmName: Name for the vm. Can be null.
*/
public ProxyFactoryRegistry startImplementation(
final IConfigurationContributor[] contributors,
boolean attachAWT,
IProject project,
String vmName,
IProgressMonitor pm)
throws CoreException {
// Not NLS'd because name is only used in debugging.
String name = null;
if (vmName == null)
name = MessageFormat.format(ProxyRemoteMessages.getString("ProxyRemoteVMName"), new Object[] { project.getName()}); //$NON-NLS-1$
else
name = MessageFormat.format(ProxyRemoteMessages.getString("ProxyRemoteVMNameWithComment"), new Object[] { project.getName(), vmName }); //$NON-NLS-1$
// Problem with launch, can't have double-quotes in vmName.
if (name.indexOf('"') != -1)
name = name.replace('"', '\'');
pm.beginTask(MessageFormat.format(ProxyRemoteMessages.getString("ProxyRemoteLaunchVM"), new Object[] { name }), 500); //$NON-NLS-1$
try {
// See if build needed or waiting or inprogress, if so, wait for it to complete. We've
// decided
// too difficult to determine if build would affect us or not, so just wait.
handleBuild(new SubProgressMonitor(pm, 100));
final IJavaProject javaProject = JavaCore.create(project);
// Add in the paths for the project
final ArrayList classPaths = new ArrayList(Arrays.asList(JavaRuntime.computeDefaultRuntimeClassPath(javaProject)));
final IClasspathContributionController controller = new IClasspathContributionController() {
public IJavaProject getJavaProject() {
return javaProject;
}
public void contributeProject(IProject project, List classpaths, int insertBeforeIndex) throws CoreException {
List projectPaths = new ArrayList(Arrays.asList(JavaRuntime.computeDefaultRuntimeClassPath(JavaCore.create(project))));
// Now we need to add to the list of paths, but we don't want to add dups.
Iterator itr2 = projectPaths.iterator();
while (itr2.hasNext()) {
Object path = itr2.next();
if (classpaths.contains(path))
itr2.remove(); // Don't add it
}
if (insertBeforeIndex == -1)
classpaths.addAll(projectPaths);
else
classpaths.addAll(insertBeforeIndex, projectPaths);
}
public void contributeClasspath(String classpath, List classpaths, int insertBeforeIndex) {
if (!classpaths.contains(classpath))
if (insertBeforeIndex == -1)
classpaths.add(classpath);
else
classpaths.add(insertBeforeIndex, classpath);
}
public void contributeClasspath(String[] classpathsToAdd, List classpaths, int insertBeforeIndex) {
for (int i = 0; i < classpathsToAdd.length; i++) {
contributeClasspath(classpathsToAdd[i], classpaths, insertBeforeIndex);
}
}
public void contributeClasspath(URL[] classpathsURLs, List classpaths, int insertBeforeIndex) {
for (int i = 0; i < classpathsURLs.length; i++) {
contributeClasspath(classpathsURLs[i].getFile(), classpaths, insertBeforeIndex);
}
}
};
// Add in any classpaths the contributors want to add.
if (contributors != null) {
for (int i = 0; i < contributors.length; i++) {
// Run in safe mode so that anything happens we don't go away.
final int ii = i;
Platform.run(new ISafeRunnable() {
public void handleException(Throwable exception) {
// Don't need to do anything. Platform.run logs it for me.
}
public void run() throws Exception {
contributors[ii].contributeClasspaths(classPaths, controller);
}
});
}
}
// Add in the required ones by the Proxy support. These are hard-coded since they are
// required.
ProxyRemoteUtil.updateClassPaths(classPaths, controller);
pm.worked(100); // Worked the classpaths step
final VMRunnerConfiguration config = new VMRunnerConfiguration("org.eclipse.jem.internal.proxy.vm.remote.RemoteVMApplication", (String[]) classPaths.toArray(new String[classPaths.size()])); //$NON-NLS-1$
REMProxyFactoryRegistry registry = new REMProxyFactoryRegistry(ProxyRemoteUtil.getRegistryController(), name);
Integer registryKey = registry.getRegistryKey();
Integer bufSize = Integer.getInteger("proxyvm.bufsize"); //$NON-NLS-1$
if (bufSize == null)
bufSize = new Integer(16000);
int masterServerPort = ProxyRemoteUtil.getRegistryController().getMasterSocketPort();
config.setVMArguments(new String[] { "-Dproxyvm.registryKey=" + registryKey, "-Dproxyvm.masterPort=" + String.valueOf(masterServerPort), "-Dproxyvm.bufsize=" + bufSize, "-Dproxyvm.servername=" + name }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
// Let contributors modify the configuration.
if (contributors != null) {
for (int i = 0; i < contributors.length; i++) {
// Run in safe mode so that anything happens we don't go away.
final int ii = i;
Platform.run(new ISafeRunnable() {
public void handleException(Throwable exception) {
// Don't need to do anything. Platform.run logs it for me.
}
public void run() throws Exception {
contributors[ii].contributeToConfiguration(config);
}
});
}
}
// See if debug mode is requested.
DebugModeHelper dh = new DebugModeHelper();
boolean debugMode = dh.debugMode(name);
// If in debug mode, we need to find a port for it to use.
int dport = -1;
if (debugMode) {
dport = findUnusedLocalPort("localhost", 5000, 15000, new int[0]); //$NON-NLS-1$
String[] dArgs = new String[] { "-Djava.compiler=NONE", "-Xdebug", "-Xnoagent", "-Xrunjdwp:transport=dt_socket,server=y,address=" + dport }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
String[] newVMArgs = new String[config.getVMArguments().length + dArgs.length];
System.arraycopy(config.getVMArguments(), 0, newVMArgs, 0, config.getVMArguments().length);
System.arraycopy(dArgs, 0, newVMArgs, config.getVMArguments().length, dArgs.length);
config.setVMArguments(newVMArgs);
}
IVMRunner runner = null;
JavaRuntime.getVMInstallTypes(); // Needed to force initialization of VM types.
IVMInstall vm = JavaRuntime.getVMInstall(javaProject);
if (vm == null)
vm = JavaRuntime.getDefaultVMInstall();
if (vm != null)
runner = vm.getVMRunner(ILaunchManager.RUN_MODE);
if (runner instanceof StandardVMRunner) {
runner = new ProxyVMRunner(vm);
}
if (runner == null) {
// this is bad, we will pop up a msg.
String msg = null;
if (vm == null)
msg = MessageFormat.format(ProxyRemoteMessages.getString("Proxy_NoVM_ERROR_"), new Object[] { name }); //$NON-NLS-1$
else
msg = MessageFormat.format(ProxyRemoteMessages.getString("Proxy_NoRunner_ERROR_"), new Object[] { name }); //$NON-NLS-1$
dh.displayErrorMessage(ProxyRemoteMessages.getString("Proxy_Error_Title"), msg); //$NON-NLS-1$
ProxyPlugin.getPlugin().getMsgLogger().log(
new Status(IStatus.ERROR, ProxyPlugin.getPlugin().getDescriptor().getUniqueIdentifier(), 0, msg, null));
return null; // Can't find the requested VM.
}
ILaunchConfigurationType lcType =
DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurationType(
IJavaLaunchConfigurationConstants.ID_JAVA_APPLICATION);
ILaunchConfigurationWorkingCopy wc = lcType.newInstance(null, vmName);
applyDebugUIConstants(wc);
ILaunch runresult = new Launch(wc, ILaunchManager.RUN_MODE, null);
// See if we have any environment variables
Map vmMap = config.getVMSpecificAttributesMap();
if (vmMap != null) {
String[] environmentVariables = (String[]) vmMap.get(ProxyPlugin.ENVIRONMENT_VARIABLE);
if (environmentVariables != null) {
((ProxyVMRunner) runner).fEnvironmentVariables = environmentVariables;
}
}
pm.worked(100); // Worked Setup launch step
// Launch the vm.
IProgressMonitor spm = new SubProgressMonitor(pm, 100);
runner.run(config, runresult, spm);
if (runresult != null) {
IProcess[] processes = runresult.getProcesses();
IProcess process = processes[0]; // There is only one.
// Check if it is already terminated. If it is, then there was a bad error, so just
// print out the results from it.
if (process.isTerminated()) {
IStreamsProxy stProxy = process.getStreamsProxy();
// Using a printWriter for println capability, but it needs to be on another
// writer, which will be string
java.io.StringWriter s = new java.io.StringWriter();
java.io.PrintWriter w = new java.io.PrintWriter(s);
w.println(ProxyRemoteMessages.getString(ProxyRemoteMessages.VM_TERMINATED));
w.println(ProxyRemoteMessages.getString(ProxyRemoteMessages.VM_TERMINATED_LINE1));
w.println(stProxy.getErrorStreamMonitor().getContents());
w.println(ProxyRemoteMessages.getString(ProxyRemoteMessages.VM_TERMINATED_LINE2));
w.println(stProxy.getOutputStreamMonitor().getContents());
w.println(ProxyRemoteMessages.getString(ProxyRemoteMessages.VM_TERMINATED_LINE3));
w.close();
String msg = MessageFormat.format(ProxyRemoteMessages.getString("Proxy_Terminated_too_soon_ERROR_"), new Object[] { name }); //$NON-NLS-1$
dh.displayErrorMessage(ProxyRemoteMessages.getString("Proxy_Error_Title"), msg); //$NON-NLS-1$
ProxyPlugin.getPlugin().getMsgLogger().log(
new Status(IStatus.WARNING, ProxyPlugin.getPlugin().getDescriptor().getUniqueIdentifier(), 0, s.toString(), null));
return null;
} else {
final String traceName = name;
IStreamsProxy fStreamsProxy = process.getStreamsProxy();
class StreamListener implements IStreamListener {
String tracePrefix;
int level;
public StreamListener(String type, int level) {
tracePrefix = traceName + ':' + type + '>';
this.level = level;
}
public void streamAppended(String newText, IStreamMonitor monitor) {
ProxyPlugin.getPlugin().getMsgLogger().log(
new Status(
level,
ProxyPlugin.getPlugin().getDescriptor().getUniqueIdentifier(),
0,
tracePrefix + newText,
null));
}
};
// Always listen to System.err output.
IStreamMonitor monitor = fStreamsProxy.getErrorStreamMonitor();
if (monitor != null)
monitor.addListener(new StreamListener("err", IStatus.WARNING)); //$NON-NLS-1$
// If debug trace is requested, then attach trace listener for System.out
if ("true".equalsIgnoreCase(Platform.getDebugOption(ProxyPlugin.getPlugin().getDescriptor().getUniqueIdentifier() + ProxyRemoteUtil.DEBUG_VM_TRACEOUT))) { //$NON-NLS-1$
// Want to trace the output of the remote vm's.
monitor = fStreamsProxy.getOutputStreamMonitor();
if (monitor != null)
monitor.addListener(new StreamListener("out", IStatus.INFO)); //$NON-NLS-1$
}
}
// If in debug mode, tester must start debugger before going on.
if (debugMode) {
if (!dh.promptPort(dport)) {
process.terminate();
return null;
}
}
// Now set up the registry.
registry.initializeRegistry(process);
new REMStandardBeanTypeProxyFactory(registry);
new REMStandardBeanProxyFactory(registry);
new REMMethodProxyFactory(registry);
if (debugMode || REMProxyFactoryRegistry.fGlobalNoTimeouts)
registry.fNoTimeouts = true;
if (attachAWT)
REMRegisterAWT.registerAWT(registry);
if (contributors != null) {
final ProxyFactoryRegistry reg = registry;
for (int i = 0; i < contributors.length; i++) {
final int ii = i;
// Run in safe mode so that anything happens we don't go away.
Platform.run(new ISafeRunnable() {
public void handleException(Throwable exception) {
// Don't need to do anything. Platform.run logs it for me.
}
public void run() throws Exception {
contributors[ii].contributeToRegistry(reg);
}
});
}
}
return registry;
} else {
// this is bad, we will pop up a msg.
String msg = MessageFormat.format(ProxyRemoteMessages.getString("PRoxy_NoLaunch_ERROR_"), new Object[] { name }); //$NON-NLS-1$
dh.displayErrorMessage(ProxyRemoteMessages.getString("Proxy_Error_Title"), msg); //$NON-NLS-1$
ProxyPlugin.getPlugin().getMsgLogger().log(
new Status(IStatus.ERROR, ProxyPlugin.getPlugin().getDescriptor().getUniqueIdentifier(), 0, msg, null));
return null; // Couldn't launch for some reason.
}
} finally {
pm.done();
}
}
private void handleBuild(IProgressMonitor pm) throws CoreException {
boolean autobuilding = ResourcesPlugin.getWorkspace().isAutoBuilding();
if (!autobuilding) {
// We are not autobuilding. So kick off a build right here and
// wait for it.
ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, pm);
} else {
Job[] build = Platform.getJobManager().find(ResourcesPlugin.FAMILY_AUTO_BUILD);
if (build.length == 1) {
if (build[0].getState() == Job.RUNNING || build[0].getState() == Job.WAITING || build[0].getState() == Job.SLEEPING) {
pm.beginTask(ProxyRemoteMessages.getString("ProxyRemoteWaitForBuild"), 100); //$NON-NLS-1$
try {
build[0].join();
} catch (InterruptedException e) {
throw new CoreException(
new Status(IStatus.ERROR, ProxyPlugin.getPlugin().getDescriptor().getUniqueIdentifier(), IStatus.ERROR, "", e)); //$NON-NLS-1$
} finally {
pm.done();
}
}
}
}
}
// Utilities to find the free port
private static final Random fgRandom = new Random(System.currentTimeMillis());
private static int findUnusedLocalPort(String host, int searchFrom, int searchTo, int[] exclude) {
for (int i = 0; i < 10; i++) {
int port = 0;
newport : while (true) {
port = getRandomPort(searchFrom, searchTo);
if (exclude != null)
for (int e = 0; e < exclude.length; e++)
if (port == exclude[e])
continue newport;
break;
}
try {
new Socket(host, port);
} catch (ConnectException e) {
return port;
} catch (IOException e) {
}
}
return -1;
}
private static int getRandomPort(int low, int high) {
return (int) (fgRandom.nextFloat() * (high - low)) + low;
}
protected static String makeAbsolute(IWorkspace ws, IPath path) {
IResource resource = ws.getRoot().findMember(path);
if (resource != null)
return resource.getLocation().toOSString();
return path.toOSString();
}
private static String ATTR_TARGET_RUN_PERSPECTIVE, ATTR_PRIVATE;
static {
ATTR_TARGET_RUN_PERSPECTIVE = ATTR_PRIVATE = null;
try {
// So that we can run headless (w/o ui), need to do class forName for debugui contants
Class debugUIConstants = Class.forName("org.eclipse.debug.ui.IDebugUIConstants"); //$NON-NLS-1$
ATTR_TARGET_RUN_PERSPECTIVE = (String) debugUIConstants.getField("ATTR_TARGET_RUN_PERSPECTIVE").get(null); //$NON-NLS-1$
ATTR_PRIVATE = (String) debugUIConstants.getField("ATTR_PRIVATE").get(null); //$NON-NLS-1$
} catch (SecurityException e) {
} catch (ClassNotFoundException e) {
} catch (NoSuchFieldException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
}
private void applyDebugUIConstants(ILaunchConfigurationWorkingCopy wc) {
if (ATTR_TARGET_RUN_PERSPECTIVE != null)
wc.setAttribute(ATTR_TARGET_RUN_PERSPECTIVE, (String) null);
if (ATTR_PRIVATE != null)
wc.setAttribute(ATTR_PRIVATE, true);
}
}