| author | Marc Aubry | 2012-09-28 08:18:10 (EDT) |
|---|---|---|
| committer | sbernard | 2012-09-28 08:18:10 (EDT) |
| commit | 3d0b93879ecdafe0087d429f40812337f3912507 (patch) (side-by-side diff) | |
| tree | 0689de3edd3f408e124b43ec6fd4d0716cc101f8 | |
| parent | 443b80ba8d881bc8b145546ab381ebbcf28fca3a (diff) | |
| download | org.eclipse.koneki.ldt-3d0b93879ecdafe0087d429f40812337f3912507.zip org.eclipse.koneki.ldt-3d0b93879ecdafe0087d429f40812337f3912507.tar.gz org.eclipse.koneki.ldt-3d0b93879ecdafe0087d429f40812337f3912507.tar.bz2 | |
bug 390674: [remote] Remote launch configuration
26 files changed, 2041 insertions, 11 deletions
diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.core/META-INF/MANIFEST.MF b/plugins/org.eclipse.koneki.ldt.remote.debug.core/META-INF/MANIFEST.MF index 14f7a04..be26fa0 100644 --- a/plugins/org.eclipse.koneki.ldt.remote.debug.core/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.core/META-INF/MANIFEST.MF @@ -1,11 +1,24 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name -Bundle-SymbolicName: org.eclipse.koneki.ldt.remote.debug.core +Bundle-SymbolicName: org.eclipse.koneki.ldt.remote.debug.core;singleton:=true Bundle-Version: 0.9.0.qualifier Bundle-Activator: org.eclipse.koneki.ldt.remote.debug.core.internal.Activator -Require-Bundle: org.eclipse.core.runtime +Require-Bundle: org.eclipse.core.runtime, + org.eclipse.debug.core, + org.eclipse.rse.core;bundle-version="3.3.0", + org.eclipse.rse;bundle-version="3.4.0", + org.eclipse.rse.services;bundle-version="3.2.200", + org.eclipse.rse.subsystems.files.core;bundle-version="3.3.0", + com.jcraft.jsch;bundle-version="0.1.46", + org.eclipse.rse.ui;bundle-version="3.3.0", + org.eclipse.rse.connectorservice.ssh;bundle-version="2.1.300", + org.eclipse.rse.services.ssh;bundle-version="3.2.0", + org.eclipse.koneki.ldt;bundle-version="0.9.0", + org.eclipse.dltk.core;bundle-version="3.0.0", + org.eclipse.dltk.launching;bundle-version="4.0.0", + org.eclipse.dltk.debug;bundle-version="4.0.0" Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-Vendor: %Bundle-Vendor -Export-Package: org.eclipse.koneki.ldt.remote.debug.core.internal;x-internal:=true +Export-Package: org.eclipse.koneki.ldt.remote.debug.core.internal;x-friends:="org.eclipse.koneki.ldt.remote.debug.ui" diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.core/OSGI-INF/l10n/bundle.properties b/plugins/org.eclipse.koneki.ldt.remote.debug.core/OSGI-INF/l10n/bundle.properties index 703d5a5..4bea453 100644 --- a/plugins/org.eclipse.koneki.ldt.remote.debug.core/OSGI-INF/l10n/bundle.properties +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.core/OSGI-INF/l10n/bundle.properties @@ -1,3 +1,4 @@ #Properties file for org.eclipse.koneki.ldt.remote.debug.core
Bundle-Name = LDT Remote Development Support Debug Core (Incubation)
-Bundle-Vendor = Eclipse Koneki
\ No newline at end of file +Bundle-Vendor = Eclipse Koneki
+launchConfigurationType.name = Lua Remote Application
\ No newline at end of file diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.core/build.properties b/plugins/org.eclipse.koneki.ldt.remote.debug.core/build.properties index 2096750..e2c8cb9 100644 --- a/plugins/org.eclipse.koneki.ldt.remote.debug.core/build.properties +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.core/build.properties @@ -13,7 +13,8 @@ output.. = bin/ bin.includes = META-INF/,\
.,\
OSGI-INF/,\
- about.html
+ about.html,\
+ plugin.xml
# The java version of the source code
javacSource=1.6
# The java version of the byte code targeted
diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.core/plugin.xml b/plugins/org.eclipse.koneki.ldt.remote.debug.core/plugin.xml new file mode 100644 index 0000000..8a48cb3 --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.core/plugin.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.4"?> +<plugin> + <extension + point="org.eclipse.debug.core.launchConfigurationTypes"> + <launchConfigurationType + id="org.eclipse.koneki.ldt.remote.debug.core.luaremotedebug" + modes="run, debug" + name="%launchConfigurationType.name" + delegate="org.eclipse.koneki.ldt.remote.debug.core.internal.LuaRemoteLaunchConfigurationDelegate" + sourceLocatorId="org.eclipse.koneki.ldt.debug.core.SourceLookup" + sourcePathComputerId="org.eclipse.koneki.ldt.debug.core.SourcePathComputer"> + </launchConfigurationType> + </extension> + +</plugin> diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/InputStreamMonitor.java b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/InputStreamMonitor.java new file mode 100644 index 0000000..aa8296c --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/InputStreamMonitor.java @@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright (c) 2012 Sierra Wireless 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: + * Sierra Wireless - initial API and implementation + *******************************************************************************/ +//FIXME This class is a copy from eclipse.debug.core plugin +package org.eclipse.koneki.ldt.remote.debug.core.internal; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Vector; + +import org.eclipse.debug.core.DebugPlugin; + +/** + * Writes to the input stream of a system process, queueing output if the stream is blocked. + * + * The input stream monitor writes to system in via an output stream. + */ +public class InputStreamMonitor { + + /** + * The stream which is being written to (connected to system in). + */ + private OutputStream fStream; + /** + * The queue of output. + */ + private Vector<String> fQueue; + + /** + * The thread which writes to the stream. + */ + private Thread fThread; + /** + * A lock for ensuring that writes to the queue are contiguous + */ + private Object fLock; + + /** + * Whether the underlying output stream has been closed + */ + private boolean fClosed = false; + + /** + * The encoding of the input stream. + */ + private String fEncoding; + + /** + * Creates an input stream monitor which writes to system in via the given output stream. + * + * @param stream + * output stream + */ + public InputStreamMonitor(OutputStream stream) { + this(stream, null); + } + + /** + * Creates an input stream monitor which writes to system in via the given output stream. + * + * @param stream + * output stream + * @param encoding + * stream encoding or <code>null</code> for system default + */ + public InputStreamMonitor(OutputStream stream, String encoding) { + fStream = stream; + fQueue = new Vector<String>(); + fLock = new Object(); + fEncoding = encoding; + } + + /** + * Appends the given text to the stream, or queues the text to be written at a later time if the stream is blocked. + * + * @param text + * text to append + */ + public void write(String text) { + synchronized (fLock) { + fQueue.add(text); + fLock.notifyAll(); + } + } + + /** + * Starts a thread which writes the stream. + */ + public void startMonitoring() { + if (fThread == null) { + fThread = new Thread(new Runnable() { + public void run() { + write(); + } + }, "Input Stream Monitor"); //$NON-NLS-1$ + fThread.setDaemon(true); + fThread.start(); + } + } + + /** + * Close all communications between this monitor and the underlying stream. + */ + public void close() { + if (fThread != null) { + Thread thread = fThread; + fThread = null; + thread.interrupt(); + } + } + + /** + * Continuously writes to the stream. + */ + protected void write() { + while (fThread != null) { + writeNext(); + } + if (!fClosed) { + try { + fStream.close(); + } catch (IOException e) { + DebugPlugin.log(e); + } + } + } + + /** + * Write the text in the queue to the stream. + */ + protected void writeNext() { + while (!fQueue.isEmpty() && !fClosed) { + String text = (String) fQueue.firstElement(); + fQueue.removeElementAt(0); + try { + if (fEncoding != null) + fStream.write(text.getBytes(fEncoding)); + else + fStream.write(text.getBytes()); + fStream.flush(); + } catch (IOException e) { + DebugPlugin.log(e); + } + } + try { + synchronized (fLock) { + fLock.wait(); + } + // CHECKSTYLE:OFF + } catch (InterruptedException e) { + // CHECKSTYLE:ON + } + + } + + /** + * Closes the output stream attached to the standard input stream of this monitor's process. + * + * @exception IOException + * if an exception occurs closing the input stream + */ + public void closeInputStream() throws IOException { + if (!fClosed) { + fClosed = true; + fStream.close(); + } else { + throw new IOException(); + } + + } +} diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/LuaRSEUtil.java b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/LuaRSEUtil.java new file mode 100644 index 0000000..4d5fc49 --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/LuaRSEUtil.java @@ -0,0 +1,77 @@ +/*******************************************************************************
+ * Copyright (c) 2012 Sierra Wireless 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:
+ * Sierra Wireless - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.koneki.ldt.remote.debug.core.internal;
+
+import java.util.EnumSet;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.dltk.core.IScriptProject;
+import org.eclipse.koneki.ldt.core.IProjectSourceVisitor;
+import org.eclipse.koneki.ldt.core.LuaUtils;
+import org.eclipse.koneki.ldt.core.LuaUtils.ProjectFragmentFilter;
+import org.eclipse.rse.services.clientserver.messages.SystemMessageException;
+import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFile;
+import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFileSubSystem;
+
+/**
+ * Utility class, contains some helpers to use RSE
+ */
+public final class LuaRSEUtil {
+ private LuaRSEUtil() {
+ }
+
+ /**
+ * upload of the given script project and all dependencies (define in buildpath) except for archive
+ *
+ * @throws CoreException
+ */
+ public static void uploadFiles(final IRemoteFileSubSystem subsystem, final IScriptProject project, final String destinationFolderPath,
+ final IProgressMonitor monitor) throws CoreException {
+ final IProjectSourceVisitor visitor = new IProjectSourceVisitor() {
+
+ @Override
+ public void processFile(IPath absolutePath, IPath relativePath, String charset, IProgressMonitor monitor) throws CoreException {
+ final String destinationPath = destinationFolderPath + subsystem.getSeparator() + relativePath.toPortableString();
+ final String destinationEncoding = subsystem.getRemoteEncoding();
+ final SubMonitor subMonitor = SubMonitor.convert(monitor, 1);
+ try {
+ // Create file directory
+ subsystem.upload(absolutePath.toOSString(), charset, destinationPath, destinationEncoding, subMonitor.newChild(1));
+ } catch (final SystemMessageException e) {
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Unable to upload files", e)); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ public void processDirectory(IPath absolutePath, IPath relativePath, IProgressMonitor monitor) throws CoreException {
+ final String innerDestinationFolderPath = destinationFolderPath + subsystem.getSeparator() + relativePath.toPortableString();
+ final SubMonitor subMonitor = SubMonitor.convert(monitor, 1);
+ try {
+ // Create remote directory
+ final IRemoteFile remoteFolder = subsystem.getRemoteFileObject(innerDestinationFolderPath, subMonitor);
+ subsystem.createFolder(remoteFolder, subMonitor.newChild(1));
+ } catch (final SystemMessageException e) {
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Unable to upload files", e)); //$NON-NLS-1$
+ }
+
+ }
+
+ };
+
+ LuaUtils.visitSourceFiles(project,
+ EnumSet.complementOf(EnumSet.of(ProjectFragmentFilter.ARCHIVE, ProjectFragmentFilter.EXECUTION_ENVIRONMENT)), visitor, monitor);
+ }
+}
diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/LuaRemoteDebugConstant.java b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/LuaRemoteDebugConstant.java new file mode 100644 index 0000000..92858a6 --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/LuaRemoteDebugConstant.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2012 Sierra Wireless 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: + * Sierra Wireless - initial API and implementation + *******************************************************************************/ +package org.eclipse.koneki.ldt.remote.debug.core.internal; + +public interface LuaRemoteDebugConstant { + + String HOST_ID = Activator.PLUGIN_ID + ".debug.hostid"; //$NON-NLS-1$ + String PROJECT_NAME = null; + String ENV_VAR_KEY_DBGP_IDE_KEY = null; + String ENV_VAR_KEY_DBGP_IDE_PORT = null; + +} diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/LuaRemoteLaunchConfigurationDelegate.java b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/LuaRemoteLaunchConfigurationDelegate.java new file mode 100644 index 0000000..6503b9b --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/LuaRemoteLaunchConfigurationDelegate.java @@ -0,0 +1,225 @@ +/******************************************************************************* + * Copyright (c) 2012 Sierra Wireless 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: + * Sierra Wireless - initial API and implementation + *******************************************************************************/ +package org.eclipse.koneki.ldt.remote.debug.core.internal; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.Charset; +import java.text.MessageFormat; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchManager; +import org.eclipse.debug.core.model.LaunchConfigurationDelegate; +import org.eclipse.dltk.core.DLTKCore; +import org.eclipse.dltk.core.IScriptProject; +import org.eclipse.dltk.dbgp.DbgpSessionIdGenerator; +import org.eclipse.dltk.debug.core.DLTKDebugPlugin; +import org.eclipse.rse.core.model.IHost; +import org.eclipse.rse.services.clientserver.messages.SystemMessageException; +import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFile; +import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFileSubSystem; +import org.osgi.framework.Bundle; + +import com.jcraft.jsch.Session; + +public class LuaRemoteLaunchConfigurationDelegate extends LaunchConfigurationDelegate { + private static final String[] DEBUG_FILES = { "/script/debugintrospection.lua", "script/debugger.lua" }; //$NON-NLS-1$ //$NON-NLS-2$ + + //private static final String DEBGUGGER_MODULE = "debugger"; //$NON-NLS-1$ + + /** + * @see org.eclipse.debug.core.model.ILaunchConfigurationDelegate#launch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String, + * org.eclipse.debug.core.ILaunch, org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException { + SubMonitor submonitor = SubMonitor.convert(monitor, 11); + try { + // wait RSE is initialized + // TODO not sure this is the good way to wait for init everywhere in the code + RSEUtil.waitForRSEInitialization(); + + // get configuration information + String projectName = configuration.getAttribute(LuaRemoteDebugConstant.PROJECT_NAME, "");//$NON-NLS-1$ + IHost host = LuaRemoteLaunchConfigurationUtil.getHost(configuration); + + @SuppressWarnings("rawtypes") + Map env = configuration.getAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, Collections.EMPTY_MAP); + + // valid configuration information + String validateLuaEmbeddedConfiguration = LuaRemoteLaunchConfigurationUtil.validateLuaEmbeddedConfiguration(projectName, host); + if (validateLuaEmbeddedConfiguration != null) + throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, validateLuaEmbeddedConfiguration)); + submonitor.worked(1); + + // get Project + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + + // get the first found remote file SubSystem + IRemoteFileSubSystem remoteFileSubSystem = RSEUtil.getRemoteFileSubsystem(host); + if (remoteFileSubSystem == null) + throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + Messages.LuaRemoteLaunchConfigurationDelegate_error_noremotefilesystem)); + + // try to connect to the target + try { + if (submonitor.isCanceled()) + return; + remoteFileSubSystem.connect(submonitor.newChild(1), false); + // CHECKSTYLE:OFF + } catch (Exception e) { + // CHECKSTYLE:ON + throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + Messages.LuaRemoteLaunchConfigurationDelegate_error_connectionfailed, e)); + } + + // compute the remote project workingdir + if (submonitor.isCanceled()) + return; + String remoteApplicationFolderPath = "/tmp/"; //$NON-NLS-1$ + String remoteapplicationpath = "/tmp/"; //$NON-NLS-1$ + + // kill Process if already running + // could happen if connection is closed and last process launch is not terminate + Session session = RSEUtil.getCurrentSshSession(host.getConnectorServices()); + SshProcess.killProcess(session, remoteApplicationFolderPath); + + // check an prepare remote folder + try { + if (submonitor.isCanceled()) + return; + IRemoteFile remoteApplicationPath = remoteFileSubSystem.getRemoteFileObject(remoteapplicationpath, submonitor.newChild(1)); + if (remoteApplicationPath.exists()) { + if (remoteApplicationPath.isFile()) { + throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + Messages.LuaRemoteLaunchConfigurationDelegate_error_filealreadyexist + remoteapplicationpath)); + } + } else { + remoteFileSubSystem.createFolder(remoteApplicationPath, submonitor.newChild(1)); + } + submonitor.setWorkRemaining(8); + + // remoteFile is a folder + // create(or delete and recreate) the working directory + if (submonitor.isCanceled()) + return; + IRemoteFile remoteWorkingFolder = remoteFileSubSystem.getRemoteFileObject(remoteApplicationFolderPath, submonitor.newChild(1)); + if (remoteWorkingFolder.exists()) { + if (remoteWorkingFolder.isFile()) { + throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, MessageFormat.format( + Messages.LuaRemoteLaunchConfigurationDelegate_error_filealreadyexist, remoteApplicationFolderPath))); + } else { + remoteFileSubSystem.delete(remoteWorkingFolder, submonitor.newChild(1)); + } + } + submonitor.setWorkRemaining(6); + remoteFileSubSystem.createFolder(remoteWorkingFolder, submonitor.newChild(1)); + + // create project application + if (submonitor.isCanceled()) + return; + + // upload sourcecode + IScriptProject scriptProject = DLTKCore.create(project); + LuaRSEUtil.uploadFiles(remoteFileSubSystem, scriptProject, remoteApplicationFolderPath, submonitor.newChild(3)); + + // upload Debug module + SubMonitor introspectionMonitor = submonitor.newChild(1); + introspectionMonitor.setWorkRemaining(DEBUG_FILES.length); + if (mode.equals(ILaunchManager.DEBUG_MODE)) { + String localEncoding = Charset.defaultCharset().name(); + String remoteEncoding = remoteFileSubSystem.getRemoteEncoding(); + for (String luaFile : DEBUG_FILES) { + try { + Bundle bundle = Platform.getBundle(Activator.PLUGIN_ID); + URL resource = bundle.getResource(luaFile); + File result = new File(FileLocator.toFileURL(resource).getPath()); + String remotePath = remoteApplicationFolderPath + remoteFileSubSystem.getSeparator() + result.getName(); + remoteFileSubSystem.upload(result.getAbsolutePath(), localEncoding, remotePath, remoteEncoding, submonitor.newChild(1)); + } catch (IOException e) { + throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + Messages.LuaRemoteLaunchConfigurationDelegate_error_unabletoloadintrospectionlib, e)); + } + } + } + } catch (SystemMessageException e) { + throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + Messages.LuaRemoteLaunchConfigurationDelegate_error_unabletoaccestoremoteapplicationdir + remoteapplicationpath, e)); + } + + // set environment var + String sessionID = DbgpSessionIdGenerator.generate(); + Map<String, String> envVars = new HashMap<String, String>(); + if (mode.equals(ILaunchManager.DEBUG_MODE)) { + // dbgp env vars + envVars.put(LuaRemoteDebugConstant.ENV_VAR_KEY_DBGP_IDE_KEY, sessionID); + envVars.put(LuaRemoteDebugConstant.ENV_VAR_KEY_DBGP_IDE_PORT, String.valueOf(DLTKDebugPlugin.getDefault().getDbgpService().getPort())); + } + for (Object oEntry : env.entrySet()) { + @SuppressWarnings("rawtypes") + Map.Entry entry = (Entry) oEntry; + envVars.put(entry.getKey().toString(), entry.getValue().toString()); + } + + // create lua execution command + // String mainRelativePath = LuaConstants.DEFAULT_MAIN_FILE; + // List<String> cmd = new ArrayList<String>(6); + // // FIXME is there a cleaner way to control buffering ? + // // see: http://lua-users.org/lists/lua-l/2011-05/msg00549.html + // String bootstrapCode = "io.stdout:setvbuf(\"line\");"; //$NON-NLS-1$ + + // TODO Retreive properties from target to create the ssh process + // cmd.add("lua"); + // if (mode.equals(ILaunchManager.DEBUG_MODE)) { + // // load debugging libraries. The -l parameter cannot be used here because the debugger MUST be the first module to be loaded + // bootstrapCode += " require(\"" + DEBGUGGER_MODULE + "\")();"; //$NON-NLS-1$//$NON-NLS-2$ + // } + // cmd.add("-e"); //$NON-NLS-1$ + // cmd.add(bootstrapCode); + // cmd.add(mainRelativePath); + // + // // Create Process + // if (submonitor.isCanceled()) + // return; + // + // SshProcess process = new SshProcess(session, launch, remoteApplicationFolderPath, cmd.toArray(new String[cmd.size()]), envVars); + // + // if (mode.equals(ILaunchManager.DEBUG_MODE)) { + // // Desactivate DBGP Stream redirection + // // TODO manage DBGP Stream redirection (so desactivate process redirection in debug mode) + // launch.setAttribute(DLTKDebugLaunchConstants.ATTR_DEBUG_CONSOLE, "false"); //$NON-NLS-1$ + // LuaRemoteDebuggingEngineRunner debugingEngine = new LuaRemoteDebuggingEngineRunner(process, sessionID, remoteApplicationFolderPath); + // debugingEngine.run(new InterpreterConfig(), launch, new NullProgressMonitor()); + // launch.addProcess(process); + // } else { + // process.start(); + // launch.addProcess(process); + // } + } finally { + submonitor.done(); + } + } +} diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/LuaRemoteLaunchConfigurationUtil.java b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/LuaRemoteLaunchConfigurationUtil.java new file mode 100644 index 0000000..77a4443 --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/LuaRemoteLaunchConfigurationUtil.java @@ -0,0 +1,123 @@ +/*******************************************************************************
+ * Copyright (c) 2012 Sierra Wireless 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:
+ * Sierra Wireless - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.koneki.ldt.remote.debug.core.internal;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.koneki.ldt.core.LuaNature;
+import org.eclipse.rse.core.RSECorePlugin;
+import org.eclipse.rse.core.model.IHost;
+import org.eclipse.rse.core.model.ISystemProfile;
+
+/**
+ * Utility class to handle ILaunchConfiguration for LuaEmbeddedLaunchConfiguration
+ */
+public final class LuaRemoteLaunchConfigurationUtil {
+
+ private LuaRemoteLaunchConfigurationUtil() {
+ }
+
+ /**
+ * set the HOST_ID attributes in the given launch configuration
+ *
+ * @param conf
+ * to edit
+ * @param host
+ * to save in the HOST_ID attribute
+ */
+ public static final void setConnectionId(ILaunchConfigurationWorkingCopy conf, IHost host) {
+ List<String> hostData = new ArrayList<String>();
+ hostData.add(host.getSystemProfileName());
+ hostData.add(host.getName());
+ conf.setAttribute(LuaRemoteDebugConstant.HOST_ID, hostData);
+ }
+
+ /**
+ * get the host identify by the given ILaunchConfiguration.
+ *
+ * @return the host defined for this ILaunchConfiguration or null if no host is found
+ */
+ public static final IHost getHost(ILaunchConfiguration conf) {
+ try {
+ List<?> hostData = conf.getAttribute(LuaRemoteDebugConstant.HOST_ID, Collections.EMPTY_LIST);
+
+ // list must contained 2 elements (profileName and host name)
+ if (hostData.size() != 2)
+ return null;
+
+ // get profile name
+ Object oProfileName = hostData.get(0);
+ if (!(oProfileName instanceof String))
+ return null;
+ String profileName = (String) oProfileName;
+
+ // get host name
+ Object oHost = hostData.get(1);
+ if (!(oHost instanceof String))
+ return null;
+ String hostID = (String) oHost;
+
+ // get profile
+ ISystemProfile systemProfile = RSECorePlugin.getTheSystemRegistry().getSystemProfile(profileName);
+ if (systemProfile == null)
+ return null;
+
+ // get host
+ IHost host = RSECorePlugin.getTheSystemRegistry().getHost(systemProfile, hostID);
+ return host;
+ } catch (CoreException e) {
+ return null;
+ }
+ }
+
+ /**
+ * internal method to validate the value of the current tab
+ *
+ * @param luaInspectorPort
+ *
+ * @return true if value is valid
+ */
+ public static String validateLuaEmbeddedConfiguration(String projectName, IHost host) {
+ // projectName validation
+ if (projectName == null || projectName.isEmpty()) {
+ return "A project must be selected.";
+ }
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+ if (project == null || !project.exists()) {
+ return "This project doesn't exist.";
+ }
+ if (!project.isOpen()) {
+ return "This project is not open.";
+ }
+ try {
+ if (!project.hasNature(LuaNature.ID)) {
+ return "This project is not a lua project.";
+ }
+ } catch (CoreException e) {
+ // must not append (at this line project is open and exist)
+ return null;
+ }
+
+ // target validation
+ if (host == null) {
+ return "An host must be selected.";
+ }
+ return null;
+ }
+
+}
diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/Messages.java b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/Messages.java new file mode 100644 index 0000000..24c2a99 --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/Messages.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2012 Sierra Wireless 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: + * Sierra Wireless - initial API and implementation + *******************************************************************************/ +package org.eclipse.koneki.ldt.remote.debug.core.internal; + +import org.eclipse.osgi.util.NLS; + +// CHECKSTYLE NLS: OFF +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.koneki.ldt.remote.debug.core.internal.messages"; //$NON-NLS-1$ + public static String LuaRemoteLaunchConfigurationDelegate_error_connectionfailed; + public static String LuaRemoteLaunchConfigurationDelegate_error_filealreadyexist; + public static String LuaRemoteLaunchConfigurationDelegate_error_noremotefilesystem; + public static String LuaRemoteLaunchConfigurationDelegate_error_unabletoaccestoremoteapplicationdir; + public static String LuaRemoteLaunchConfigurationDelegate_error_unabletoloadintrospectionlib; + public static String LuaRemoteLaunchConfigurationUtil_error_closed_project; + public static String LuaRemoteLaunchConfigurationUtil_error_command_path_empty; + public static String LuaRemoteLaunchConfigurationUtil_error_no_host_selected; + public static String LuaRemoteLaunchConfigurationUtil_error_no_project; + public static String LuaRemoteLaunchConfigurationUtil_error_not_lua_project; + public static String LuaRemoteLaunchConfigurationUtil_error_remote_application_path_empty; + public static String LuaRemoteLaunchConfigurationUtil_error_unexisted_project; + public static String SshProcess_exception_kill_process_failed; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} +// CHECKSTYLE NLS: ON diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/OutputStreamMonitor.java b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/OutputStreamMonitor.java new file mode 100644 index 0000000..b90a0e7 --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/OutputStreamMonitor.java @@ -0,0 +1,274 @@ +/******************************************************************************* + * Copyright (c) 2012 Sierra Wireless 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: + * Sierra Wireless - initial API and implementation + *******************************************************************************/ +//FIXME This class is a copy from eclipse.debug.core plugin +package org.eclipse.koneki.ldt.remote.debug.core.internal; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IStreamListener; +import org.eclipse.debug.core.model.IFlushableStreamMonitor; + +/** + * Monitors the output stream of a system process and notifies listeners of additions to the stream. + * + * The output stream monitor reads system out (or err) via and input stream. + */ +public class OutputStreamMonitor implements IFlushableStreamMonitor { + + /** + * The size of the read buffer + */ + private static final int BUFFER_SIZE = 8192; + + /** + * The stream being monitored (connected system out or err). + */ + private InputStream fStream; + + /** + * A collection of listeners + */ + private ListenerList fListeners = new ListenerList(); + + /** + * Whether content is being buffered + */ + private boolean fBuffered = true; + + /** + * The local copy of the stream contents + */ + private StringBuffer fContents; + + /** + * The thread which reads from the stream + */ + private Thread fThread; + + /** + * Whether or not this monitor has been killed. When the monitor is killed, it stops reading from the stream immediately. + */ + private boolean fKilled = false; + + private long lastSleep; + + private String fEncoding; + + /** + * Creates an output stream monitor on the given stream (connected to system out or err). + * + * @param stream + * input stream to read from + * @param encoding + * stream encoding or <code>null</code> for system default + */ + public OutputStreamMonitor(InputStream stream, String encoding) { + fStream = new BufferedInputStream(stream, 8192); + fEncoding = encoding; + fContents = new StringBuffer(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.core.model.IStreamMonitor#addListener(org.eclipse.debug.core.IStreamListener) + */ + public synchronized void addListener(IStreamListener listener) { + fListeners.add(listener); + } + + /** + * Causes the monitor to close all communications between it and the underlying stream by waiting for the thread to terminate. + */ + protected void close() { + if (fThread != null) { + Thread thread = fThread; + fThread = null; + try { + thread.join(); + } catch (InterruptedException ie) { + Activator.logWarning("Output Stream Monitor thread interrupted on close."); //$NON-NLS-1$ + } + fListeners = new ListenerList(); + } + } + + /** + * Notifies the listeners that text has been appended to the stream. + */ + private void fireStreamAppended(String text) { + getNotifier().notifyAppend(text); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.core.model.IStreamMonitor#getContents() + */ + public synchronized String getContents() { + return fContents.toString(); + } + + /** + * Continually reads from the stream. + * <p> + * This method, along with the <code>startReading</code> method is used to allow <code>OutputStreamMonitor</code> to implement + * <code>Runnable</code> without publicly exposing a <code>run</code> method. + */ + private void read() { + lastSleep = System.currentTimeMillis(); + long currentTime = lastSleep; + byte[] bytes = new byte[BUFFER_SIZE]; + int read = 0; + while (read >= 0) { + try { + if (fKilled) { + break; + } + read = fStream.read(bytes); + if (read > 0) { + String text; + if (fEncoding != null) { + text = new String(bytes, 0, read, fEncoding); + } else { + text = new String(bytes, 0, read); + } + synchronized (this) { + if (isBuffered()) { + fContents.append(text); + } + fireStreamAppended(text); + } + } + } catch (IOException ioe) { + if (!fKilled) { + DebugPlugin.log(ioe); + } + return; + } catch (NullPointerException e) { + // killing the stream monitor while reading can cause an NPE + // when reading from the stream + if (!fKilled && fThread != null) { + DebugPlugin.log(e); + } + return; + } + + currentTime = System.currentTimeMillis(); + if (currentTime - lastSleep > 1000) { + lastSleep = currentTime; + try { + Thread.sleep(1); // just give up CPU to maintain UI responsiveness. + } catch (InterruptedException e) { + Activator.logWarning("Output Stream Monitor thread interrupted on read"); //$NON-NLS-1$ + } + } + } + try { + fStream.close(); + } catch (IOException e) { + DebugPlugin.log(e); + } + } + + protected void kill() { + fKilled = true; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.core.model.IStreamMonitor#removeListener(org.eclipse.debug.core.IStreamListener) + */ + public synchronized void removeListener(IStreamListener listener) { + fListeners.remove(listener); + } + + /** + * Starts a thread which reads from the stream + */ + protected void startMonitoring() { + if (fThread == null) { + fThread = new Thread(new Runnable() { + public void run() { + read(); + } + }, "Output Stream Monitor"); //$NON-NLS-1$ + fThread.setDaemon(true); + fThread.setPriority(Thread.MIN_PRIORITY); + fThread.start(); + } + } + + /** + * @see org.eclipse.debug.core.model.IFlushableStreamMonitor#setBuffered(boolean) + */ + public synchronized void setBuffered(boolean buffer) { + fBuffered = buffer; + } + + /** + * @see org.eclipse.debug.core.model.IFlushableStreamMonitor#flushContents() + */ + public synchronized void flushContents() { + fContents.setLength(0); + } + + /** + * @see IFlushableStreamMonitor#isBuffered() + */ + public synchronized boolean isBuffered() { + return fBuffered; + } + + private ContentNotifier getNotifier() { + return new ContentNotifier(); + } + + class ContentNotifier implements ISafeRunnable { + + private IStreamListener fListener; + private String fText; + + /** + * @see org.eclipse.core.runtime.ISafeRunnable#handleException(java.lang.Throwable) + */ + public void handleException(Throwable exception) { + DebugPlugin.log(exception); + } + + /** + * @see org.eclipse.core.runtime.ISafeRunnable#run() + */ + public void run() throws Exception { + fListener.streamAppended(fText, OutputStreamMonitor.this); + } + + public void notifyAppend(String text) { + if (text == null) + return; + fText = text; + Object[] copiedListeners = fListeners.getListeners(); + for (int i = 0; i < copiedListeners.length; i++) { + fListener = (IStreamListener) copiedListeners[i]; + SafeRunner.run(this); + } + fListener = null; + fText = null; + } + } +} diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/RSEUtil.java b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/RSEUtil.java new file mode 100644 index 0000000..65f0e03 --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/RSEUtil.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2012 Sierra Wireless 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: + * Sierra Wireless - initial API and implementation + *******************************************************************************/ +package org.eclipse.koneki.ldt.remote.debug.core.internal; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IResource; +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.SubMonitor; +import org.eclipse.rse.core.RSECorePlugin; +import org.eclipse.rse.core.model.IHost; +import org.eclipse.rse.core.subsystems.IConnectorService; +import org.eclipse.rse.core.subsystems.ISubSystem; +import org.eclipse.rse.internal.connectorservice.ssh.SshConnectorService; +import org.eclipse.rse.services.clientserver.messages.SystemMessageException; +import org.eclipse.rse.services.files.IFileService; +import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFile; +import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFileSubSystem; + +import com.jcraft.jsch.Session; + +/** + * Utility class, contains some helpers to use RSE + */ +@SuppressWarnings("restriction") +public final class RSEUtil { + private RSEUtil() { + } + + /** + * Return the ssh connection session in this connector services + */ + public static Session getCurrentSshSession(IConnectorService[] connectors) { + // TODO find a way to not use internal class (and so remove the rse.ui dependency) + for (IConnectorService connectorService : connectors) { + if (connectorService instanceof SshConnectorService) { + SshConnectorService iConnectorService = (SshConnectorService) connectorService; + return iConnectorService.getSession(); + } + } + return null; + } + + /** + * stop the current thread until RSE is initialized or the thread is interrupted + */ + public static void waitForRSEInitialization() { + try { + RSECorePlugin.waitForInitCompletion(); + // CHECKSTYLE:OFF + } catch (InterruptedException e) { + // nothing to do .. + // CHECKSTYLE:ON + } + } + + /** + * Gets the first found remote file SubSystem in the given host + * + * @param host + * @return a remote file subsystem or null if none is found + */ + public static IRemoteFileSubSystem getRemoteFileSubsystem(IHost host) { + ISubSystem[] subSystems = host.getSubSystems(); + for (ISubSystem subsystem : subSystems) { + if (subsystem instanceof IRemoteFileSubSystem) { + return ((IRemoteFileSubSystem) subsystem); + } + } + return null; + } + + /** + * workaround of BUG ECLIPSE TOOLSLINUX-86 349947, no more needed since tm 3.4 + * + * @return true if the file exist + */ + @Deprecated + public static boolean fileExist(IRemoteFileSubSystem subsystem, IRemoteFile file) { + try { + IRemoteFile[] list = subsystem.list(file.getParentRemoteFile(), file.getName(), IFileService.FILE_TYPE_FILES_AND_FOLDERS, + new NullProgressMonitor()); + return list.length == 1; + } catch (SystemMessageException e) { + return false; + } + } + + /** + * upload the content of the given folder at the given destination + * + * @throws CoreException + */ + public static void uploadFiles(IRemoteFileSubSystem subsystem, IFolder sourceFolder, String destinationFolderPath, IProgressMonitor monitor) + throws CoreException { + IResource[] members = sourceFolder.members(); + SubMonitor subMonitor = SubMonitor.convert(monitor, members.length); + if (members.length > 0) { + for (int i = 0; i < members.length && !subMonitor.isCanceled(); i++) { + try { + IResource sourceMember = members[i]; + if (sourceMember instanceof IFile) { + IFile sourceFile = ((IFile) sourceMember); + String sourcePath = sourceFile.getLocation().toOSString(); + String sourceEncoding = sourceFile.getCharset(); + String destinationPath = destinationFolderPath + subsystem.getSeparator() + sourceFile.getName(); + String destinationEncoding = subsystem.getRemoteEncoding(); + subsystem.upload(sourcePath, sourceEncoding, destinationPath, destinationEncoding, subMonitor.newChild(3)); + } else if (sourceMember instanceof IFolder) { + IFolder innerSourceFolder = (IFolder) sourceMember; + String innerDestinationFolderPath = destinationFolderPath + subsystem.getSeparator() + innerSourceFolder.getName(); + IRemoteFile remoteFolder = subsystem.getRemoteFileObject(innerDestinationFolderPath, subMonitor.newChild(1)); + subsystem.createFolder(remoteFolder, subMonitor.newChild(1)); + uploadFiles(subsystem, innerSourceFolder, innerDestinationFolderPath, subMonitor.newChild(1)); + } + } catch (SystemMessageException e) { + throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Unable to upload files", e)); //$NON-NLS-1$ + } + } + + } + } +} diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/SshProcess.java b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/SshProcess.java new file mode 100644 index 0000000..24cdb16 --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/SshProcess.java @@ -0,0 +1,359 @@ +/*******************************************************************************
+ * Copyright (c) 2012 Sierra Wireless 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:
+ * Sierra Wireless - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.koneki.ldt.remote.debug.core.internal;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.core.model.IStreamsProxy;
+
+import com.jcraft.jsch.Channel;
+import com.jcraft.jsch.ChannelExec;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+/**
+ * Implementation of {@link org.eclipse.debug.core.model.IProcess} based on ssh connection
+ */
+public class SshProcess implements IProcess {
+
+ private ILaunch launch;
+ private ChannelExec channelExec;
+ private StreamsProxy sshStreamProxy;
+ private String label;
+ private Session currentSession;
+ private String workingDir;
+
+ /**
+ * Create and launch a process corresponding to the given command throw an ssh connection<br>
+ * <b>/!\ Session must be connected !</b>
+ *
+ * A .PID file will be create in the working directory for this process.(which contains the PID of this process)<br>
+ * This file is used to be able to stop (kill) the process when needed.<br>
+ * So, User must have the write to create file in the working directory and so 2 process must not be run at the same time in the same working
+ * directory
+ *
+ * @throws CoreException
+ * if exec channel can not be created, or session is down
+ */
+ public SshProcess(Session session, ILaunch launch, String workingDirectoryPath, String[] command, Map<String, String> envVars)
+ throws CoreException {
+ this.launch = launch;
+ this.currentSession = session;
+ this.workingDir = workingDirectoryPath;
+
+ // open exec channel
+ Channel channel;
+ try {
+ channel = session.openChannel("exec"); //$NON-NLS-1$
+ } catch (JSchException e) {
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Unable to create SShProcess", e)); //$NON-NLS-1$
+ }
+ if (!(channel instanceof ChannelExec))
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Unable to create SShProcess")); //$NON-NLS-1$
+ channelExec = (ChannelExec) channel;
+
+ // Create command label (unescaped form)
+ StringBuilder labelCmd = new StringBuilder();
+ for (String part : command) {
+ labelCmd.append(part).append(" "); //$NON-NLS-1$
+ }
+ this.label = labelCmd.toString();
+
+ // create composed command
+ String composedCommand = createLaunchCommand(workingDirectoryPath, command, envVars);
+ channelExec.setCommand(composedCommand);
+ }
+
+ /**
+ * Escapes a string so it became a valid Shell token.
+ *
+ * @param s
+ * @return
+ */
+ public static String escapeShell(String s) {
+ // TODO: there is still some sequences to escape
+ return "\"" + s.replaceAll("\"", Matcher.quoteReplacement("\\\"")) + "\""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ }
+
+ /**
+ * start the process <br>
+ * don't forget to call Terminate if an error is raised.
+ *
+ * @throws CoreException
+ * if an error occured (session is down, channel is not open,IO Problem on channel's stream)
+ */
+ public void start() throws CoreException {
+ // start stream monitoring
+ try {
+ sshStreamProxy = new StreamsProxy(channelExec.getInputStream(), channelExec.getErrStream(), channelExec.getOutputStream(), null, null);
+ } catch (IOException e) {
+ this.terminate();
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Unable to start SShProcess", e)); //$NON-NLS-1$
+ }
+
+ // start connection
+ try {
+ channelExec.connect();
+ } catch (JSchException e) {
+ this.terminate();
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Unable to start SShProcess", e)); //$NON-NLS-1$
+ }
+ fireCreationEvent();
+
+ // monitor channel state
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ boolean connected;
+ do {
+ connected = channelExec.isConnected();
+ try {
+ Thread.sleep(100);
+ // CHECKSTYLE:OFF
+ } catch (InterruptedException e) {
+ // nothing to do
+ // CHECKSTYLE:ON
+ }
+ } while (connected);
+ sshStreamProxy.kill();
+ fireTerminateEvent();
+ }
+ }).start();
+ }
+
+ /**
+ * create one command from workingdir, envpath and command
+ */
+ private String createLaunchCommand(String workingDirectoryPath, String[] command, Map<String, String> envVars) {
+ // TODO : should works only on linux...
+
+ StringBuilder composedCommand = new StringBuilder();
+ // add : move to the working directory
+ composedCommand.append("cd "); //$NON-NLS-1$
+ composedCommand.append(escapeShell(workingDirectoryPath));
+ composedCommand.append(" && "); //$NON-NLS-1$
+
+ // add : set env path
+ for (Entry<String, String> entrySet : envVars.entrySet()) {
+ composedCommand.append("export "); //$NON-NLS-1$
+ composedCommand.append(entrySet.getKey());
+ composedCommand.append("="); //$NON-NLS-1$
+ composedCommand.append(escapeShell(entrySet.getValue()));
+ composedCommand.append(" && "); //$NON-NLS-1$
+ }
+
+ // if a .PID file already exist, we try to kill the corresponding process
+ // and remove the old .PID file
+ // run this command in silent mode (redirect all the output stream in /dev/null)
+ composedCommand.append("("); //$NON-NLS-1$
+ composedCommand.append("cat .PID > /dev/null 2>&1"); //$NON-NLS-1$
+ composedCommand.append(" && "); //$NON-NLS-1$
+ composedCommand.append("kill `cat .PID` > /dev/null 2>&1"); //$NON-NLS-1$
+ composedCommand.append(" && "); //$NON-NLS-1$
+ composedCommand.append("rm .PID > /dev/null 2>&1"); //$NON-NLS-1$
+ composedCommand.append(")"); //$NON-NLS-1$
+ composedCommand.append("; "); //$NON-NLS-1$
+
+ // launch command in background
+ composedCommand.append("{ "); //$NON-NLS-1$
+ for (String part : command) {
+ composedCommand.append(escapeShell(part)).append(" "); //$NON-NLS-1$
+ }
+ composedCommand.append(" & }"); //$NON-NLS-1$
+ composedCommand.append(" && "); //$NON-NLS-1$
+
+ // store the PID of the last background task (so the command below)
+ composedCommand.append("echo $! > .PID"); //$NON-NLS-1$
+ composedCommand.append(" && "); //$NON-NLS-1$
+
+ // wait the end of the command execution
+ composedCommand.append("wait $!"); //$NON-NLS-1$
+ composedCommand.append(" && "); //$NON-NLS-1$
+
+ // remove the PID file
+ composedCommand.append("rm .PID"); //$NON-NLS-1$
+
+ return composedCommand.toString();
+ }
+
+ /**
+ * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
+ */
+ @Override
+ public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
+ return null;
+ }
+
+ /**
+ * @see org.eclipse.debug.core.model.ITerminate#canTerminate()
+ */
+ @Override
+ public boolean canTerminate() {
+ return !isTerminated();
+ }
+
+ /**
+ * @see org.eclipse.debug.core.model.ITerminate#isTerminated()
+ */
+ @Override
+ public boolean isTerminated() {
+ return channelExec.isClosed();
+ }
+
+ /**
+ * @see org.eclipse.debug.core.model.ITerminate#terminate()
+ */
+ @Override
+ public void terminate() throws DebugException {
+ if (!isTerminated()) {
+ SshProcess.killProcess(currentSession, workingDir);
+ } else {
+ sshStreamProxy.kill();
+ fireTerminateEvent();
+ }
+ }
+
+ /**
+ * create the kill command for the current process
+ */
+ private static String createKillCommand(String pidContainerFolder) {
+ // TODO : should works only on linux...
+
+ StringBuilder composedCommand = new StringBuilder();
+ // add : move to the working directory
+ composedCommand.append("cd "); //$NON-NLS-1$
+ composedCommand.append(escapeShell(pidContainerFolder));
+ composedCommand.append(" && "); //$NON-NLS-1$
+
+ // kill process
+ composedCommand.append("kill `cat .PID`"); //$NON-NLS-1$
+ composedCommand.append(" && "); //$NON-NLS-1$
+ composedCommand.append(" rm .PID"); //$NON-NLS-1$
+ return composedCommand.toString();
+ }
+
+ /**
+ * @see org.eclipse.debug.core.model.IProcess#getLabel()
+ */
+ @Override
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * @see org.eclipse.debug.core.model.IProcess#getLaunch()
+ */
+ @Override
+ public ILaunch getLaunch() {
+ return launch;
+ }
+
+ /**
+ * @see org.eclipse.debug.core.model.IProcess#getStreamsProxy()
+ */
+ @Override
+ public IStreamsProxy getStreamsProxy() {
+ return sshStreamProxy;
+ }
+
+ /**
+ * @see org.eclipse.debug.core.model.IProcess#setAttribute(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void setAttribute(String key, String value) {
+ }
+
+ /**
+ * @see org.eclipse.debug.core.model.IProcess#getAttribute(java.lang.String)
+ */
+ @Override
+ public String getAttribute(String key) {
+ return null;
+ }
+
+ /**
+ * @see org.eclipse.debug.core.model.IProcess#getExitValue()
+ */
+ @Override
+ public int getExitValue() throws DebugException {
+ return channelExec.getExitStatus();
+ }
+
+ /**
+ * Fires a creation event.
+ */
+ protected void fireCreationEvent() {
+ fireEvent(new DebugEvent(this, DebugEvent.CREATE));
+ }
+
+ /**
+ * Fires the given debug event.
+ *
+ * @param event
+ * debug event to fire
+ */
+ protected void fireEvent(DebugEvent event) {
+ DebugPlugin manager = DebugPlugin.getDefault();
+ if (manager != null) {
+ manager.fireDebugEventSet(new DebugEvent[] { event });
+ }
+ }
+
+ /**
+ * Fires a terminate event.
+ */
+ protected void fireTerminateEvent() {
+ fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
+ }
+
+ /**
+ * Fires a change event.
+ */
+ protected void fireChangeEvent() {
+ fireEvent(new DebugEvent(this, DebugEvent.CHANGE));
+ }
+
+ /**
+ * try to kill the process with the PID equal to the value of the .PID file contained in pidContainerFolder
+ */
+ public static void killProcess(Session session, String pidContainerFolder) throws DebugException {
+ try {
+ // create a new channel
+ Channel channel = session.openChannel("exec"); //$NON-NLS-1$
+ if (!(channel instanceof ChannelExec))
+ throw new JSchException("Unable to create exec channel"); //$NON-NLS-1$
+ ChannelExec killChannel = (ChannelExec) channel;
+
+ // create kill command
+ String killCommand = createKillCommand(pidContainerFolder);
+ killChannel.setCommand(killCommand);
+
+ // execute command
+ killChannel.connect();
+ // CHECKSTYLE:OFF
+ } catch (Exception e) {
+ // CHECKSTYLE:ON
+ throw new DebugException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.SshProcess_exception_kill_process_failed, e));
+ }
+ }
+}
diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/StreamsProxy.java b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/StreamsProxy.java new file mode 100644 index 0000000..eb528a3 --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/StreamsProxy.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (c) 2012 Sierra Wireless 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: + * Sierra Wireless - initial API and implementation + *******************************************************************************/ +package org.eclipse.koneki.ldt.remote.debug.core.internal; + +//FIXME This class is partial copy from eclipse.debug.core plugin + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.eclipse.debug.core.model.IStreamMonitor; +import org.eclipse.debug.core.model.IStreamsProxy; +import org.eclipse.debug.core.model.IStreamsProxy2; + +/** + * Standard implementation of a streams proxy for IStreamsProxy. + */ + +public class StreamsProxy implements IStreamsProxy, IStreamsProxy2 { + /** + * The monitor for the output stream (connected to standard out of the process) + */ + private OutputStreamMonitor fOutputMonitor; + /** + * The monitor for the error stream (connected to standard error of the process) + */ + private OutputStreamMonitor fErrorMonitor; + /** + * The monitor for the input stream (connected to standard in of the process) + */ + private InputStreamMonitor fInputMonitor; + /** + * Records the open/closed state of communications with the underlying streams. Note: fClosed is initialized to <code>false</code> by default. + */ + private boolean fClosed; + + /** + * Creates a <code>StreamsProxy</code> on the streams of the given system process. + * + * @param process + * system process to create a streams proxy on + * @param encoding + * the process's encoding or <code>null</code> if default + */ + public StreamsProxy(InputStream inputStream, InputStream errorStream, OutputStream outputStream, String inputEncoding, String outputEncoding) { + fOutputMonitor = new OutputStreamMonitor(inputStream, inputEncoding); + fErrorMonitor = new OutputStreamMonitor(errorStream, inputEncoding); + fInputMonitor = new InputStreamMonitor(outputStream, outputEncoding); + fOutputMonitor.startMonitoring(); + fErrorMonitor.startMonitoring(); + fInputMonitor.startMonitoring(); + } + + /** + * Causes the proxy to close all communications between it and the underlying streams after all remaining data in the streams is read. + */ + public void close() { + if (!isClosed(true)) { + fOutputMonitor.close(); + fErrorMonitor.close(); + fInputMonitor.close(); + } + } + + /** + * Returns whether the proxy is currently closed. This method synchronizes access to the <code>fClosed</code> flag. + * + * @param setClosed + * If <code>true</code> this method will also set the <code>fClosed</code> flag to true. Otherwise, the <code>fClosed</code> flag is + * not modified. + * @return Returns whether the stream proxy was already closed. + */ + private boolean isClosed(boolean setClosed) { + boolean closed; + synchronized (this) { + closed = fClosed; + if (setClosed) { + fClosed = true; + } + } + return closed; + } + + /** + * Causes the proxy to close all communications between it and the underlying streams immediately. Data remaining in the streams is lost. + */ + public void kill() { + synchronized (this) { + fClosed = true; + } + fOutputMonitor.kill(); + fErrorMonitor.kill(); + fInputMonitor.close(); + } + + /** + * @see IStreamsProxy#getErrorStreamMonitor() + */ + public IStreamMonitor getErrorStreamMonitor() { + return fErrorMonitor; + } + + /** + * @see IStreamsProxy#getOutputStreamMonitor() + */ + public IStreamMonitor getOutputStreamMonitor() { + return fOutputMonitor; + } + + /** + * @see IStreamsProxy#write(String) + */ + public void write(String input) throws IOException { + if (!isClosed(false)) { + fInputMonitor.write(input); + } else { + throw new IOException(); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.core.model.IStreamsProxy2#closeInputStream() + */ + public void closeInputStream() throws IOException { + if (!isClosed(false)) { + fInputMonitor.closeInputStream(); + } else { + throw new IOException(); + } + + } + +} diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/messages.properties b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/messages.properties new file mode 100644 index 0000000..68d19a7 --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.core/src/org/eclipse/koneki/ldt/remote/debug/core/internal/messages.properties @@ -0,0 +1,13 @@ +LuaRemoteLaunchConfigurationDelegate_error_connectionfailed=Connection to host failed. Unable to copy file on the host +LuaRemoteLaunchConfigurationDelegate_error_filealreadyexist=A file already exist with the path :{0} +LuaRemoteLaunchConfigurationDelegate_error_noremotefilesystem=The host for this launch configuration has no remote file system. Unable to copy file on the host. +LuaRemoteLaunchConfigurationDelegate_error_unabletoaccestoremoteapplicationdir=Error when trying to access to remote application path :{0} +LuaRemoteLaunchConfigurationDelegate_error_unabletoloadintrospectionlib=Error while loading introspection library +LuaRemoteLaunchConfigurationUtil_error_closed_project=This project is not open. +LuaRemoteLaunchConfigurationUtil_error_command_path_empty=Lua Command path must not be empty. +LuaRemoteLaunchConfigurationUtil_error_no_host_selected=An host must be selected. +LuaRemoteLaunchConfigurationUtil_error_no_project=A project must be selected. +LuaRemoteLaunchConfigurationUtil_error_not_lua_project=This project is not a lua project. +LuaRemoteLaunchConfigurationUtil_error_remote_application_path_empty=Remote Application path must not be empty. +LuaRemoteLaunchConfigurationUtil_error_unexisted_project=This project doesn't exist. +SshProcess_exception_kill_process_failed=An exception occurred when trying to stop the application. diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.ui/META-INF/MANIFEST.MF b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/META-INF/MANIFEST.MF index a1539e7..a2796d3 100644 --- a/plugins/org.eclipse.koneki.ldt.remote.debug.ui/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/META-INF/MANIFEST.MF @@ -1,11 +1,19 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name -Bundle-SymbolicName: org.eclipse.koneki.ldt.remote.debug.ui +Bundle-SymbolicName: org.eclipse.koneki.ldt.remote.debug.ui;singleton:=true Bundle-Version: 0.9.0.qualifier Bundle-Activator: org.eclipse.koneki.ldt.remote.debug.ui.internal.Activator -Require-Bundle: org.eclipse.core.runtime +Require-Bundle: org.eclipse.core.runtime, + org.eclipse.debug.ui, + org.eclipse.koneki.ldt.ui;bundle-version="0.9.0", + org.eclipse.ui;bundle-version="3.103.0", + org.eclipse.koneki.ldt.remote.debug.core;bundle-version="0.9.0", + org.eclipse.dltk.launching;bundle-version="4.0.0", + org.eclipse.rse.ui;bundle-version="3.3.0", + org.eclipse.rse.core;bundle-version="3.3.0" Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-Vendor: %Bundle-Vendor -Export-Package: org.eclipse.koneki.ldt.remote.debug.ui.internal;x-internal:=true +Export-Package: org.eclipse.koneki.ldt.remote.debug.ui.internal;x-internal:=true, + org.eclipse.koneki.ldt.remote.debug.ui.internal.launch.tab;x-internal:=true diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.ui/build.properties b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/build.properties index 2096750..e2c8cb9 100644 --- a/plugins/org.eclipse.koneki.ldt.remote.debug.ui/build.properties +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/build.properties @@ -13,7 +13,8 @@ output.. = bin/ bin.includes = META-INF/,\
.,\
OSGI-INF/,\
- about.html
+ about.html,\
+ plugin.xml
# The java version of the source code
javacSource=1.6
# The java version of the byte code targeted
diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.ui/icons/obj16/lua_launcher.gif b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/icons/obj16/lua_launcher.gif Binary files differnew file mode 100644 index 0000000..0fe4de9 --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/icons/obj16/lua_launcher.gif diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.ui/icons/obj16/main_tab.gif b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/icons/obj16/main_tab.gif Binary files differnew file mode 100644 index 0000000..0193dbe --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/icons/obj16/main_tab.gif diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.ui/plugin.xml b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/plugin.xml new file mode 100644 index 0000000..8f715b1 --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/plugin.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.4"?> +<plugin> + <extension + point="org.eclipse.debug.ui.launchConfigurationTabGroups"> + <launchConfigurationTabGroup + class="org.eclipse.koneki.ldt.remote.debug.ui.internal.launch.tab.LuaRemoteLaunchConfigurationTabGroup" + id="org.eclipse.koneki.ldt.remote.debug.ui.luaRemoteApplicationLaunchTabGroup" + type="org.eclipse.koneki.ldt.remote.debug.core.luaremotedebug"> + <launchMode + mode="debug" + perspective="org.eclipse.debug.ui.DebugPerspective"> + </launchMode> + <launchMode + mode="run" + perspective="org.eclipse.debug.ui.DebugPerspective"> + </launchMode> + </launchConfigurationTabGroup> + </extension> + <extension + point="org.eclipse.debug.ui.launchConfigurationTypeImages"> + <launchConfigurationTypeImage + configTypeID="org.eclipse.koneki.ldt.remote.debug.core.luaremotedebug" + icon="icons/obj16/lua_launcher.gif" + id="org.eclipse.koneki.ldt.remote.debug.core.luaremotedebug.image"> + </launchConfigurationTypeImage> + </extension> +</plugin> diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/Activator.java b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/Activator.java index 824fd8c..e78b645 100644 --- a/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/Activator.java +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/Activator.java @@ -11,14 +11,15 @@ package org.eclipse.koneki.ldt.remote.debug.ui.internal;
import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
/**
* The activator class controls the plug-in life cycle.
*/
-public class Activator extends Plugin {
+public class Activator extends AbstractUIPlugin {
/** The plug-in ID */
public static final String PLUGIN_ID = "org.eclipse.koneki.ldt.remote.debug.ui"; //$NON-NLS-1$
@@ -62,6 +63,15 @@ public class Activator extends Plugin { }
/**
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#initializeImageRegistry(org.eclipse.jface.resource.ImageRegistry)
+ */
+ @Override
+ protected void initializeImageRegistry(ImageRegistry reg) {
+ reg.put(ImageConstants.LUA_REMOTE_APP_LAUNCH_MAIN_TAB_ICON,
+ AbstractUIPlugin.imageDescriptorFromPlugin(PLUGIN_ID, ImageConstants.LUA_REMOTE_APP_LAUNCH_MAIN_TAB_ICON));
+ }
+
+ /**
* Log a error message caused by the given exception
*
* @param message
diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/ImageConstants.java b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/ImageConstants.java new file mode 100644 index 0000000..6bcf9bd --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/ImageConstants.java @@ -0,0 +1,15 @@ +/******************************************************************************* + * Copyright (c) 2012 Sierra Wireless 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: + * Sierra Wireless - initial API and implementation + *******************************************************************************/ +package org.eclipse.koneki.ldt.remote.debug.ui.internal; + +public interface ImageConstants { + String LUA_REMOTE_APP_LAUNCH_MAIN_TAB_ICON = "icons/obj16/main_tab.gif"; //$NON-NLS-1$ +} diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/launch/tab/LuaRemoteLaunchConfigurationMainTab.java b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/launch/tab/LuaRemoteLaunchConfigurationMainTab.java new file mode 100644 index 0000000..d8b2488 --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/launch/tab/LuaRemoteLaunchConfigurationMainTab.java @@ -0,0 +1,292 @@ +/******************************************************************************* + * Copyright (c) 2012 Sierra Wireless 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: + * Sierra Wireless - initial API and implementation + *******************************************************************************/ +package org.eclipse.koneki.ldt.remote.debug.ui.internal.launch.tab; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; +import org.eclipse.dltk.launching.ScriptLaunchConfigurationConstants; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.koneki.ldt.remote.debug.core.internal.LuaRemoteDebugConstant; +import org.eclipse.koneki.ldt.remote.debug.core.internal.LuaRemoteLaunchConfigurationUtil; +import org.eclipse.koneki.ldt.remote.debug.ui.internal.Activator; +import org.eclipse.koneki.ldt.remote.debug.ui.internal.ImageConstants; +import org.eclipse.koneki.ldt.ui.LuaDialogUtil; +import org.eclipse.koneki.ldt.ui.SWTUtil; +import org.eclipse.rse.core.RSECorePlugin; +import org.eclipse.rse.core.model.IHost; +import org.eclipse.rse.ui.widgets.SystemHostCombo; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +public class LuaRemoteLaunchConfigurationMainTab extends AbstractLaunchConfigurationTab { + + /** + * Commons listener for Textfield (used to update dialog buttons) + */ + private final class TextModifyListener implements ModifyListener { + @Override + public void modifyText(ModifyEvent e) { + updateLaunchConfigurationDialog(); + } + } + + // private Text luacpathText; + // private Text luapathText; + private SystemHostCombo hostCombo; + private TextModifyListener textModifyListener = new TextModifyListener(); + private Text projectNameText; + private Button projectSelectionButton; + + /** + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#createControl(org.eclipse.swt.widgets.Composite) + */ + @Override + public void createControl(Composite parent) { + Composite comp = new Composite(parent, SWT.NONE); + setControl(comp); + GridLayoutFactory.swtDefaults().applyTo(comp); + comp.setFont(parent.getFont()); + + // TODO not sure this is the good way to wait for init everywhere in the code + try { + RSECorePlugin.waitForInitCompletion(); + // CHECKSTYLE:OFF + } catch (InterruptedException e) { + // nothing to do .. + // CHECKSTYLE:ON + } + + createProjectConfigComponent(comp); + createTargetConfigComponent(comp); + } + + /** + * create the composite used to configure the target information + * + * @param comp + */ + private void createProjectConfigComponent(Composite comp) { + // create group + Group group = new Group(comp, SWT.None); + group.setText(Messages.LuaRemoteMainTab_projectgroup_title); + GridDataFactory.fillDefaults().grab(true, false).applyTo(group); + GridLayoutFactory.swtDefaults().numColumns(2).applyTo(group); + + // create project choice : + projectNameText = new Text(group, SWT.SINGLE | SWT.BORDER); + GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).applyTo(projectNameText); + projectSelectionButton = new Button(group, SWT.PUSH); + projectSelectionButton.setText(Messages.LuaRemoteMainTab_projectgroup_browseprojectbutton); + final int browseButtonHorizontalHint = SWTUtil.getButtonWidthHint(projectSelectionButton); + GridDataFactory.fillDefaults().align(SWT.END, SWT.BEGINNING).hint(browseButtonHorizontalHint, SWT.DEFAULT).applyTo(projectSelectionButton); + } + + /** + * create the composite used to configure the target information + */ + private void createTargetConfigComponent(Composite comp) { + // create group + Group group = new Group(comp, SWT.None); + group.setText(Messages.LuaRemoteMainTab_targetgroup_title); + GridDataFactory.fillDefaults().grab(true, false).applyTo(group); + GridLayoutFactory.swtDefaults().numColumns(2).applyTo(group); + + // create target combo + Label hostLabel = new Label(group, SWT.None); + hostLabel.setText(Messages.LuaRemoteMainTab_targetgroup_hostlabel); + IHost selectfirstHost = null; + hostCombo = new SystemHostCombo(group, SWT.None, selectfirstHost, true, "files", false); //$NON-NLS-1$ + final int newSystemHint = SWTUtil.getButtonWidthHint(hostCombo.getNewButton()); + hostCombo.setButtonWidthHint(newSystemHint); + + GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).applyTo(hostCombo); + + // create interpreter command : + // Label luacommandLabel = new Label(group, SWT.None); + // luacommandLabel.setText(Messages.LuaEmbeddedMainTab_targetgroup_luacommandlabel); + // luaCommandPathText = new Text(group, SWT.SINGLE | SWT.BORDER); + // GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).applyTo(luaCommandPathText); + + // // create luapath + // Label luapathLabel = new Label(group, SWT.None); + // luapathLabel.setText(Messages.LuaEmbeddedMainTab_targetgroup_luapathlabel); + // luapathText = new Text(group, SWT.SINGLE | SWT.BORDER); + // GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).applyTo(luapathText); + // + // // create luacpath + // Label luacpathLabel = new Label(group, SWT.None); + // luacpathLabel.setText(Messages.LuaEmbeddedMainTab_targetgroup_luacpathlabel); + // luacpathText = new Text(group, SWT.SINGLE | SWT.BORDER); + // GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).applyTo(luacpathText); + + // create remote path + // Label ramoteApplicationPathLabel = new Label(group, SWT.None); + // ramoteApplicationPathLabel.setText(Messages.LuaEmbeddedMainTab_targetgroup_remoteapppathlabel); + // remoteApplicationPathText = new Text(group, SWT.SINGLE | SWT.BORDER); + // GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).applyTo(remoteApplicationPathText); + } + + /** + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#setDefaults(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + @Override + public void setDefaults(ILaunchConfigurationWorkingCopy configuration) { + // TODO : get project of selected resource + configuration.setAttribute(ScriptLaunchConfigurationConstants.ATTR_PROJECT_NAME, ""); //$NON-NLS-1$ + + // get first available target + // IHost[] hosts = RSECorePlugin.getTheSystemRegistry().getHosts(); + // if (hosts.length > 0) { + // LuaRemoteLaunchConfigurationUtil.setConnectionId(configuration, hosts[0]); + // } + + // TODO integrate UI, see org.eclipse.dltk.debug.ui.launchConfigurations.ScriptLaunchConfigurationTab + configuration.setAttribute(ScriptLaunchConfigurationConstants.ENABLE_DBGP_LOGGING, false); + } + + /** + * add listeners to be aware of tab modification (to update dialog buttons) + */ + private void addListeners() { + if (!projectNameText.isListening(SWT.Modify)) + projectNameText.addModifyListener(textModifyListener); + if (!projectSelectionButton.isListening(SWT.Selection)) + projectSelectionButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + selectLuaEmbeddedProject(); + } + }); + // if (!luapathText.isListening(SWT.Modify)) + // luapathText.addModifyListener(textModifyListener); + // if (!luacpathText.isListening(SWT.Modify)) + // luacpathText.addModifyListener(textModifyListener); + if (!hostCombo.isListening(SWT.Selection)) + hostCombo.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + updateLaunchConfigurationDialog(); + } + }); + } + + /** + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration) + */ + @Override + public void initializeFrom(ILaunchConfiguration configuration) { + try { + String projectName = configuration.getAttribute(ScriptLaunchConfigurationConstants.ATTR_PROJECT_NAME, ""); //$NON-NLS-1$ + if (!projectName.equals(projectNameText.getText())) + projectNameText.setText(projectName); + IHost host = LuaRemoteLaunchConfigurationUtil.getHost(configuration); + if (hostCombo.getHost() != host) + hostCombo.select(host); + addListeners(); + } catch (CoreException e) { + Activator.logError("Launch Configuration main tab for lua embedded failed at initialization", e); //$NON-NLS-1$ + } + } + + /** + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#performApply(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + @Override + public void performApply(ILaunchConfigurationWorkingCopy configuration) { + // Save attributes + configuration.setAttribute(ScriptLaunchConfigurationConstants.ATTR_PROJECT_NAME, projectNameText.getText()); + IHost host = hostCombo.getHost(); + if (host != null) + LuaRemoteLaunchConfigurationUtil.setConnectionId(configuration, hostCombo.getHost()); + else + configuration.removeAttribute(LuaRemoteDebugConstant.HOST_ID); + } + + /** + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getName() + */ + @Override + public String getName() { + return Messages.LuaRemoteMainTab_tabname; + } + + /** + * @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#getImage() + */ + @Override + public Image getImage() { + return Activator.getDefault().getImageRegistry().get(ImageConstants.LUA_REMOTE_APP_LAUNCH_MAIN_TAB_ICON); + } + + /** + * @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#isValid(org.eclipse.debug.core.ILaunchConfiguration) + */ + @Override + public boolean isValid(ILaunchConfiguration configuration) { + // try { + // String projectName = configuration.getAttribute(ScriptLaunchConfigurationConstants.ATTR_PROJECT_NAME, "");//$NON-NLS-1$ + // IHost host = LuaRemoteLaunchConfigurationUtil.getHost(configuration); + // return innerIsValuesValid(projectName, host, luacommandpath, remoteapplicationpath); + // return false; + // } catch (CoreException e) { + // Activator.logError("Launch Configuration main tab for lua embedded failed at validation", e); //$NON-NLS-1$ + // return false; + // } + return true; + } + + /** + * @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#canSave() + */ + @Override + public boolean canSave() { + String projectName = projectNameText.getText(); + IHost host = hostCombo.getHost(); + return innerIsValuesValid(projectName, host); + } + + /** + * internal method to validate the value of the current tab + * + * @param luaInspectorPort + * + * @return true if value is valid + */ + private boolean innerIsValuesValid(String projectName, IHost host) { + String error = LuaRemoteLaunchConfigurationUtil.validateLuaEmbeddedConfiguration(projectName, host); + setErrorMessage(error); + return error == null; + } + + /** + * Open project selection dialog + */ + private void selectLuaEmbeddedProject() { + String currentProjectName = projectNameText.getText(); + IProject selectedProject = LuaDialogUtil.openSelectLuaProjectDialog(getShell(), projectNameText.getText()); + if (selectedProject != null && !selectedProject.getName().equals(currentProjectName)) + projectNameText.setText(selectedProject.getName()); + } + +} diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/launch/tab/LuaRemoteLaunchConfigurationTabGroup.java b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/launch/tab/LuaRemoteLaunchConfigurationTabGroup.java new file mode 100644 index 0000000..d187caa --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/launch/tab/LuaRemoteLaunchConfigurationTabGroup.java @@ -0,0 +1,23 @@ +package org.eclipse.koneki.ldt.remote.debug.ui.internal.launch.tab; + +import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup; +import org.eclipse.debug.ui.CommonTab; +import org.eclipse.debug.ui.EnvironmentTab; +import org.eclipse.debug.ui.ILaunchConfigurationDialog; +import org.eclipse.debug.ui.ILaunchConfigurationTab; + +public class LuaRemoteLaunchConfigurationTabGroup extends AbstractLaunchConfigurationTabGroup { + + public LuaRemoteLaunchConfigurationTabGroup() { + } + + @Override + public void createTabs(ILaunchConfigurationDialog dialog, String mode) { + ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[3]; + tabs[0] = new LuaRemoteLaunchConfigurationMainTab(); + tabs[1] = new EnvironmentTab(); + tabs[2] = new CommonTab(); + setTabs(tabs); + } + +} diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/launch/tab/Messages.java b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/launch/tab/Messages.java new file mode 100644 index 0000000..e64c410 --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/launch/tab/Messages.java @@ -0,0 +1,31 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Sierra Wireless 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:
+ * Sierra Wireless - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.koneki.ldt.remote.debug.ui.internal.launch.tab;
+
+import org.eclipse.osgi.util.NLS;
+
+//CHECKSTYLE NLS: OFF
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.koneki.ldt.remote.debug.ui.internal.launch.tab.messages"; //$NON-NLS-1$
+ public static String LuaRemoteMainTab_projectgroup_browseprojectbutton;
+ public static String LuaRemoteMainTab_projectgroup_title;
+ public static String LuaRemoteMainTab_tabname;
+ public static String LuaRemoteMainTab_targetgroup_hostlabel;
+ public static String LuaRemoteMainTab_targetgroup_title;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
+// CHECKSTYLE NLS: ON
diff --git a/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/launch/tab/messages.properties b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/launch/tab/messages.properties new file mode 100644 index 0000000..bfe51b4 --- a/dev/null +++ b/plugins/org.eclipse.koneki.ldt.remote.debug.ui/src/org/eclipse/koneki/ldt/remote/debug/ui/internal/launch/tab/messages.properties @@ -0,0 +1,5 @@ +LuaRemoteMainTab_projectgroup_browseprojectbutton=Bro&wse...
+LuaRemoteMainTab_projectgroup_title=Project :
+LuaRemoteMainTab_tabname=Main
+LuaRemoteMainTab_targetgroup_hostlabel=Host :
+LuaRemoteMainTab_targetgroup_title=Target Settings :
|

