diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java index 8947b514c97..df43b455dcb 100644 --- a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java @@ -19,8 +19,8 @@ import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.ReflectionSequence; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; -import org.eclipse.cdt.dsf.concurrent.Sequence; import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.DataModelInitializedEvent; import org.eclipse.cdt.dsf.datamodel.IDMContext; @@ -49,580 +49,624 @@ import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IStatusHandler; -public class FinalLaunchSequence extends Sequence { +public class FinalLaunchSequence extends ReflectionSequence { - Step[] fSteps = new Step[] { - new Step() { @Override - public void execute(RequestMonitor requestMonitor) { - fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fLaunch.getSession().getId()); - requestMonitor.done(); - } - @Override - public void rollBack(RequestMonitor requestMonitor) { - if (fTracker != null) fTracker.dispose(); - fTracker = null; - requestMonitor.done(); - }}, + private GdbLaunch fLaunch; + private SessionType fSessionType; + private boolean fAttach; - /* - * Fetch the GDBBackend, CommandControl and Process services for later use - */ - new Step() { @Override - public void execute(RequestMonitor requestMonitor) { - fGDBBackend = fTracker.getService(IGDBBackend.class); - if (fGDBBackend == null) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot obtain GDBBackend service", null)); //$NON-NLS-1$ - requestMonitor.done(); - return; - } + private IGDBControl fCommandControl; + private IGDBBackend fGDBBackend; + private IMIProcesses fProcService; + private CommandFactory fCommandFactory; - fCommandControl = fTracker.getService(IGDBControl.class); - if (fCommandControl == null) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot obtain control service", null)); //$NON-NLS-1$ - requestMonitor.done(); - return; - } + private DsfServicesTracker fTracker; - fCommandFactory = fCommandControl.getCommandFactory(); - - fProcService = fTracker.getService(IMIProcesses.class); - if (fProcService == null) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot obtain process service", null)); //$NON-NLS-1$ - requestMonitor.done(); - return; - } + public FinalLaunchSequence(DsfExecutor executor, GdbLaunch launch, SessionType sessionType, boolean attach, IProgressMonitor pm) { + super(executor, pm, LaunchMessages.getString("FinalLaunchSequence.0"), LaunchMessages.getString("FinalLaunchSequence.1")); //$NON-NLS-1$ //$NON-NLS-2$ + fLaunch = launch; + fSessionType = sessionType; + fAttach = attach; + } - requestMonitor.done(); - }}, - /* - * Specify GDB's working directory - */ - new Step() { @Override - public void execute(final RequestMonitor requestMonitor) { - IPath dir = null; - try { - dir = fGDBBackend.getGDBWorkingDirectory(); - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get working directory", e)); //$NON-NLS-1$ - requestMonitor.done(); - return; - } + @Override + protected String[] getExecutionOrder(String group) { + if (GROUP_TOP_LEVEL.equals(group)) { + return new String[] { + "stepInitializeFinalLaunchSequence", //$NON-NLS-1$ + "stepSetEnvironmentDirectory", //$NON-NLS-1$ + "stepSourceGDBInitFile", //$NON-NLS-1$ + "stepSetEnvironmentVariables", //$NON-NLS-1$ + "stepSetExecutable", //$NON-NLS-1$ + "stepSetArguments", //$NON-NLS-1$ + "stepSetNonStop", //$NON-NLS-1$ + "stepSetAutoLoadSharedLibrarySymbols", //$NON-NLS-1$ + "stepSetSharedLibraryPaths", //$NON-NLS-1$ + "stepSetSourceLookupPath", //$NON-NLS-1$ + "stepSpecifyCoreFile", //$NON-NLS-1$ + "stepRemoteConnection", //$NON-NLS-1$ + "stepAttachToProcess", //$NON-NLS-1$ + "stepStartTrackingBreakpoints", //$NON-NLS-1$ + "stepStartExecution", //$NON-NLS-1$ + "stepDataModelInitializationComplete", //$NON-NLS-1$ + "stepCleanup", //$NON-NLS-1$ + }; + } + return null; + } - if (dir != null) { - fCommandControl.queueCommand( - fCommandFactory.createMIEnvironmentCD(fCommandControl.getContext(), dir.toPortableString()), - new DataRequestMonitor(getExecutor(), requestMonitor)); - } else { - requestMonitor.done(); - } - }}, - /* - * Source the gdbinit file specified in the launch - */ - new Step() { @Override - public void execute(final RequestMonitor requestMonitor) { - try { - final String gdbinitFile = fGDBBackend.getGDBInitFile(); - - if (gdbinitFile != null && gdbinitFile.length() > 0) { - fCommandControl.queueCommand( - fCommandFactory.createCLISource(fCommandControl.getContext(), gdbinitFile), - new DataRequestMonitor(getExecutor(), requestMonitor) { - @Override - protected void handleCompleted() { - // If the gdbinitFile is the default, then it may not exist and we - // should not consider this an error. - // If it is not the default, then the user must have specified it and - // we want to warn the user if we can't find it. - if (!gdbinitFile.equals(IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT )) { - requestMonitor.setStatus(getStatus()); - } - requestMonitor.done(); - } - }); - } else { - requestMonitor.done(); - } - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get gdbinit option", e)); //$NON-NLS-1$ - requestMonitor.done(); - } - }}, - /* - * Specify environment variables if needed - */ - new Step() { @Override - public void execute(final RequestMonitor requestMonitor) { - boolean clear = false; - Properties properties = new Properties(); - try { - clear = fGDBBackend.getClearEnvironment(); - properties = fGDBBackend.getEnvironmentVariables(); - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get environment information", e)); //$NON-NLS-1$ - requestMonitor.done(); - return; - } + /** + * Initialize the members of the {@link FinalLaunchSequence} class. + * This step is mandatory for the rest fo the sequence to complete. + * @since 4.0 + */ + @Execute + public void stepInitializeFinalLaunchSequence(RequestMonitor requestMonitor) { + fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fLaunch.getSession().getId()); + fGDBBackend = fTracker.getService(IGDBBackend.class); + if (fGDBBackend == null) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot obtain GDBBackend service", null)); //$NON-NLS-1$ + requestMonitor.done(); + return; + } - if (clear == true || properties.size() > 0) { - fCommandControl.setEnvironment(properties, clear, requestMonitor); - } else { - requestMonitor.done(); - } - }}, - /* - * Specify the executable file to be debugged and read the symbol table. - */ - new Step() { @Override - public void execute(final RequestMonitor requestMonitor) { - boolean noFileCommand = IGDBLaunchConfigurationConstants.DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP_DEFAULT; - try { - noFileCommand = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP, - IGDBLaunchConfigurationConstants.DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP_DEFAULT); - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot read use solib symbols for app options", e)); //$NON-NLS-1$ - requestMonitor.done(); - return; - } + fCommandControl = fTracker.getService(IGDBControl.class); + if (fCommandControl == null) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot obtain control service", null)); //$NON-NLS-1$ + requestMonitor.done(); + return; + } - final IPath execPath = fGDBBackend.getProgramPath(); - if (!noFileCommand && execPath != null && !execPath.isEmpty()) { - fCommandControl.queueCommand( - fCommandFactory.createMIFileExecAndSymbols(fCommandControl.getContext(), - execPath.toPortableString()), - new DataRequestMonitor(getExecutor(), requestMonitor)); - } else { - requestMonitor.done(); - } - }}, - /* - * Specify the arguments to the executable file - */ - new Step() { @Override - public void execute(final RequestMonitor requestMonitor) { - try { - String args = fGDBBackend.getProgramArguments(); - - if (args != null) { - fCommandControl.queueCommand( - fCommandFactory.createMIGDBSetArgs(fCommandControl.getContext(), args), - new DataRequestMonitor(getExecutor(), requestMonitor)); - } else { - requestMonitor.done(); - } - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get inferior arguments", e)); //$NON-NLS-1$ - requestMonitor.done(); - } - }}, - /* - * Enable non-stop mode if necessary - */ - new Step() { @Override - public void execute(final RequestMonitor requestMonitor) { - boolean isNonStop = false; - try { - isNonStop = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, - IGDBLaunchConfigurationConstants.DEBUGGER_NON_STOP_DEFAULT); - } catch (CoreException e) { - } + fCommandFactory = fCommandControl.getCommandFactory(); - // GDBs that don't support non-stop don't allow you to set it to false. - // We really should set it to false when GDB supports it though. - // Something to fix later. - if (isNonStop) { - fCommandControl.queueCommand( - fCommandFactory.createMIGDBSetTargetAsync(fCommandControl.getContext(), true), - new DataRequestMonitor(getExecutor(), requestMonitor) { - @Override - protected void handleSuccess() { - fCommandControl.queueCommand( - fCommandFactory.createMIGDBSetPagination(fCommandControl.getContext(), false), - new DataRequestMonitor(getExecutor(), requestMonitor) { - @Override - protected void handleSuccess() { - fCommandControl.queueCommand( - fCommandFactory.createMIGDBSetNonStop(fCommandControl.getContext(), true), - new DataRequestMonitor(getExecutor(), requestMonitor)); - } - }); - } + fProcService = fTracker.getService(IMIProcesses.class); + if (fProcService == null) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot obtain process service", null)); //$NON-NLS-1$ + requestMonitor.done(); + return; + } + + requestMonitor.done(); + } + + /** + * Rollback method for {@link #stepInitializeFinalLaunchSequence()} + * @since 4.0 + */ + @RollBack("stepInitializeFinalLaunchSequence") + public void rollBackInitializeFinalLaunchSequence(RequestMonitor requestMonitor) { + if (fTracker != null) fTracker.dispose(); + fTracker = null; + requestMonitor.done(); + } + + /** + * Specify GDB's working directory. + * @since 4.0 + */ + @Execute + public void stepSetEnvironmentDirectory(final RequestMonitor requestMonitor) { + IPath dir = null; + try { + dir = fGDBBackend.getGDBWorkingDirectory(); + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get working directory", e)); //$NON-NLS-1$ + requestMonitor.done(); + return; + } + + if (dir != null) { + fCommandControl.queueCommand( + fCommandFactory.createMIEnvironmentCD(fCommandControl.getContext(), dir.toPortableString()), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } else { + requestMonitor.done(); + } + } + + /** + * Source the gdbinit file specified in the launch. + * @since 4.0 + */ + @Execute + public void stepSourceGDBInitFile(final RequestMonitor requestMonitor) { + try { + final String gdbinitFile = fGDBBackend.getGDBInitFile(); + + if (gdbinitFile != null && gdbinitFile.length() > 0) { + fCommandControl.queueCommand( + fCommandFactory.createCLISource(fCommandControl.getContext(), gdbinitFile), + new DataRequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleCompleted() { + // If the gdbinitFile is the default, then it may not exist and we + // should not consider this an error. + // If it is not the default, then the user must have specified it and + // we want to warn the user if we can't find it. + if (!gdbinitFile.equals(IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT )) { + requestMonitor.setStatus(getStatus()); + } + requestMonitor.done(); + } + }); + } else { + requestMonitor.done(); + } + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get gdbinit option", e)); //$NON-NLS-1$ + requestMonitor.done(); + } + } + + /** + * Specify environment variables if needed + * @since 4.0 + */ + @Execute + public void stepSetEnvironmentVariables(final RequestMonitor requestMonitor) { + boolean clear = false; + Properties properties = new Properties(); + try { + clear = fGDBBackend.getClearEnvironment(); + properties = fGDBBackend.getEnvironmentVariables(); + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get environment information", e)); //$NON-NLS-1$ + requestMonitor.done(); + return; + } + + if (clear == true || properties.size() > 0) { + fCommandControl.setEnvironment(properties, clear, requestMonitor); + } else { + requestMonitor.done(); + } + } + + /** + * Specify the executable file to be debugged and read the symbol table. + * @since 4.0 + */ + @Execute + public void stepSetExecutable(final RequestMonitor requestMonitor) { + boolean noFileCommand = IGDBLaunchConfigurationConstants.DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP_DEFAULT; + try { + noFileCommand = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP, + IGDBLaunchConfigurationConstants.DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP_DEFAULT); + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot read use solib symbols for app options", e)); //$NON-NLS-1$ + requestMonitor.done(); + return; + } + + final IPath execPath = fGDBBackend.getProgramPath(); + if (!noFileCommand && execPath != null && !execPath.isEmpty()) { + fCommandControl.queueCommand( + fCommandFactory.createMIFileExecAndSymbols(fCommandControl.getContext(), + execPath.toPortableString()), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } else { + requestMonitor.done(); + } + } + + /** + * Specify the arguments to the executable file. + * @since 4.0 + */ + @Execute + public void stepSetArguments(final RequestMonitor requestMonitor) { + try { + String args = fGDBBackend.getProgramArguments(); + + if (args != null) { + fCommandControl.queueCommand( + fCommandFactory.createMIGDBSetArgs(fCommandControl.getContext(), args), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } else { + requestMonitor.done(); + } + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get inferior arguments", e)); //$NON-NLS-1$ + requestMonitor.done(); + } + } + + /** + * Enable non-stop mode if requested. + * @since 4.0 + */ + @Execute + public void stepSetNonStop(final RequestMonitor requestMonitor) { + boolean isNonStop = false; + try { + isNonStop = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, + IGDBLaunchConfigurationConstants.DEBUGGER_NON_STOP_DEFAULT); + } catch (CoreException e) { + } + + // GDBs that don't support non-stop don't allow you to set it to false. + // We really should set it to false when GDB supports it though. + // Something to fix later. + if (isNonStop) { + fCommandControl.queueCommand( + fCommandFactory.createMIGDBSetTargetAsync(fCommandControl.getContext(), true), + new DataRequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + fCommandControl.queueCommand( + fCommandFactory.createMIGDBSetPagination(fCommandControl.getContext(), false), + new DataRequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + fCommandControl.queueCommand( + fCommandFactory.createMIGDBSetNonStop(fCommandControl.getContext(), true), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } + }); + } }); - } else { - requestMonitor.done(); - } - }}, - /* - * Tell GDB to automatically load or not the shared library symbols - */ - new Step() { @Override - public void execute(RequestMonitor requestMonitor) { - try { - boolean autolib = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_AUTO_SOLIB, - IGDBLaunchConfigurationConstants.DEBUGGER_AUTO_SOLIB_DEFAULT); - fCommandControl.queueCommand( - fCommandFactory.createMIGDBSetAutoSolib(fCommandControl.getContext(), autolib), - new DataRequestMonitor(getExecutor(), requestMonitor)); - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot set shared library option", e)); //$NON-NLS-1$ - requestMonitor.done(); - } - }}, - /* - * Set the shared library paths - */ - new Step() { @Override - public void execute(final RequestMonitor requestMonitor) { - try { - List p = fGDBBackend.getSharedLibraryPaths(); - - if (p.size() > 0) { - String[] paths = p.toArray(new String[p.size()]); - fCommandControl.queueCommand( - fCommandFactory.createMIGDBSetSolibSearchPath(fCommandControl.getContext(), paths), - new DataRequestMonitor(getExecutor(), requestMonitor) { - @Override - protected void handleSuccess() { -// Sysroot is not available in GDB6.6 and will make the launch fail in that case. -// Let's remove it for now - requestMonitor.done(); -// // If we are able to set the solib-search-path, -// // we should disable the sysroot variable, as indicated -// // in the GDB documentation. This is to avoid the sysroot -// // variable finding libraries that were not meant to be found. -// fCommandControl.queueCommand( -// new MIGDBSetSysroot(fCommandControl.getContext()), -// new DataRequestMonitor(getExecutor(), requestMonitor)); - }; - }); - } else { - requestMonitor.done(); - } - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot set share library paths", e)); //$NON-NLS-1$ - requestMonitor.done(); - } - }}, - /* - * Setup the source paths - */ - new Step() { @Override - public void execute(RequestMonitor requestMonitor) { - CSourceLookup sourceLookup = fTracker.getService(CSourceLookup.class); - CSourceLookupDirector locator = (CSourceLookupDirector)fLaunch.getSourceLocator(); - ISourceLookupDMContext sourceLookupDmc = (ISourceLookupDMContext)fCommandControl.getContext(); + } else { + requestMonitor.done(); + } + } - sourceLookup.setSourceLookupPath(sourceLookupDmc, locator.getSourceContainers(), requestMonitor); - }}, - /* - * Specify the core file to be debugged if we are launching such a debug session. - */ - new Step() { - // Need a job because prompter.handleStatus will block - class PromptForCoreJob extends Job { - DataRequestMonitor fRequestMonitor; + /** + * Tell GDB to automatically load or not the shared library symbols + * @since 4.0 + */ + @Execute + public void stepSetAutoLoadSharedLibrarySymbols(RequestMonitor requestMonitor) { + try { + boolean autolib = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_AUTO_SOLIB, + IGDBLaunchConfigurationConstants.DEBUGGER_AUTO_SOLIB_DEFAULT); + fCommandControl.queueCommand( + fCommandFactory.createMIGDBSetAutoSolib(fCommandControl.getContext(), autolib), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot set shared library option", e)); //$NON-NLS-1$ + requestMonitor.done(); + } + } - public PromptForCoreJob(String name, DataRequestMonitor rm) { - super(name); - fRequestMonitor = rm; - } + /** + * Set the shared library paths. + * @since 4.0 + */ + @Execute + public void stepSetSharedLibraryPaths(final RequestMonitor requestMonitor) { + try { + List p = fGDBBackend.getSharedLibraryPaths(); - @Override - protected IStatus run(IProgressMonitor monitor) { - final IStatus promptStatus = new Status(IStatus.INFO, "org.eclipse.debug.ui", 200/*STATUS_HANDLER_PROMPT*/, "", null); //$NON-NLS-1$//$NON-NLS-2$ - final IStatus filePrompt = new Status(IStatus.INFO, "org.eclipse.cdt.dsf.gdb.ui", 1001, "", null); //$NON-NLS-1$//$NON-NLS-2$ - // consult a status handler - final IStatusHandler prompter = DebugPlugin.getDefault().getStatusHandler(promptStatus); + if (p.size() > 0) { + String[] paths = p.toArray(new String[p.size()]); + fCommandControl.queueCommand( + fCommandFactory.createMIGDBSetSolibSearchPath(fCommandControl.getContext(), paths), + new DataRequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + // Sysroot is not available in GDB6.6 and will make the launch fail in that case. + // Let's remove it for now + requestMonitor.done(); + // // If we are able to set the solib-search-path, + // // we should disable the sysroot variable, as indicated + // // in the GDB documentation. This is to avoid the sysroot + // // variable finding libraries that were not meant to be found. + // fCommandControl.queueCommand( + // new MIGDBSetSysroot(fCommandControl.getContext()), + // new DataRequestMonitor(getExecutor(), requestMonitor)); + }; + }); + } else { + requestMonitor.done(); + } + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot set share library paths", e)); //$NON-NLS-1$ + requestMonitor.done(); + } + } - final Status NO_CORE_STATUS = new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, - LaunchMessages.getString("LocalCDILaunchDelegate.6"), //$NON-NLS-1$ - null); + /** + * Setup the source paths. + * @since 4.0 + */ + @Execute + public void stepSetSourceLookupPath(RequestMonitor requestMonitor) { + CSourceLookup sourceLookup = fTracker.getService(CSourceLookup.class); + CSourceLookupDirector locator = (CSourceLookupDirector)fLaunch.getSourceLocator(); + ISourceLookupDMContext sourceLookupDmc = (ISourceLookupDMContext)fCommandControl.getContext(); - if (prompter == null) { - fRequestMonitor.setStatus(NO_CORE_STATUS); - fRequestMonitor.done(); - return Status.OK_STATUS; - } + sourceLookup.setSourceLookupPath(sourceLookupDmc, locator.getSourceContainers(), requestMonitor); + } - try { - Object result = prompter.handleStatus(filePrompt, null); - if (result instanceof String) { - fRequestMonitor.setData((String)result); - } else { - fRequestMonitor.setStatus(NO_CORE_STATUS); - } - } catch (CoreException e) { - fRequestMonitor.setStatus(NO_CORE_STATUS); - } - fRequestMonitor.done(); + /** @since 4.0 */ + protected class PromptForCoreJob extends Job { + protected DataRequestMonitor fRequestMonitor; - return Status.OK_STATUS; - } - }; - @Override - public void execute(final RequestMonitor requestMonitor) { - if (fSessionType == SessionType.CORE) { - try { - String coreFile = fLaunch.getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_COREFILE_PATH, ""); //$NON-NLS-1$ - final String coreType = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_POST_MORTEM_TYPE, - IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_TYPE_DEFAULT); - if (coreFile.length() == 0) { - new PromptForCoreJob( - "Prompt for post mortem file", //$NON-NLS-1$ - new DataRequestMonitor(getExecutor(), requestMonitor) { - @Override - protected void handleSuccess() { - String newCoreFile = getData(); - if (newCoreFile == null || newCoreFile.length()== 0) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get post mortem file path", null)); //$NON-NLS-1$ - requestMonitor.done(); - } else { - if (coreType.equals(IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_CORE_FILE)) { - fCommandControl.queueCommand( - fCommandFactory.createMITargetSelectCore(fCommandControl.getContext(), newCoreFile), - new DataRequestMonitor(getExecutor(), requestMonitor)); - } else if (coreType.equals(IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_TRACE_FILE)) { - IGDBTraceControl traceControl = fTracker.getService(IGDBTraceControl.class); - if (traceControl != null) { - ITraceTargetDMContext targetDmc = DMContexts.getAncestorOfType(fCommandControl.getContext(), ITraceTargetDMContext.class); - traceControl.loadTraceData(targetDmc, newCoreFile, requestMonitor); - } else { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Tracing not supported", null)); - requestMonitor.done(); - } - } else { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Invalid post-mortem type", null)); - requestMonitor.done(); - } - } - } - }).schedule(); - } else { - if (coreType.equals(IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_CORE_FILE)) { - fCommandControl.queueCommand( - fCommandFactory.createMITargetSelectCore(fCommandControl.getContext(), coreFile), - new DataRequestMonitor(getExecutor(), requestMonitor)); - } else if (coreType.equals(IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_TRACE_FILE)) { - IGDBTraceControl traceControl = fTracker.getService(IGDBTraceControl.class); - if (traceControl != null) { - ITraceTargetDMContext targetDmc = DMContexts.getAncestorOfType(fCommandControl.getContext(), ITraceTargetDMContext.class); - traceControl.loadTraceData(targetDmc, coreFile, requestMonitor); - } else { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Tracing not supported", null)); - requestMonitor.done(); - } - } else { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Invalid post-mortem type", null)); - requestMonitor.done(); - } - } - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get post mortem file path", e)); - requestMonitor.done(); - } - } else { - requestMonitor.done(); - } - } - }, - /* - * If remote debugging, connect to target. - */ - new Step() { - private boolean fTcpConnection; - private String fRemoteTcpHost; - private String fRemoteTcpPort; - private String fSerialDevice; - - private boolean checkConnectionType(RequestMonitor requestMonitor) { - try { - fTcpConnection = fLaunch.getLaunchConfiguration().getAttribute( - IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, - false); - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve connection mode", e)); //$NON-NLS-1$ - requestMonitor.done(); - return false; - } - return true; - } - - private boolean getSerialDevice(RequestMonitor requestMonitor) { - try { - fSerialDevice = fLaunch.getLaunchConfiguration().getAttribute( - IGDBLaunchConfigurationConstants.ATTR_DEV, "invalid"); //$NON-NLS-1$ - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve serial device", e)); //$NON-NLS-1$ - requestMonitor.done(); - return false; - } - return true; - } - - private boolean getTcpHost(RequestMonitor requestMonitor) { - try { - fRemoteTcpHost = fLaunch.getLaunchConfiguration().getAttribute( - IGDBLaunchConfigurationConstants.ATTR_HOST, "invalid"); //$NON-NLS-1$ - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve remote TCP host", e)); //$NON-NLS-1$ - requestMonitor.done(); - return false; - } - return true; - } + public PromptForCoreJob(String name, DataRequestMonitor rm) { + super(name); + fRequestMonitor = rm; + } - private boolean getTcpPort(RequestMonitor requestMonitor) { - try { - fRemoteTcpPort = fLaunch.getLaunchConfiguration().getAttribute( - IGDBLaunchConfigurationConstants.ATTR_PORT, "invalid"); //$NON-NLS-1$ - } catch (CoreException e) { - requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve remote TCP port", e)); //$NON-NLS-1$ - requestMonitor.done(); - return false; - } - return true; - } - - @Override - public void execute(final RequestMonitor requestMonitor) { - if (fSessionType == SessionType.REMOTE) { - if (!checkConnectionType(requestMonitor)) return; - - if (fTcpConnection) { - if (!getTcpHost(requestMonitor)) return; - if (!getTcpPort(requestMonitor)) return; - - fCommandControl.queueCommand( - fCommandFactory.createMITargetSelect(fCommandControl.getContext(), - fRemoteTcpHost, fRemoteTcpPort, fAttach), - new DataRequestMonitor(getExecutor(), requestMonitor)); - } else { - if (!getSerialDevice(requestMonitor)) return; - - fCommandControl.queueCommand( - fCommandFactory.createMITargetSelect(fCommandControl.getContext(), - fSerialDevice, fAttach), - new DataRequestMonitor(getExecutor(), requestMonitor)); - } - } else { - requestMonitor.done(); - } - } - }, - /* - * If attach session, perform the attach. - */ - new Step() { - @Override - public void execute(final RequestMonitor requestMonitor) { - // A local attach can figure out the binary from the attach - // command. This allows the user not to specify the binary - // in the launch. But for breakpoints to work, we must do the - // attach before we set the breakpoints, i.e., here. - // On the other hand, for a remote attach, we need to specify - // the binary anyway, or use the solib command. In both cases, - // breakpoints can be set before we attach. Therefore, we don't - // force an attach here, but wait for the user to decide to connect - // using the connect action. - if (fAttach && fSessionType != SessionType.REMOTE) { - // If we are attaching, get the process id. - int pid = -1; - try { - // have we already been given the pid (maybe from a JUnit test launch or something) - pid = fLaunch.getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_ATTACH_PROCESS_ID, -1); - } catch (CoreException e) { - // do nothing and fall to below - } + @Override + protected IStatus run(IProgressMonitor monitor) { + final IStatus promptStatus = new Status(IStatus.INFO, "org.eclipse.debug.ui", 200/*STATUS_HANDLER_PROMPT*/, "", null); //$NON-NLS-1$//$NON-NLS-2$ + final IStatus filePrompt = new Status(IStatus.INFO, "org.eclipse.cdt.dsf.gdb.ui", 1001, "", null); //$NON-NLS-1$//$NON-NLS-2$ + // consult a status handler + final IStatusHandler prompter = DebugPlugin.getDefault().getStatusHandler(promptStatus); - if (pid != -1) { - fProcService.attachDebuggerToProcess( - fProcService.createProcessContext(fCommandControl.getContext(), Integer.toString(pid)), - new DataRequestMonitor(getExecutor(), requestMonitor)); - } else { - IConnect connectCommand = (IConnect)fLaunch.getSession().getModelAdapter(IConnect.class); - if (connectCommand != null) { - connectCommand.connect(requestMonitor); - } else { - requestMonitor.done(); - } - } - } else { - requestMonitor.done(); - } - } - }, - /* - * Start tracking the breakpoints once we know we are connected to the target (necessary for remote debugging) - */ - new Step() { @Override - public void execute(final RequestMonitor requestMonitor) { - if (fSessionType != SessionType.CORE) { - MIBreakpointsManager bpmService = fTracker.getService(MIBreakpointsManager.class); - IBreakpointsTargetDMContext breakpointDmc = (IBreakpointsTargetDMContext)fCommandControl.getContext(); + final Status NO_CORE_STATUS = new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, + LaunchMessages.getString("LocalCDILaunchDelegate.6"), //$NON-NLS-1$ + null); - bpmService.startTrackingBreakpoints(breakpointDmc, requestMonitor); - } else { - requestMonitor.done(); - } - }}, - /* - * Start the program. - */ - new Step() { - @Override - public void execute(final RequestMonitor requestMonitor) { - if (fSessionType != SessionType.CORE) { - fCommandControl.start(fLaunch, requestMonitor); - } else { - requestMonitor.done(); - } - } - }, - /* - * Indicate that the Data Model has been filled. This will trigger the Debug view to expand. - */ - new Step() { - @Override - public void execute(final RequestMonitor requestMonitor) { - fLaunch.getSession().dispatchEvent(new DataModelInitializedEvent(fCommandControl.getContext()), - fCommandControl.getProperties()); - requestMonitor.done(); - } - }, - /* - * Cleanup - */ - new Step() { - @Override - public void execute(final RequestMonitor requestMonitor) { - fTracker.dispose(); - fTracker = null; - requestMonitor.done(); - } - }, - }; + if (prompter == null) { + fRequestMonitor.setStatus(NO_CORE_STATUS); + fRequestMonitor.done(); + return Status.OK_STATUS; + } - GdbLaunch fLaunch; - SessionType fSessionType; - boolean fAttach; + try { + Object result = prompter.handleStatus(filePrompt, null); + if (result instanceof String) { + fRequestMonitor.setData((String)result); + } else { + fRequestMonitor.setStatus(NO_CORE_STATUS); + } + } catch (CoreException e) { + fRequestMonitor.setStatus(NO_CORE_STATUS); + } + fRequestMonitor.done(); - private IGDBControl fCommandControl; - private IGDBBackend fGDBBackend; - private IMIProcesses fProcService; - private CommandFactory fCommandFactory; + return Status.OK_STATUS; + } + }; - DsfServicesTracker fTracker; - - public FinalLaunchSequence(DsfExecutor executor, GdbLaunch launch, SessionType sessionType, boolean attach, IProgressMonitor pm) { - super(executor, pm, LaunchMessages.getString("FinalLaunchSequence.0"), LaunchMessages.getString("FinalLaunchSequence.1")); //$NON-NLS-1$ //$NON-NLS-2$ - fLaunch = launch; - fSessionType = sessionType; - fAttach = attach; - } + /** + * If we are dealing with a post-mortem debug session, specify the core file. + * @since 4.0 + */ + @Execute + public void stepSpecifyCoreFile(final RequestMonitor requestMonitor) { + if (fSessionType == SessionType.CORE) { + try { + String coreFile = fLaunch.getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_COREFILE_PATH, ""); //$NON-NLS-1$ + final String coreType = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_POST_MORTEM_TYPE, + IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_TYPE_DEFAULT); + if (coreFile.length() == 0) { + new PromptForCoreJob( + "Prompt for post mortem file", //$NON-NLS-1$ + new DataRequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + String newCoreFile = getData(); + if (newCoreFile == null || newCoreFile.length()== 0) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get post mortem file path", null)); //$NON-NLS-1$ + requestMonitor.done(); + } else { + if (coreType.equals(IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_CORE_FILE)) { + fCommandControl.queueCommand( + fCommandFactory.createMITargetSelectCore(fCommandControl.getContext(), newCoreFile), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } else if (coreType.equals(IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_TRACE_FILE)) { + IGDBTraceControl traceControl = fTracker.getService(IGDBTraceControl.class); + if (traceControl != null) { + ITraceTargetDMContext targetDmc = DMContexts.getAncestorOfType(fCommandControl.getContext(), ITraceTargetDMContext.class); + traceControl.loadTraceData(targetDmc, newCoreFile, requestMonitor); + } else { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Tracing not supported", null)); + requestMonitor.done(); + } + } else { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Invalid post-mortem type", null)); + requestMonitor.done(); + } + } + } + }).schedule(); + } else { + if (coreType.equals(IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_CORE_FILE)) { + fCommandControl.queueCommand( + fCommandFactory.createMITargetSelectCore(fCommandControl.getContext(), coreFile), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } else if (coreType.equals(IGDBLaunchConfigurationConstants.DEBUGGER_POST_MORTEM_TRACE_FILE)) { + IGDBTraceControl traceControl = fTracker.getService(IGDBTraceControl.class); + if (traceControl != null) { + ITraceTargetDMContext targetDmc = DMContexts.getAncestorOfType(fCommandControl.getContext(), ITraceTargetDMContext.class); + traceControl.loadTraceData(targetDmc, coreFile, requestMonitor); + } else { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Tracing not supported", null)); + requestMonitor.done(); + } + } else { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Invalid post-mortem type", null)); + requestMonitor.done(); + } + } + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get post mortem file path", e)); + requestMonitor.done(); + } + } else { + requestMonitor.done(); + } + } - @Override - public Step[] getSteps() { - return fSteps; - } + + private boolean fTcpConnection; + private String fRemoteTcpHost; + private String fRemoteTcpPort; + private String fSerialDevice; + + private boolean checkConnectionType(RequestMonitor requestMonitor) { + try { + fTcpConnection = fLaunch.getLaunchConfiguration().getAttribute( + IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, + false); + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve connection mode", e)); //$NON-NLS-1$ + requestMonitor.done(); + return false; + } + return true; + } + + private boolean getSerialDevice(RequestMonitor requestMonitor) { + try { + fSerialDevice = fLaunch.getLaunchConfiguration().getAttribute( + IGDBLaunchConfigurationConstants.ATTR_DEV, "invalid"); //$NON-NLS-1$ + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve serial device", e)); //$NON-NLS-1$ + requestMonitor.done(); + return false; + } + return true; + } + + private boolean getTcpHost(RequestMonitor requestMonitor) { + try { + fRemoteTcpHost = fLaunch.getLaunchConfiguration().getAttribute( + IGDBLaunchConfigurationConstants.ATTR_HOST, "invalid"); //$NON-NLS-1$ + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve remote TCP host", e)); //$NON-NLS-1$ + requestMonitor.done(); + return false; + } + return true; + } + + private boolean getTcpPort(RequestMonitor requestMonitor) { + try { + fRemoteTcpPort = fLaunch.getLaunchConfiguration().getAttribute( + IGDBLaunchConfigurationConstants.ATTR_PORT, "invalid"); //$NON-NLS-1$ + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve remote TCP port", e)); //$NON-NLS-1$ + requestMonitor.done(); + return false; + } + return true; + } + + /** + * If we are dealing with a remote debugging session, connect to the target. + * @since 4.0 + */ + @Execute + public void stepRemoteConnection(final RequestMonitor requestMonitor) { + if (fSessionType == SessionType.REMOTE) { + if (!checkConnectionType(requestMonitor)) return; + + if (fTcpConnection) { + if (!getTcpHost(requestMonitor)) return; + if (!getTcpPort(requestMonitor)) return; + + fCommandControl.queueCommand( + fCommandFactory.createMITargetSelect(fCommandControl.getContext(), + fRemoteTcpHost, fRemoteTcpPort, fAttach), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } else { + if (!getSerialDevice(requestMonitor)) return; + + fCommandControl.queueCommand( + fCommandFactory.createMITargetSelect(fCommandControl.getContext(), + fSerialDevice, fAttach), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } + } else { + requestMonitor.done(); + } + } + + /** + * If we are dealing with an attach debugging session, perform the attach. + * @since 4.0 + */ + @Execute + public void stepAttachToProcess(final RequestMonitor requestMonitor) { + // A local attach can figure out the binary from the attach + // command. This allows the user not to specify the binary + // in the launch. But for breakpoints to work, we must do the + // attach before we set the breakpoints, i.e., here. + // On the other hand, for a remote attach, we need to specify + // the binary anyway, or use the solib command. In both cases, + // breakpoints can be set before we attach. Therefore, we don't + // force an attach here, but wait for the user to decide to connect + // using the connect action. + if (fAttach && fSessionType != SessionType.REMOTE) { + // If we are attaching, get the process id. + int pid = -1; + try { + // have we already been given the pid (maybe from a JUnit test launch or something) + pid = fLaunch.getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_ATTACH_PROCESS_ID, -1); + } catch (CoreException e) { + // do nothing and fall to below + } + + if (pid != -1) { + fProcService.attachDebuggerToProcess( + fProcService.createProcessContext(fCommandControl.getContext(), Integer.toString(pid)), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } else { + IConnect connectCommand = (IConnect)fLaunch.getSession().getModelAdapter(IConnect.class); + if (connectCommand != null) { + connectCommand.connect(requestMonitor); + } else { + requestMonitor.done(); + } + } + } else { + requestMonitor.done(); + } + } + + /** + * Start tracking the breakpoints. Note that for remote debugging + * we should first connect to the target. + * @since 4.0 + */ + @Execute + public void stepStartTrackingBreakpoints(final RequestMonitor requestMonitor) { + if (fSessionType != SessionType.CORE) { + MIBreakpointsManager bpmService = fTracker.getService(MIBreakpointsManager.class); + IBreakpointsTargetDMContext breakpointDmc = (IBreakpointsTargetDMContext)fCommandControl.getContext(); + + bpmService.startTrackingBreakpoints(breakpointDmc, requestMonitor); + } else { + requestMonitor.done(); + } + } + + /** + * Start executing the program. + * @since 4.0 + */ + @Execute + public void stepStartExecution(final RequestMonitor requestMonitor) { + if (fSessionType != SessionType.CORE) { + fCommandControl.start(fLaunch, requestMonitor); + } else { + requestMonitor.done(); + } + } + + /** + * Indicate that the Data Model has been filled. This will trigger the Debug view to expand. + * @since 4.0 + */ + @Execute + public void stepDataModelInitializationComplete(final RequestMonitor requestMonitor) { + fLaunch.getSession().dispatchEvent(new DataModelInitializedEvent(fCommandControl.getContext()), + fCommandControl.getProperties()); + requestMonitor.done(); + } + + /** + * Cleanup now that the sequence has been run. + * @since 4.0 + */ + @Execute + public void stepCleanup(final RequestMonitor requestMonitor) { + fTracker.dispose(); + fTracker = null; + requestMonitor.done(); + } } diff --git a/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ReflectionSequence.java b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ReflectionSequence.java new file mode 100644 index 00000000000..0cec06ff28f --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/concurrent/ReflectionSequence.java @@ -0,0 +1,264 @@ +/******************************************************************************* + * Copyright (c) 2010 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.concurrent; + +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.internal.DsfPlugin; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * A type of {@link Sequence} which uses reflection and annotations to + * declare its different {@link Sequence.Step}. It can be used to make + * larger DSF sequences more readable and easier to override. + * + * The order of execution of the {@code @Execute} methods is determined by + * the {@link #getExecutionOrder()} method. + * + * {@code @Execute} methods can be grouped in a hierarchical set of groups, + * which should be included in the result of {@link #getExecutionOrder()}. + * Using groups can make overriding slightly simpler. + * + * A usage example follows:
+ *    public class MyReflectionSequence extends ReflectionSequence {
+ *
+ *        public MyReflectionSequence(DsfExecutor executor) {
+ *            super(executor);
+ *        }
+ *        
+ *        protected static final String GROUP_INIT = "GROUP_INIT";
+ *   
+ *        {@code @Override}
+ *        protected String[] getExecutionOrder(String group) {
+ *           if (GROUP_TOP_LEVEL.equals(group)) {
+ *               // This is the top level group which contains
+ *               // all sub-groups, or steps that are not in
+ *               // other groups.
+ *               return new String[] { GROUP_INIT, "step3", "step4" };
+ *           }
+ *           
+ *           // Now deal with the content of sub-groups
+ *           if (GROUP_INIT.equals(group)) {
+ *               return new String[] { "step1", "step2" };
+ *           }
+ *           
+ *           // An invalid group was requested
+ *           return null;
+ *        }
+ *
+ *        {@code @Execute}
+ *        public void step1(RequestMonitor rm) {
+ *            // Do something
+ *            rm.done(); 
+ *        }
+ *
+ *        {@code @RollBack("step1")}
+ *        public void rollBack1(RequestMonitor rm) {
+ *        	// Rollback what was done in step1()
+ *        	rm.done(); 
+ *        }
+ *
+ *        {@code @Execute}
+ *        public void step2(RequestMonitor rm) {
+ *            // Do something else
+ *            rm.done(); 
+ *        }
+ *        
+ *        {@code @Execute}
+ *        public void step3(RequestMonitor rm) {
+ *            // Do something else
+ *            rm.done(); 
+ *        }
+ *
+ *        {@code @Execute}
+ *        public void step4(RequestMonitor rm) {
+ *            // Do something else
+ *            rm.done(); 
+ *        }
+ *    }
+ * 
+ * + * @since 2.2 + */ +abstract public class ReflectionSequence extends Sequence { + + /** + * The top-level group in which all sub-groups or steps that + * are not part of any sub-groups are contained. This group + * identifier is the one that will be used in the first call to + * {@link #getExecutionOrder()}. + */ + public static final String GROUP_TOP_LEVEL = "GROUP_TOP_LEVEL"; //$NON-NLS-1$ + + private Step[] fReflectionSteps; + + /** + * Annotation used to indicate that a method corresponds to an + * {@link Sequence.Step#execute()} method of a {@link Sequence.Step}. + * The annotated method must be declared public. + */ + @Inherited + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public static @interface Execute {} + + /** + * Annotation used to indicate that a method corresponds to a + * {@link Sequence.Step#rollBack()} method of a {@link Sequence.Step}. + * Declaring such a method is optional. If declared, the annotated + * method must be declared public. + */ + @Inherited + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public static @interface RollBack { + /** + * Name of the method tagged with the {@link Execute} annotation that this method rolls back. + */ + String value(); + } + + private class ReflectionStep extends Step { + final private Method fExecuteMethod; + final private Method fRollbackMethod; + + private ReflectionStep(Method executeMethod, Method rollbackMethod) { + assert executeMethod != null; + + fExecuteMethod = executeMethod;; + fRollbackMethod = rollbackMethod; + } + + @Override + public void execute(RequestMonitor rm) { + try { + fExecuteMethod.invoke(ReflectionSequence.this, rm); + } catch (Exception e) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Error executing step execute method: " + fExecuteMethod.getName(), e)); //$NON-NLS-1$ + rm.done(); + } + } + + @Override + public void rollBack(RequestMonitor rm) { + if (fRollbackMethod == null) { + super.rollBack(rm); + } else { + try { + fRollbackMethod.invoke(ReflectionSequence.this, rm); + } catch (Exception e) { + rm.setStatus(new Status(IStatus.ERROR, DsfPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Error executing step rollback method: " + fRollbackMethod.getName(), e)); //$NON-NLS-1$ + rm.done(); + } + } + } + } + + public ReflectionSequence(DsfExecutor executor) { + super(executor); + } + + public ReflectionSequence(DsfExecutor executor, RequestMonitor rm) { + super(executor, rm); + } + + public ReflectionSequence(DsfExecutor executor, IProgressMonitor pm, String taskName, String rollbackTaskName) { + super(executor, pm, taskName, rollbackTaskName); + } + + public ReflectionSequence(DsfExecutor executor, RequestMonitorWithProgress rm, String taskName, String rollbackTaskName) { + super(executor, rm, taskName, rollbackTaskName); + } + + /** + * This method must return the execution order of {@code @Execute} methods and/or groups. + * + * @param groupName The name of a group for which the list of {@code @Execute} methods + * or sub-groups should be returned in the order they should be executed. + * If the concept of groups is not used, then this parameter can be ignored, + * at the top-level ordering should be returned. + * + * @return An array containing the list of @Execute methods and groups in the order + * they should be executed, or null if the specified groupName is unknown. + */ + abstract protected String[] getExecutionOrder(String groupName); + + @Override + public Step[] getSteps() { + if (fReflectionSteps == null) { + Map executeMethods = getAnnotatedMethods(Execute.class); + Map rollBackMethods = getAnnotatedMethods(RollBack.class); + List steps = getGroupSteps(GROUP_TOP_LEVEL, executeMethods, rollBackMethods); + fReflectionSteps = steps.toArray(new ReflectionStep[steps.size()]); + } + return fReflectionSteps; + } + + private List getGroupSteps(String groupId, Map executeMethods, Map rollBackMethods) { + List steps = new ArrayList(executeMethods.size()); + + String[] order = getExecutionOrder(groupId); + if (order == null) { + throw new RuntimeException("Unknown group in sequence: " + groupId); //$NON-NLS-1$ + } + + for (String name : order) { + Method executeMethod = executeMethods.get(name); + if (executeMethod == null) { + // name is a group id + steps.addAll(getGroupSteps(name, executeMethods, rollBackMethods)); + } else { + steps.add(new ReflectionStep(executeMethod, rollBackMethods.get(executeMethod.getName()))); + } + } + return steps; + } + + private Map getAnnotatedMethods(Class annotationType) { + Map retVal = new HashMap(); + try { + Method[] methods = getClass().getMethods(); + for (Method method : methods) { + if (method.isAnnotationPresent(annotationType)) { + Class[] paramTypes = method.getParameterTypes(); + if (paramTypes.length != 1) { // must have one and only param, the RequestMonitor + throw new IllegalArgumentException("Method " + //$NON-NLS-1$ + method.getDeclaringClass().getSimpleName() + "#" + method.getName() + //$NON-NLS-1$ + " must have a single parameter"); //$NON-NLS-1$ + } else { + if (annotationType.equals(Execute.class)) { + retVal.put(method.getName(), method); + } else {// @Rollback + retVal.put(method.getAnnotation(RollBack.class).value(), method); + } + } + } + } + } catch(SecurityException e) { + throw new IllegalArgumentException("No permission to access ReflectionSequence method"); //$NON-NLS-1$ + } + return retVal; + } + + +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/concurrent/DsfSequenceTests.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/concurrent/DsfSequenceTests.java index d8ac41785a6..e9019429dbd 100644 --- a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/concurrent/DsfSequenceTests.java +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/concurrent/DsfSequenceTests.java @@ -17,6 +17,7 @@ import java.util.concurrent.TimeUnit; import junit.framework.Assert; import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.ReflectionSequence; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.concurrent.Sequence; import org.eclipse.cdt.tests.dsf.DsfTestPlugin; @@ -93,6 +94,55 @@ public class DsfSequenceTests { Assert.assertTrue(sequence.isDone()); Assert.assertTrue(!sequence.isCancelled()); } + + + public class SimpleReflectionSequence extends ReflectionSequence { + + public int fStepCounter; + + public SimpleReflectionSequence() { + super(fExecutor); + } + + @Override + protected String[] getExecutionOrder(String groupName) { + return new String[] { "step1", "step2"}; + } + + @Execute() + public void step1(RequestMonitor rm) { + fStepCounter++; + rm.done(); + } + + @Execute() + public void step2(RequestMonitor rm) { + fStepCounter++; + rm.done(); + } + } + + @Test + public void simpleReflectionTest() throws InterruptedException, ExecutionException { + + // Create, start, and wait for the sequence. + SimpleReflectionSequence sequence = new SimpleReflectionSequence(); + + //Sequence sequence = new SimpleReflectionSequence(); + Assert.assertTrue(!sequence.isDone()); + Assert.assertTrue(!sequence.isCancelled()); + + fExecutor.execute(sequence); + sequence.get(); + + // Check the count + Assert.assertTrue(sequence.fStepCounter == 2); + + // Check post conditions + Assert.assertTrue(sequence.isDone()); + Assert.assertTrue(!sequence.isCancelled()); + } + @Test (expected = ExecutionException.class) public void rollbackTest() throws InterruptedException, ExecutionException { @@ -149,6 +199,133 @@ public class DsfSequenceTests { Assert.assertTrue("Exception should have been thrown", false); //$NON-NLS-1$ } + + public class RollBackReflectionSequence extends ReflectionSequence { + + public int fStepCounter; + public int fRollBackCounter; + + public RollBackReflectionSequence() { + super(fExecutor); + } + + @Override + protected String[] getExecutionOrder(String groupName) { + return new String[] { "step1", "step2"}; + } + + @Execute() + public void step1(RequestMonitor rm) { + fStepCounter++; + rm.done(); + } + + @RollBack("step1") + public void rollBack1(RequestMonitor rm) { + fRollBackCounter++; + rm.done(); + } + + @Execute() + public void step2(RequestMonitor rm) { + fStepCounter++; + rm.setStatus(new Status(IStatus.ERROR, DsfTestPlugin.PLUGIN_ID, -1, "", null)); //$NON-NLS-1$ + rm.done(); + } + + @RollBack("step2") + public void rollBack2(RequestMonitor rm) { + fRollBackCounter++; + rm.done(); + } + } + + @Test (expected = ExecutionException.class) + public void rollbackReflectionTest() throws InterruptedException, ExecutionException { + // Create and start. + RollBackReflectionSequence sequence = new RollBackReflectionSequence(); + fExecutor.execute(sequence); + + // Block and wait for sequence to complete. + try { + sequence.get(); + } finally { + // Both steps should be performed + Assert.assertEquals(2, sequence.fStepCounter); + // Only one step is rolled back, the first one. + Assert.assertEquals(1, sequence.fRollBackCounter); + + // Check state from Future interface + Assert.assertTrue(sequence.isDone()); + Assert.assertTrue(!sequence.isCancelled()); + } + Assert.assertTrue("Exception should have been thrown", false); //$NON-NLS-1$ + } + + public class RollBackReflectionSequence2 extends ReflectionSequence { + + public int fStepCounter; + public int fRollBackCounter; + + public RollBackReflectionSequence2() { + super(fExecutor); + } + + @Override + protected String[] getExecutionOrder(String groupName) { + return new String[] { "step1", "step2", "step3" }; + } + + @Execute() + public void step1(RequestMonitor rm) { + fStepCounter++; + rm.done(); + } + + @RollBack("step1") + public void rollBack1(RequestMonitor rm) { + fRollBackCounter++; + rm.done(); + } + @Execute() + + public void step2(RequestMonitor rm) { + fStepCounter++; + rm.done(); + } + + @Execute() + public void step3(RequestMonitor rm) { + fStepCounter++; + rm.setStatus(new Status(IStatus.ERROR, DsfTestPlugin.PLUGIN_ID, -1, "", null)); //$NON-NLS-1$ + rm.done(); + } + } + + @Test (expected = ExecutionException.class) + public void rollbackReflectionWithoutRollBackMethodTest() throws InterruptedException, ExecutionException { + // Create and start. + RollBackReflectionSequence2 sequence = new RollBackReflectionSequence2(); + fExecutor.execute(sequence); + + // Block and wait for sequence to complete. + try { + sequence.get(); + } finally { + // All three steps should be performed + Assert.assertEquals(3, sequence.fStepCounter); + // Two steps are rolled back, but only the first one has + // a rollback method. + Assert.assertEquals(1, sequence.fRollBackCounter); + + // Check state from Future interface + Assert.assertTrue(sequence.isDone()); + Assert.assertTrue(!sequence.isCancelled()); + } + Assert.assertTrue("Exception should have been thrown", false); //$NON-NLS-1$ + } + + /** * The goal of this test it to check that if an exception is thrown within * the Step.execute(), the step will return from the Future.get() method.