/******************************************************************************* * This file is protected by Copyright. * Please refer to the COPYRIGHT file distributed with this source distribution. * * This file is part of REDHAWK IDE. * * 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 *******************************************************************************/ package gov.redhawk.ide.debug; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.variables.IStringVariableManager; import org.eclipse.core.variables.VariablesPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.emf.common.util.BasicDiagnostic; import org.eclipse.emf.common.util.Diagnostic; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.util.Diagnostician; import org.omg.CORBA.BAD_OPERATION; import org.omg.CORBA.SystemException; import org.omg.CORBA.TCKind; import org.omg.CosNaming.NamingContextExt; import org.omg.CosNaming.NamingContextExtHelper; import org.omg.CosNaming.NamingContextPackage.NotFound; import CF.DataType; import CF.Resource; import CF.ResourceOperations; import CF.LifeCyclePackage.InitializeError; import CF.PropertyEmitterPackage.AlreadyInitialized; import CF.PropertySetPackage.InvalidConfiguration; import CF.PropertySetPackage.PartialConfiguration; import CF.ResourcePackage.StartError; import gov.redhawk.ide.debug.internal.LaunchLogger; import gov.redhawk.ide.debug.internal.jobs.TerminateJob; import gov.redhawk.ide.debug.variables.LaunchVariables; import gov.redhawk.model.sca.IRefreshable; import gov.redhawk.model.sca.ProfileObjectWrapper; import gov.redhawk.model.sca.RefreshDepth; import gov.redhawk.model.sca.ScaAbstractComponent; import gov.redhawk.model.sca.ScaAbstractProperty; import gov.redhawk.model.sca.ScaComponent; import gov.redhawk.model.sca.ScaDevice; import gov.redhawk.model.sca.ScaFactory; import gov.redhawk.model.sca.ScaPropertyContainer; import gov.redhawk.model.sca.ScaService; import gov.redhawk.model.sca.commands.ScaModelCommand; import gov.redhawk.model.sca.commands.ScaModelCommandWithResult; import gov.redhawk.sca.launch.ScaLaunchConfigurationConstants; import gov.redhawk.sca.launch.ScaLaunchConfigurationUtil; import gov.redhawk.sca.util.Debug; import gov.redhawk.sca.util.ORBUtil; import gov.redhawk.sca.util.OrbSession; import gov.redhawk.sca.util.SubMonitor; import mil.jpeojtrs.sca.prf.Properties; import mil.jpeojtrs.sca.prf.PropertyConfigurationType; import mil.jpeojtrs.sca.prf.util.PropertiesUtil; import mil.jpeojtrs.sca.scd.ComponentType; import mil.jpeojtrs.sca.scd.SoftwareComponent; import mil.jpeojtrs.sca.spd.SoftPkg; import mil.jpeojtrs.sca.util.CFErrorFormatter; import mil.jpeojtrs.sca.util.NamedThreadFactory; import mil.jpeojtrs.sca.util.ScaResourceFactoryUtil; /** * A collection of utility methods that help to launch a {@link SoftPkg}. * * @since 4.0 */ public final class SpdLauncherUtil { /** * The status code for indicating errors with XML files related to the SPD (e.g. PRF, SCD, etc) instead of the SPD itself. * @since 8.3 */ public static final int ERR_CODE_RELATED_XML = 1; private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool(new NamedThreadFactory(SpdLauncherUtil.class.getName())); private static final Debug DEBUG_ARGS = new Debug(ScaDebugPlugin.getInstance(), "LauncherArgs"); private SpdLauncherUtil() { } /** * Waits for a {@link SoftPkg} to register with the naming context up to the timeout specified in the launch. Then: * <ol> * <li>Performs initial life cycle (initializeProperties, initialize, configure, start)</li> * <li>Creates a model object and refreshes it</li> * <li>Adds the model object to the model</li> * </ol> * @param spd The {@link SoftPkg} profile that was launched * @param configuration The launch configuration * @param mode The launch mode (run/debug) * @param launch The launch object * @param monitor A progress monitor * @throws CoreException */ public static void postLaunch(final SoftPkg spd, final ILaunchConfiguration configuration, final String mode, final ILaunch launch, final IProgressMonitor monitor) throws CoreException { final int WORK_FIND_CORBA_OBJ = 10; final int WORK_GENERAL = 1; SubMonitor progress = SubMonitor.convert(monitor, "Post-launch tasks", WORK_FIND_CORBA_OBJ + 7 * WORK_GENERAL); final ComponentType type; if (spd.getDescriptor() == null || spd.getDescriptor().getComponent() == null) { String errorMsg = String.format("Unable to determine the component type for %s during post-launch in the sandbox.", spd.getName()); ScaDebugPlugin.logWarning(errorMsg, null); type = ComponentType.OTHER; } else { type = SoftwareComponent.Util.getWellKnownComponentType(spd.getDescriptor().getComponent()); } LocalAbstractComponent comp = null; String description = "unknown resource"; try { switch (type) { case DEVICE: comp = SpdLauncherUtil.postLaunchDevice(launch, progress.newChild(WORK_FIND_CORBA_OBJ)); description = String.format("device %s", launch.getAttribute(LaunchVariables.DEVICE_LABEL)); break; case EVENT_SERVICE: case SERVICE: comp = SpdLauncherUtil.postLaunchService(launch, progress.newChild(WORK_FIND_CORBA_OBJ)); description = String.format("service %s", launch.getAttribute(LaunchVariables.SERVICE_NAME)); break; case RESOURCE: comp = SpdLauncherUtil.postLaunchResource(spd, launch, progress.newChild(WORK_FIND_CORBA_OBJ)); description = String.format("component %s", launch.getAttribute(LaunchVariables.NAME_BINDING)); break; default: String errorMsg = String.format("Unsupported component type during post-launch in the sandbox (%s) - treating as component", spd.getName()); ScaDebugPlugin.logWarning(errorMsg, null); comp = SpdLauncherUtil.postLaunchResource(spd, launch, progress.newChild(WORK_FIND_CORBA_OBJ)); break; } } catch (final CoreException e) { // If there is a problem with the component terminate it launch.terminate(); throw e; } if (comp == null) { // the launch was terminated or otherwise interrupted throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Failed to resolve component.", null)); } // Update the model with information about the details of the launch final LocalAbstractComponent newComponent = comp; final String execString = launch.getAttribute(LaunchVariables.EXEC_PARAMS); final String implID = launch.getLaunchConfiguration().getAttribute(ScaDebugLaunchConstants.ATT_IMPL_ID, (String) null); ScaModelCommand.execute(newComponent, new ScaModelCommand() { @Override public void execute() { newComponent.setExecParam(execString); newComponent.setImplementationID(implID); newComponent.setMode(launch.getLaunchMode()); newComponent.setLaunch(launch); } }); progress.worked(WORK_GENERAL); // Set the profile object, if applicable if (newComponent instanceof ProfileObjectWrapper< ? >) { final ProfileObjectWrapper< ? > obj = (ProfileObjectWrapper< ? >) newComponent; obj.fetchProfileObject(progress.newChild(WORK_GENERAL)); } else { progress.notWorked(WORK_GENERAL); } // Perform configure for properties as needed if (newComponent instanceof ScaPropertyContainer< ? , ? >) { final ScaPropertyContainer< ? , ? > scaComp = (ScaPropertyContainer< ? , ? >) newComponent; // Load the properties from the PRF and override with values from the launch configuration final ScaComponent propHolder = ScaFactory.eINSTANCE.createScaComponent(); propHolder.setProfileURI(spd.eResource().getURI()); propHolder.fetchProfileObject(progress.newChild(WORK_GENERAL)); propHolder.fetchProperties(progress.newChild(WORK_GENERAL)); ScaLaunchConfigurationUtil.loadProperties(launch.getLaunchConfiguration(), propHolder); // Find configurable properties that aren't set to their default List<DataType> configureProps = new ArrayList<DataType>(); for (final ScaAbstractProperty< ? > prop : propHolder.getProperties()) { if (!prop.isDefaultValue() && !prop.getDefinition().isKind(PropertyConfigurationType.PROPERTY) && PropertiesUtil.canConfigure(prop.getDefinition())) { configureProps.add(new DataType(prop.getId(), prop.toAny())); } } // Configure properties // NOTE: Devices are configured as part of Register Device if (type != ComponentType.DEVICE) { try { scaComp.configure(configureProps.toArray(new DataType[configureProps.size()])); } catch (final PartialConfiguration e) { LaunchLogger.INSTANCE.writeToConsole(launch, CFErrorFormatter.format(e, description), ConsoleColor.STDERR); } catch (final InvalidConfiguration e) { LaunchLogger.INSTANCE.writeToConsole(launch, CFErrorFormatter.format(e, description), ConsoleColor.STDERR); } } progress.worked(WORK_GENERAL); } else { progress.notWorked(3 * WORK_GENERAL); } // Start the component, if requested final boolean autoStart = launch.getLaunchConfiguration().getAttribute(ScaLaunchConfigurationConstants.ATT_START, ScaLaunchConfigurationConstants.DEFAULT_VALUE_ATT_START); if (newComponent instanceof ResourceOperations) { if (autoStart) { try { ((ResourceOperations) newComponent).start(); } catch (final StartError e) { LaunchLogger.INSTANCE.writeToConsole(launch, CFErrorFormatter.format(e, description), ConsoleColor.STDERR); } progress.worked(WORK_GENERAL); } else { progress.notWorked(WORK_GENERAL); } } else { progress.notWorked(WORK_GENERAL); } // Perform a full refresh of the object, if applicable if (newComponent instanceof IRefreshable) { try { ((IRefreshable) newComponent).refresh(progress.newChild(WORK_GENERAL), RefreshDepth.FULL); } catch (InterruptedException e) { // PASS } } else { progress.notWorked(WORK_GENERAL); } } private static LocalAbstractComponent postLaunchResource(SoftPkg spd, final ILaunch launch, final IProgressMonitor monitor) throws CoreException { final int WORK_WAIT_REGISTRATION = 10; final int WORK_INITIALIZE_RESOURCE = 10; SubMonitor progress = SubMonitor.convert(monitor, "Wait for component to register", WORK_WAIT_REGISTRATION); final String nameBinding = launch.getAttribute(LaunchVariables.NAME_BINDING); final String namingContextIOR = launch.getAttribute(LaunchVariables.NAMING_CONTEXT_IOR); final String compID = launch.getAttribute(LaunchVariables.COMPONENT_IDENTIFIER); if (nameBinding == null || namingContextIOR == null) { throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "No naming context or name binding to locate component with, post launch failed. " + compID, null)); } // Wait for the component to be registered in the appropriate naming context of the naming service final Future<org.omg.CORBA.Object> future = SpdLauncherUtil.EXECUTOR.submit(new Callable<org.omg.CORBA.Object>() { @Override public org.omg.CORBA.Object call() throws Exception { OrbSession session = OrbSession.createSession(); NamingContextExt namingContext = null; try { // Resolve the naming context while (namingContext == null) { if (launch.isTerminated()) { throw new EarlyTerminationException("Component terminated while waiting to launch. " + compID, launch); } try { namingContext = NamingContextExtHelper.narrow(session.getOrb().string_to_object(namingContextIOR)); } catch (SystemException e) { // PASS } if (namingContext == null) { try { Thread.sleep(100); } catch (final InterruptedException e1) { throw e1; } } } while (!launch.isTerminated()) { try { return namingContext.resolve_str(nameBinding); } catch (NotFound e) { try { Thread.sleep(100); } catch (final InterruptedException e1) { throw e1; } } } throw new EarlyTerminationException("Component terminated while waiting to launch. " + compID, launch); } finally { if (namingContext != null) { ORBUtil.release(namingContext); namingContext = null; } session.dispose(); } } }); org.omg.CORBA.Object corbaObj; try { // Invoke the Future to get the parent waveform and the Resource object final int timeout = launch.getLaunchConfiguration().getAttribute(ScaDebugLaunchConstants.ATT_LAUNCH_TIMEOUT, ScaDebugLaunchConstants.DEFAULT_ATT_LAUNCH_TIMEOUT); if (timeout < 0 || ILaunchManager.DEBUG_MODE.equals(launch.getLaunchMode())) { // In debug-mode wait they may have placed a break-point // that is delaying registration so wait forever corbaObj = future.get(); } else { corbaObj = future.get(timeout, TimeUnit.SECONDS); } progress.worked(WORK_WAIT_REGISTRATION); } catch (final InterruptedException ex) { String msg = String.format("Interrupted waiting for component %s to launch.", nameBinding); throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, msg, ex)); } catch (final ExecutionException ex) { String msg = String.format("Error while waiting for component %s to launch.", nameBinding); throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, msg, ex.getCause())); } catch (final TimeoutException ex) { future.cancel(true); String msg = String.format("Timed out waiting for component %s to launch.", nameBinding); throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, msg, ex)); } LocalAbstractComponent resource; if (SoftPkg.Util.isComponentHost(spd.eResource().getURI())) { resource = SpdLauncherUtil.postLaunchComponentHost(spd, launch, corbaObj, nameBinding, progress.newChild(WORK_INITIALIZE_RESOURCE)); } else { resource = SpdLauncherUtil.postLaunchComponent(spd, launch, corbaObj, nameBinding, progress.newChild(WORK_INITIALIZE_RESOURCE)); } return resource; } private static LocalScaExecutableDevice postLaunchComponentHost(final SoftPkg spd, final ILaunch launch, final org.omg.CORBA.Object corbaObj, String nameBinding, final IProgressMonitor monitor) throws CoreException { final int WORK_GENERAL = 10, WORK_ADD_TO_MODEL = 1; SubMonitor progress = SubMonitor.convert(monitor, "Wait for component to initialize", WORK_GENERAL + WORK_ADD_TO_MODEL); final LocalScaExecutableDevice componentHost = ScaDebugFactory.eINSTANCE.createLocalScaExecutableDevice(); componentHost.setCorbaObj(corbaObj); componentHost.setLaunch(launch); componentHost.setProfileURI(spd.eResource().getURI()); // Load the properties from the PRF and override with values from the launch configuration final ScaComponent propHolder = ScaFactory.eINSTANCE.createScaComponent(); propHolder.setProfileURI(spd.eResource().getURI()); propHolder.fetchProfileObject(progress.newChild(WORK_GENERAL)); propHolder.fetchProperties(progress.newChild(WORK_GENERAL)); ScaLaunchConfigurationUtil.loadProperties(launch.getLaunchConfiguration(), propHolder); initializeResource(componentHost, propHolder, launch, nameBinding, monitor); // Add the ComponentHost to its waveform final String parentWaveformName = launch.getLaunchConfiguration().getAttribute(LaunchVariables.WAVEFORM_NAME, ""); if (parentWaveformName.isEmpty()) { final LocalScaWaveform sandboxWaveform = ScaDebugPlugin.getInstance().getLocalSca().getSandboxWaveform(); ScaModelCommand.execute(sandboxWaveform, new ScaModelCommand() { @Override public void execute() { sandboxWaveform.setComponentHost(componentHost); } }); } else { Boolean result = ScaModelCommandWithResult.execute(ScaDebugPlugin.getInstance().getLocalSca(), new ScaModelCommandWithResult<Boolean>() { @Override public void execute() { for (LocalScaWaveform waveform : ScaDebugPlugin.getInstance().getLocalSca().getWaveforms()) { if (parentWaveformName.equals(waveform.getName())) { waveform.setComponentHost(componentHost); setResult(true); return; } } setResult(false); } }); if (result == null || !result) { throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Unable to find the parent waveform for a launched component")); } } progress.newChild(WORK_ADD_TO_MODEL); return componentHost; } /** * Uses the naming context and name binding from the launch to resolve a reference to the component. The * {@link Resource#initializeProperties(DataType[])} and {@link Resource#initialize()} calls are performed and * a new model object is added to the model. * @param spd The {@link SoftPkg} for the component being launched * @param launch The launch that just occurred * @param nameBinding * @param corbaObj * @param monitor A progress monitor * @return A new model object for the component * @throws CoreException */ private static LocalAbstractComponent postLaunchComponent(final SoftPkg spd, final ILaunch launch, final org.omg.CORBA.Object corbaObj, final String nameBinding, final IProgressMonitor monitor) throws CoreException { final int WORK_GENERAL = 10, WORK_ADD_TO_MODEL = 1; SubMonitor progress = SubMonitor.convert(monitor, "Wait for component to initialize", WORK_GENERAL + WORK_ADD_TO_MODEL); // Create the model object final LocalScaComponent component = ScaDebugFactory.eINSTANCE.createLocalScaComponent(); component.setCorbaObj(corbaObj); component.setName(nameBinding); component.setProfileURI(spd.eResource().getURI()); // Load the properties from the PRF and override with values from the launch configuration final ScaComponent propHolder = ScaFactory.eINSTANCE.createScaComponent(); propHolder.setProfileURI(spd.eResource().getURI()); propHolder.fetchProfileObject(progress.newChild(WORK_GENERAL)); propHolder.fetchProperties(progress.newChild(WORK_GENERAL)); ScaLaunchConfigurationUtil.loadProperties(launch.getLaunchConfiguration(), propHolder); initializeResource(component, propHolder, launch, nameBinding, monitor); // Add the component to its waveform final String parentWaveformName = launch.getLaunchConfiguration().getAttribute(LaunchVariables.WAVEFORM_NAME, ""); if (parentWaveformName.isEmpty()) { final LocalScaWaveform sandboxWaveform = ScaDebugPlugin.getInstance().getLocalSca().getSandboxWaveform(); ScaModelCommand.execute(sandboxWaveform, new ScaModelCommand() { @Override public void execute() { sandboxWaveform.getComponents().add(component); } }); } else { Boolean result = ScaModelCommandWithResult.execute(ScaDebugPlugin.getInstance().getLocalSca(), new ScaModelCommandWithResult<Boolean>() { @Override public void execute() { for (LocalScaWaveform waveform : ScaDebugPlugin.getInstance().getLocalSca().getWaveforms()) { if (parentWaveformName.equals(waveform.getName())) { waveform.getComponents().add(component); setResult(true); return; } } setResult(false); } }); if (result == null || !result) { throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Unable to find the parent waveform for a launched component")); } } progress.newChild(WORK_ADD_TO_MODEL); return component; } private static void initializeResource(ScaAbstractComponent< ? > resource, ScaComponent propHolder, final ILaunch launch, String nameBinding, final IProgressMonitor monitor) { final int WORK_INITIALIZE_PROPS = 1, WORK_INITIALIZE = 1; SubMonitor progress = SubMonitor.convert(monitor, WORK_INITIALIZE_PROPS + WORK_INITIALIZE); // Collect non-null properties of type 'property' (but not type 'execparam') List<DataType> initializeProps = new ArrayList<DataType>(); for (final ScaAbstractProperty< ? > prop : propHolder.getProperties()) { if (PropertiesUtil.canInitialize(prop.getDefinition())) { DataType dt = prop.getProperty(); if (dt.value != null && dt.value.type().kind() != TCKind.tk_null) { initializeProps.add(dt); } } } // Initialize properties try { DataType[] initializePropsArray = initializeProps.toArray(new CF.DataType[initializeProps.size()]); resource.initializeProperties(initializePropsArray); } catch (AlreadyInitialized e) { LaunchLogger.INSTANCE.writeToConsole(launch, CFErrorFormatter.format(e, "component " + nameBinding), ConsoleColor.STDERR); } catch (InvalidConfiguration e) { LaunchLogger.INSTANCE.writeToConsole(launch, CFErrorFormatter.format(e, "component " + nameBinding), ConsoleColor.STDERR); } catch (PartialConfiguration e) { LaunchLogger.INSTANCE.writeToConsole(launch, CFErrorFormatter.format(e, "component " + nameBinding), ConsoleColor.STDERR); } catch (BAD_OPERATION e) { String msg; if (initializeProps.size() == 0) { msg = String.format("Could not call initializeProperties on component %s in the sandbox (CORBA BAD_OPERATION). " + "If the installed version of REDHAWK is pre-2.0, this is expected and can be ignored.", nameBinding); } else { msg = "Component has properties of kind 'property', but does not appear to support REDHAWK 2.0 API (CORBA BAD_OPERATION)"; } LaunchLogger.INSTANCE.writeToConsole(launch, msg, ConsoleColor.STDERR); } progress.newChild(WORK_INITIALIZE_PROPS); // Initialize try { resource.initialize(); } catch (InitializeError e) { LaunchLogger.INSTANCE.writeToConsole(launch, CFErrorFormatter.format(e, "component " + nameBinding), ConsoleColor.STDERR); } progress.newChild(WORK_INITIALIZE); } /** * Locates a service that was just launched locally. The service name form the launch is matched against services * in the sandbox device manager. * * @param launch The launch that just occurred * @param monitor A progress monitor * @return The newly launched service * @throws CoreException */ private static LocalAbstractComponent postLaunchService(final ILaunch launch, final IProgressMonitor monitor) throws CoreException { SubMonitor progress = SubMonitor.convert(monitor, "Wait for service to register", 1); final String name = launch.getAttribute(LaunchVariables.SERVICE_NAME); final LocalSca localSca = ScaDebugPlugin.getInstance().getLocalSca(null); final Future<LocalAbstractComponent> future = SpdLauncherUtil.EXECUTOR.submit(new Callable<LocalAbstractComponent>() { @Override public LocalAbstractComponent call() throws Exception { LocalAbstractComponent retVal = null; while (retVal == null) { // If this launch was terminated, immediately bail if (launch.isTerminated()) { throw new EarlyTerminationException("Service terminated while waiting to launch. " + name, launch); } for (final ScaService service : localSca.getSandboxDeviceManager().fetchServices(null, RefreshDepth.SELF)) { if (name.equals(service.getName())) { retVal = (LocalAbstractComponent) service; break; } } if (retVal == null) { try { Thread.sleep(500); } catch (final InterruptedException e) { throw e; } } } return retVal; } }); try { final int timeout = launch.getLaunchConfiguration().getAttribute(ScaDebugLaunchConstants.ATT_LAUNCH_TIMEOUT, ScaDebugLaunchConstants.DEFAULT_ATT_LAUNCH_TIMEOUT); if (timeout < 0 || ILaunchManager.DEBUG_MODE.equals(launch.getLaunchMode())) { // In debug-mode wait they may have placed a break-point // that is delaying registration so wait forever final LocalAbstractComponent newComponent = future.get(); return newComponent; } else { final LocalAbstractComponent newComponent = future.get(timeout, TimeUnit.SECONDS); return newComponent; } } catch (final InterruptedException e1) { throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Interrupted waiting for service to start. " + name, e1)); } catch (final ExecutionException e1) { throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Error while waiting for service to start. " + name, e1)); } catch (final TimeoutException e1) { future.cancel(true); throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Timed out waiting for service to start. " + name, e1)); } finally { progress.done(); } } /** * Locates a device that was just launched locally. The device label name form the launch is matched against * devices in the sandbox device manager. * * @param launch The launch that just occurred * @param monitor A progress monitor * @return The newly launched service * @throws CoreException */ private static LocalAbstractComponent postLaunchDevice(final ILaunch launch, final IProgressMonitor monitor) throws CoreException { SubMonitor progress = SubMonitor.convert(monitor, "Wait for device to register", 1); final String deviceLabel = launch.getAttribute(LaunchVariables.DEVICE_LABEL); final LocalSca localSca = ScaDebugPlugin.getInstance().getLocalSca(null); final Future<LocalAbstractComponent> future = SpdLauncherUtil.EXECUTOR.submit(new Callable<LocalAbstractComponent>() { @Override public LocalAbstractComponent call() throws Exception { LocalAbstractComponent retVal = null; while (retVal == null) { // If this launch was terminated, immediately bail if (launch.isTerminated()) { throw new EarlyTerminationException("Device terminated while waiting to launch. " + deviceLabel, launch); } for (final ScaDevice< ? > device : localSca.getSandboxDeviceManager().fetchDevices(null, RefreshDepth.SELF)) { final String label = device.fetchLabel(null); if (deviceLabel.equals(label)) { retVal = (LocalAbstractComponent) device; break; } } if (retVal == null) { try { Thread.sleep(100); } catch (final InterruptedException e) { throw e; } } } return retVal; } }); try { final int timeout = launch.getLaunchConfiguration().getAttribute(ScaDebugLaunchConstants.ATT_LAUNCH_TIMEOUT, ScaDebugLaunchConstants.DEFAULT_ATT_LAUNCH_TIMEOUT); if (timeout < 0 || ILaunchManager.DEBUG_MODE.equals(launch.getLaunchMode())) { // In debug-mode wait they may have placed a break-point that is delaying registration, so wait forever return future.get(); } else { return future.get(timeout, TimeUnit.SECONDS); } } catch (final InterruptedException e1) { throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Interrupted waiting for device to start. " + deviceLabel, e1)); } catch (final ExecutionException e1) { throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Error while waiting for component to start. " + deviceLabel, e1)); } catch (final TimeoutException e1) { future.cancel(true); throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Timed out waiting for component to start. " + deviceLabel, e1)); } finally { progress.done(); } } private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\$\\{((\\w+)(:(\\w+))?)\\}"); /** * Finds and expands known variable references in the command-line args of a launch. Which variables get expanded * depends on the {@link ILauncherVariableDesc}s that are registered with the {@link ILauncherVariableRegistry}. * * @param spd The SoftPkg being launched * @param input The initial command-line arguments * @param launch The launch that is about to occur * @param configuration The launch configuration * @return The input command line with variables expanded * @throws CoreException */ public static String insertProgramArguments(final SoftPkg spd, String input, final ILaunch launch, final ILaunchConfiguration configuration) throws CoreException { if (input == null || input.trim().length() == 0) { final ComponentType type = SoftwareComponent.Util.getWellKnownComponentType(spd.getDescriptor().getComponent()); input = SpdLauncherUtil.getDefaultProgramArguments(type); } final StringBuilder builder = new StringBuilder(); final String args = input; final Matcher matcher = SpdLauncherUtil.VARIABLE_PATTERN.matcher(args); final ILauncherVariableRegistry registry = ScaDebugPlugin.getInstance().getLauncherVariableRegistry(); int previousEnd = 0; while (matcher.find()) { builder.append(args.subSequence(previousEnd, matcher.start())); final String var = matcher.group(2); final String arg = matcher.group(4); final ILauncherVariableDesc desc = registry.getDesc(var); if (desc != null) { final String value = desc.resolveValue(arg, spd, launch, configuration); if (value != null && !value.isEmpty()) { builder.append(value); } else if (SpdLauncherUtil.DEBUG_ARGS.enabled) { SpdLauncherUtil.DEBUG_ARGS.message("Failed to resolve launcher variable: " + desc.getName()); } } else { // No replacement leave as is, it will be replaced later by Eclipse dynamic variable resolver builder.append(matcher.group()); } previousEnd = matcher.end(); } builder.append(args.substring(previousEnd)); final String result = builder.toString().trim(); if (SpdLauncherUtil.DEBUG_ARGS.enabled) { SpdLauncherUtil.DEBUG_ARGS.message(result); } return result; } /** * Create the default command-line arguments to launch a particular {@link SoftPkg}. * * @param type The {@link ComponentType} of the {@link SoftPkg}. * @return */ public static String getDefaultProgramArguments(final ComponentType type) { if (type == null) { return null; } switch (type) { case DEVICE: return SpdLauncherUtil.createDefaultDeviceProgramArgs(); case EVENT_SERVICE: case SERVICE: return SpdLauncherUtil.createDefaultServiceProgramArgs(); case RESOURCE: return SpdLauncherUtil.createDefaultComponentProgramArgs(); default: String errorMsg = String.format("Unsupported component type (%s) while launching in the sandbox. It will be treated as a component.", type.getName()); ScaDebugPlugin.logWarning(errorMsg, null); return SpdLauncherUtil.createDefaultComponentProgramArgs(); } } /** * Create the default command-line arguments for running a REDHAWK component. The arguments will contain variable * references which will be expanded at launch time. * * @return The default command-line arguments */ private static String createDefaultComponentProgramArgs() { final IStringVariableManager manager = VariablesPlugin.getDefault().getStringVariableManager(); final StringBuilder retVal = new StringBuilder(); retVal.append(manager.generateVariableExpression(LaunchVariables.EXEC_PARAMS, null)); retVal.append(" "); retVal.append(LaunchVariables.COMPONENT_IDENTIFIER); retVal.append(" "); retVal.append(manager.generateVariableExpression(LaunchVariables.COMPONENT_IDENTIFIER, null)); retVal.append(" "); retVal.append(LaunchVariables.NAME_BINDING); retVal.append(" "); retVal.append(manager.generateVariableExpression(LaunchVariables.NAME_BINDING, null)); retVal.append(" "); retVal.append(LaunchVariables.PROFILE_NAME); retVal.append(" "); retVal.append(manager.generateVariableExpression(LaunchVariables.PROFILE_NAME, null)); retVal.append(" "); retVal.append(manager.generateVariableExpression(LaunchVariables.DEBUG_LEVEL, null)); retVal.append(" "); retVal.append(LaunchVariables.NAMING_CONTEXT_IOR); retVal.append(" "); retVal.append(manager.generateVariableExpression(LaunchVariables.NAMING_CONTEXT_IOR, null)); retVal.append(" "); retVal.append(manager.generateVariableExpression(LaunchVariables.RH_DEPLOYMENT_ROOT, null)); return retVal.toString(); } /** * Create the default command-line arguments for running a REDHAWK service. The arguments will contain variable * references which will be expanded at launch time. * * @return The default command-line arguments */ private static String createDefaultServiceProgramArgs() { final IStringVariableManager manager = VariablesPlugin.getDefault().getStringVariableManager(); final StringBuilder retVal = new StringBuilder(); retVal.append(manager.generateVariableExpression(LaunchVariables.EXEC_PARAMS, null)); retVal.append(" "); retVal.append(LaunchVariables.SERVICE_NAME); retVal.append(" "); retVal.append(manager.generateVariableExpression(LaunchVariables.SERVICE_NAME, null)); retVal.append(" "); retVal.append(manager.generateVariableExpression(LaunchVariables.DEBUG_LEVEL, null)); retVal.append(" "); retVal.append(LaunchVariables.DEVICE_MGR_IOR); retVal.append(" "); retVal.append(manager.generateVariableExpression(LaunchVariables.DEVICE_MGR_IOR, null)); return retVal.toString(); } /** * Create the default command-line arguments for running a REDHAWK device. The arguments will contain variable * references which will be expanded at launch time. * * @return The default command-line arguments */ private static String createDefaultDeviceProgramArgs() { final IStringVariableManager manager = VariablesPlugin.getDefault().getStringVariableManager(); final StringBuilder retVal = new StringBuilder(); retVal.append(manager.generateVariableExpression(LaunchVariables.EXEC_PARAMS, null)); retVal.append(" "); retVal.append(LaunchVariables.PROFILE_NAME); retVal.append(" "); retVal.append(manager.generateVariableExpression(LaunchVariables.PROFILE_NAME, null)); retVal.append(" "); retVal.append(LaunchVariables.DEVICE_ID); retVal.append(" "); retVal.append(manager.generateVariableExpression(LaunchVariables.DEVICE_ID, null)); retVal.append(" "); retVal.append(LaunchVariables.DEVICE_LABEL); retVal.append(" "); retVal.append(manager.generateVariableExpression(LaunchVariables.DEVICE_LABEL, null)); retVal.append(" "); retVal.append(manager.generateVariableExpression(LaunchVariables.DEBUG_LEVEL, null)); retVal.append(" "); retVal.append(LaunchVariables.DEVICE_MGR_IOR); retVal.append(" "); retVal.append(manager.generateVariableExpression(LaunchVariables.DEVICE_MGR_IOR, null)); return retVal.toString(); } public static String createExecParamString(final Map<String, Object> params) { if (params.isEmpty()) { return ""; } final StringBuilder builder = new StringBuilder(); for (final Map.Entry<String, ? > entry : params.entrySet()) { if (entry.getValue() != null) { builder.append(" "); builder.append(entry.getKey()); builder.append(" \""); builder.append(entry.getValue()); builder.append("\" "); } } return builder.toString(); } public static ComponentType getComponentType(final SoftPkg spd) { try { final ComponentType type = SoftwareComponent.Util.getWellKnownComponentType(spd.getDescriptor().getComponent()); return type; } catch (final Exception e) { // SUPPRESS CHECKSTYLE Type doesn't match any known component type so return null return null; } } /** * Loads the {@link SoftPkg} specified in a launch configuration. * * @param configuration The launch configuration * @return The {@link SoftPkg} object * @throws CoreException */ public static SoftPkg getSpd(final ILaunchConfiguration configuration) throws CoreException { final URI spdURI = ScaLaunchConfigurationUtil.getProfileURI(configuration); final ResourceSet resourceSet = ScaResourceFactoryUtil.createResourceSet(); return SoftPkg.Util.getSoftPkg(resourceSet.getResource(spdURI, true)); } /** * Ensure there aren't obvious errors with the XML or dependent XML (PRFs, SCDs) that will prevent a launch. * @since 8.3 */ public static IStatus validateAllXML(SoftPkg spd) { // Check SPD. Return immediately if there are errors. IStatus status = validateXML(spd, String.format("Errors in SPD for component %s", spd.getName())); if (status.getSeverity() >= IStatus.ERROR) { return status; } MultiStatus multiStatus = new MultiStatus(ScaDebugPlugin.ID, ERR_CODE_RELATED_XML, "Some XML file(s) have errors", null); // Check PRF if applicable if (spd.getPropertyFile() != null) { Properties prf = spd.getPropertyFile().getProperties(); String prfFilePath = spd.getPropertyFile().getLocalFile().getName(); String msg = String.format("Errors in PRF for component %s (%s)", spd.getName(), prfFilePath); status = validateXML(prf, msg); if (status.getSeverity() >= IStatus.ERROR) { multiStatus.add(status); } } // Check SCD if applicable if (spd.getDescriptor() != null) { SoftwareComponent scd = spd.getDescriptor().getComponent(); String scdFilePath = spd.getDescriptor().getLocalfile().getName(); String msg = String.format("Errors in SCD for component %s (%s)", spd.getName(), scdFilePath); status = validateXML(scd, msg); if (status.getSeverity() >= IStatus.ERROR) { multiStatus.add(status); } } if (multiStatus.getSeverity() >= IStatus.ERROR) { return multiStatus; } else { return Status.OK_STATUS; } } /** * Check for EMF validation <b>errors</b> on an {@link EObject} and its children within a * {@link org.eclipse.emf.ecore.resource.Resource Resource}. * @param eObject * @param errorMsg The error message to use in the returned status * @return */ private static IStatus validateXML(EObject eObject, String errorMsg) { BasicDiagnostic diagnostic = new BasicDiagnostic(ScaDebugPlugin.ID, 0, errorMsg, null) { @Override public void add(Diagnostic diagnostic) { if (diagnostic != null && diagnostic.getSeverity() < IStatus.ERROR) { return; } super.add(diagnostic); } }; Diagnostician.INSTANCE.validate(eObject, diagnostic); return BasicDiagnostic.toIStatus(diagnostic); } /** * @since 8.0 */ public static void terminate(final LocalLaunch localLaunch) { ILaunch launch = localLaunch.getLaunch(); Job job = new TerminateJob(launch, launch.getLaunchConfiguration().getName()); job.setUser(true); job.schedule(); } }