/******************************************************************************* * 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.internal.cf.impl; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.impl.EObjectImpl; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.omg.CORBA.BAD_OPERATION; import org.omg.CORBA.SystemException; import org.omg.CORBA.TCKind; import CF.DataType; import CF.Device; import CF.DeviceManagerOperations; import CF.ErrorNumberType; import CF.FileSystem; import CF.InvalidIdentifier; import CF.InvalidObjectReference; import CF.LifeCycle; import CF.LifeCycleHelper; import CF.PropertiesHolder; import CF.Resource; import CF.UnknownProperties; import CF.DeviceManagerPackage.ServiceType; import CF.ExecutableDevicePackage.ExecuteFail; import CF.LifeCyclePackage.InitializeError; import CF.LifeCyclePackage.ReleaseError; import CF.PortSetPackage.PortInfoType; import CF.PortSupplierPackage.UnknownPort; import CF.PropertyEmitterPackage.AlreadyInitialized; import CF.PropertySetPackage.InvalidConfiguration; import CF.PropertySetPackage.PartialConfiguration; import gov.redhawk.ide.debug.ConsoleColor; import gov.redhawk.ide.debug.ILaunchConfigurationFactory; import gov.redhawk.ide.debug.LocalAbstractComponent; import gov.redhawk.ide.debug.LocalScaDeviceManager; import gov.redhawk.ide.debug.ScaDebugPlugin; import gov.redhawk.ide.debug.internal.LaunchLogger; import gov.redhawk.ide.debug.variables.LaunchVariables; import gov.redhawk.model.sca.RefreshDepth; 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.ScaService; import gov.redhawk.sca.efs.WrappedFileStore; import gov.redhawk.sca.launch.ScaLaunchConfigurationUtil; import gov.redhawk.sca.util.PluginUtil; 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.DceUuidUtil; import mil.jpeojtrs.sca.util.ScaResourceFactoryUtil; /** * This is the implementation of the sandbox's device manager. It is represented in the REDHAWK model by a {@link LocalScaDeviceManager}. */ public class DeviceManagerImpl extends EObjectImpl implements DeviceManagerOperations { private final String profile; private final String identifier; private final String name; private final FileSystem fileSystem; /** * The list of devices that have registered with the device manager. */ private List<Device> devices = Collections.synchronizedList(new ArrayList<Device>()); /** * The list of service that have registered with the device manager. */ private List<ServiceType> services = Collections.synchronizedList(new ArrayList<ServiceType>()); /** * Holds the initial properties to use for <code>initializeProperties</code> and <code>configure</code>. Maps * a device ID to a model object with the properties values and profile information. */ private Map<String, ScaComponent> initialProperties = Collections.synchronizedMap(new HashMap<String, ScaComponent>()); private final Job refreshJob; private LocalScaDeviceManager devMgrModelObj; public DeviceManagerImpl(final String profile, final String identifier, final String name, final LocalScaDeviceManager devMgrModelObj, final FileSystem fileSystem) { super(); this.profile = profile; this.identifier = identifier; this.name = name; this.fileSystem = fileSystem; this.devMgrModelObj = devMgrModelObj; refreshJob = new Job("Refreshing Device Manager") { @Override protected IStatus run(IProgressMonitor monitor) { try { devMgrModelObj.refresh(monitor, RefreshDepth.FULL); } catch (InterruptedException e) { return Status.CANCEL_STATUS; } return Status.OK_STATUS; } }; } @Override public void initializeProperties(final DataType[] configProperties) throws AlreadyInitialized, InvalidConfiguration, PartialConfiguration { throw new InvalidConfiguration(); } @Override public void configure(final DataType[] configProperties) throws InvalidConfiguration, PartialConfiguration { throw new InvalidConfiguration(); } @Override public void query(final PropertiesHolder configProperties) throws UnknownProperties { // Do nothing } @Override public String registerPropertyListener(org.omg.CORBA.Object obj, String[] propIds, float interval) throws UnknownProperties, InvalidObjectReference { throw new IllegalStateException("Not implemented"); } @Override public void unregisterPropertyListener(String id) throws InvalidIdentifier { throw new IllegalStateException("Not implemented"); } @Override public org.omg.CORBA.Object getPort(final String name) throws UnknownPort { throw new UnknownPort("No ports"); } public PortInfoType[] getPortSet() { return new PortInfoType[0]; } @Override public String deviceConfigurationProfile() { return this.profile; } @Override public FileSystem fileSys() { return this.fileSystem; } @Override public String identifier() { return this.identifier; } @Override public String label() { return this.name; } @Override public CF.DomainManager domMgr() { // TODO return null; } @Override public Device[] registeredDevices() { synchronized (devices) { boolean changed = false; for (Iterator<Device> iterator = devices.iterator(); iterator.hasNext();) { Device d = iterator.next(); try { if (d._non_existent()) { iterator.remove(); changed = true; } } catch (SystemException e) { iterator.remove(); changed = true; } } if (changed) { refreshJob.schedule(); } return devices.toArray(new Device[devices.size()]); } } @Override public ServiceType[] registeredServices() { synchronized (services) { boolean changed = false; for (Iterator<ServiceType> iterator = services.iterator(); iterator.hasNext();) { ServiceType type = iterator.next(); try { if (type.serviceObject._non_existent()) { iterator.remove(); changed = true; } } catch (SystemException e) { iterator.remove(); changed = true; } } if (changed) { refreshJob.schedule(); } return services.toArray(new ServiceType[services.size()]); } } @Override public void registerDevice(final Device registeringDevice) throws InvalidObjectReference { if (registeringDevice == null) { throw new InvalidObjectReference("Cannot register a null device reference"); } // Collect some info from the device via CORBA calls String deviceId = registeringDevice.identifier(); String deviceLabel = registeringDevice.label(); // Iterate all launches until we find this device's ILaunch, then load its properties ILaunch launch = null; ScaDevice<Device> propHolder = null; for (ILaunch candidateLaunch : DebugPlugin.getDefault().getLaunchManager().getLaunches()) { // Find the launch by matching device id and/or label String launchDevId = candidateLaunch.getAttribute(LaunchVariables.DEVICE_ID); String launchDevLabel = candidateLaunch.getAttribute(LaunchVariables.DEVICE_LABEL); if (launchDevId != null && !launchDevId.equals(deviceId)) { continue; } if (launchDevLabel != null && !launchDevLabel.equals(deviceLabel)) { continue; } launch = candidateLaunch; ILaunchConfiguration launchConfig = candidateLaunch.getLaunchConfiguration(); // Load the properties from the PRF and override with values from the launch configuration try { propHolder = ScaFactory.eINSTANCE.createScaDevice(); propHolder.setProfileURI(ScaLaunchConfigurationUtil.getProfileURI(launchConfig)); propHolder.fetchProfileObject(new NullProgressMonitor()); propHolder.fetchProperties(new NullProgressMonitor()); ScaLaunchConfigurationUtil.loadProperties(launchConfig, propHolder); } catch (CoreException e) { propHolder = null; ScaDebugPlugin.logError("Unable to retrieve properties for a device launch", e); } break; } // If we have properties available if (propHolder != null) { // 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()]); registeringDevice.initializeProperties(initializePropsArray); } catch (AlreadyInitialized e) { LaunchLogger.INSTANCE.writeToConsole(launch, CFErrorFormatter.format(e, "device " + deviceLabel), ConsoleColor.STDERR); } catch (InvalidConfiguration e) { LaunchLogger.INSTANCE.writeToConsole(launch, CFErrorFormatter.format(e, "device " + deviceLabel), ConsoleColor.STDERR); } catch (PartialConfiguration e) { LaunchLogger.INSTANCE.writeToConsole(launch, CFErrorFormatter.format(e, "device " + deviceLabel), ConsoleColor.STDERR); } catch (BAD_OPERATION e) { String msg; if (initializeProps.size() == 0) { msg = String.format("Could not call initializeProperties on device %s in the sandbox (CORBA BAD_OPERATION). " + "If the installed version of REDHAWK is pre-2.0, this is expected and can be ignored.", deviceLabel); } else { msg = "Device 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); } } // Initialize try { registeringDevice.initialize(); } catch (final InitializeError e) { LaunchLogger.INSTANCE.writeToConsole(launch, CFErrorFormatter.format(e, "device " + deviceLabel), ConsoleColor.STDERR); } // If we have properties available if (propHolder != null) { // Configure - 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())); } } String description = String.format("device %s", launch.getAttribute(LaunchVariables.DEVICE_LABEL)); try { registeringDevice.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); } } // Register the device and refresh the model so it notices it devices.add(registeringDevice); refreshJob.schedule(); } @Override public void unregisterDevice(final Device registeredDevice) throws InvalidObjectReference { if (registeredDevice == null) { throw new InvalidObjectReference("Null reference", "Null reference"); } final String deviceId = registeredDevice.identifier(); if (deviceId == null) { return; } synchronized (devices) { for (Iterator<Device> iterator = devices.iterator(); iterator.hasNext();) { Device d = iterator.next(); if (d == registeredDevice || PluginUtil.equals(d.identifier(), deviceId)) { iterator.remove(); return; } } } refreshJob.schedule(); try { registeredDevice.releaseObject(); } catch (ReleaseError e) { throw new InvalidObjectReference("Release error", Arrays.toString(e.errorMessages)); } } @Override public void shutdown() { for (ServiceType type : this.services.toArray(new ServiceType[services.size()])) { try { unregisterService(type.serviceObject, type.serviceName); } catch (InvalidObjectReference e) { ScaDebugPlugin.logError("Failed to release service " + type.serviceName, e); } } for (Device d : this.devices.toArray(new Device[devices.size()])) { try { unregisterDevice(d); } catch (InvalidObjectReference e) { ScaDebugPlugin.logError("Failed to release device", e); } } } @Override public void registerService(final org.omg.CORBA.Object registeringService, final String name) throws InvalidObjectReference { ServiceType type = new ServiceType(registeringService, name); services.add(type); refreshJob.schedule(); if (registeringService._is_a(LifeCycleHelper.id())) { LifeCycle service = LifeCycleHelper.narrow(registeringService); try { service.initialize(); } catch (InitializeError e) { throw new InvalidObjectReference("Initialize error", Arrays.toString(e.errorMessages)); } } } @Override public void unregisterService(final org.omg.CORBA.Object unregisteringService, final String name) throws InvalidObjectReference { if (name == null) { return; } synchronized (services) { for (Iterator<ServiceType> iterator = services.iterator(); iterator.hasNext();) { ServiceType type = iterator.next(); if (PluginUtil.equals(type.serviceName, name)) { iterator.remove(); return; } } } refreshJob.schedule(); if (unregisteringService._is_a(LifeCycleHelper.id())) { LifeCycle service = LifeCycleHelper.narrow(unregisteringService); try { service.releaseObject(); } catch (ReleaseError e) { throw new InvalidObjectReference("Release error", Arrays.toString(e.errorMessages)); } } } @Override public String getComponentImplementationId(final String componentInstantiationId) { // TODO return ""; } public LocalAbstractComponent launch(final String usageName, String compId, final DataType[] initConfiguration, @NonNull final URI spdURI, final String implId, final String mode) throws CoreException { // Create launch config ILaunchConfigurationWorkingCopy config = createLaunchConfig(usageName, compId, initConfiguration, spdURI, implId, mode); // Launch final ILaunch launch = config.launch(mode, new NullProgressMonitor(), false); // Find the device/service and return it return postLaunch(launch); } /** * @deprecated Use {@link #launch(String, String, DataType[], URI, String, String)} */ @Deprecated public Resource launch(final String compId, final DataType[] initConfiguration, @NonNull final String spdURI, final String implId, final String mode) throws ExecuteFail { Assert.isNotNull(spdURI, "SPD URI must not be null"); try { URI uri = URI.createURI(spdURI); if (uri == null) { throw new NullPointerException(); } LocalAbstractComponent abstractComponent = launch(null, compId, initConfiguration, uri, implId, mode); // Get the device CORBA object and return if (abstractComponent instanceof ScaDevice) { return ((ScaDevice<?>) abstractComponent).fetchNarrowedObject(new NullProgressMonitor()); } else { abstractComponent.getLaunch().terminate(); throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Launched softpkg is not a device and cannot be narrowed to CF.Resource", null)); } } catch (final CoreException e) { ScaDebugPlugin.getInstance().getLog().log(new Status(e.getStatus().getSeverity(), ScaDebugPlugin.ID, "Failed to launch device.", e)); throw new ExecuteFail(ErrorNumberType.CF_EFAULT, e.getStatus().getMessage()); } } /** * @deprecated Use {@link #launch(String, String, DataType[], URI, String, String)} */ @NonNull @Deprecated public Resource launch(@Nullable String usageName, @Nullable final String compId, @Nullable final String execParams, @NonNull URI spdURI, @Nullable final String implId, @Nullable String mode) throws CoreException { // Create launch config ILaunchConfigurationWorkingCopy config = createLaunchConfig(usageName, compId, null, spdURI, implId, mode); if (execParams != null && execParams.length() > 0) { config.setAttribute(LaunchVariables.EXEC_PARAMS, execParams); } // Launch final ILaunch launch = config.launch(mode, new NullProgressMonitor(), false); // Find the device and return LocalAbstractComponent abstractComponent = postLaunch(launch); if (abstractComponent instanceof ScaDevice) { return ((ScaDevice<?>) abstractComponent).fetchNarrowedObject(new NullProgressMonitor()); } else { launch.terminate(); throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Launched softpkg is not a device and cannot be narrowed to CF.Resource", null)); } } private ILaunchConfigurationWorkingCopy createLaunchConfig(String usageName, String compId, DataType[] initConfiguration, URI spdURI, String implId, String mode) throws CoreException { Assert.isNotNull(spdURI, "SPD URI must not be null"); // Use EFS to unwrap non-platform URIs; for example convert sca URI -> file URI if (!spdURI.isPlatform()) { IFileStore store = EFS.getStore(java.net.URI.create(spdURI.toString())); IFileStore unwrappedStore = WrappedFileStore.unwrap(store); if (unwrappedStore != null) { URI tmp = URI.createURI(unwrappedStore.toURI().toString()); if (tmp == null) { throw new NullPointerException(); } spdURI = tmp; } } // Load SPD final ResourceSet resourceSet = ScaResourceFactoryUtil.createResourceSet(); final SoftPkg spd = SoftPkg.Util.getSoftPkg(resourceSet.getResource(spdURI, true)); if (mode == null) { mode = ILaunchManager.RUN_MODE; } // Create a launch config final ILaunchConfigurationFactory factory = ScaDebugPlugin.getInstance().getLaunchConfigurationFactoryRegistry().getFactory(spd, implId); if (factory == null) { throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Failed to obtain Launch Factory for impl: " + implId)); } final ILaunchConfigurationWorkingCopy config = factory.createLaunchConfiguration(spd.getName(), implId, spd); // Determine usage name if not specified if (usageName == null && compId != null) { if (DceUuidUtil.isValid(compId)) { usageName = compId; } else { int index = compId.indexOf(':'); if (index != -1 && compId.length() > (index + 1)) { usageName = compId.substring(index + 1); } else { usageName = compId; } } } // Set appropriate identifiers (name, label, id, etc) ComponentType type = SoftwareComponent.Util.getWellKnownComponentType(spd.getDescriptor().getComponent()); switch (type) { case SERVICE: if (usageName != null) { config.setAttribute(LaunchVariables.SERVICE_NAME, usageName); } break; case DEVICE: if (usageName != null) { config.setAttribute(LaunchVariables.DEVICE_LABEL, usageName); } if (compId != null) { config.setAttribute(LaunchVariables.DEVICE_ID, compId); // Initial properties if (initConfiguration != null) { final ScaComponent propHolder = ScaFactory.eINSTANCE.createScaComponent(); propHolder.setProfileURI(spdURI); propHolder.fetchProfileObject(new NullProgressMonitor()); propHolder.fetchProperties(new NullProgressMonitor()); for (DataType dt : initConfiguration) { ScaAbstractProperty< ? > prop = propHolder.getProperty(dt.id); if (prop != null) { prop.fromAny(dt.value); } } ScaLaunchConfigurationUtil.saveProperties(config, propHolder); initialProperties.put(compId, propHolder); } } break; default: throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Cannot create a launch configuration for " + spd.getName())); } return config; } private LocalAbstractComponent postLaunch(ILaunch launch) throws CoreException { ILaunchConfiguration config = launch.getLaunchConfiguration(); ComponentType type; String matchName; if (config.hasAttribute(LaunchVariables.SERVICE_NAME)) { type = ComponentType.SERVICE; matchName = config.getAttribute(LaunchVariables.SERVICE_NAME, ""); } else if (config.hasAttribute(LaunchVariables.DEVICE_LABEL)) { type = ComponentType.DEVICE; matchName = config.getAttribute(LaunchVariables.DEVICE_ID, ""); } else { throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Cannot determine type of launched resource")); } for (int tries = 0; tries < 100; tries++) { switch (type) { case DEVICE: for (final ScaDevice< ? > device : devMgrModelObj.fetchDevices(new NullProgressMonitor(), RefreshDepth.SELF)) { device.fetchAttributes(null); if (matchName.equals(device.getIdentifier())) { return (LocalAbstractComponent) device; } } break; case SERVICE: for (final ScaService service : devMgrModelObj.fetchServices(new NullProgressMonitor(), RefreshDepth.SELF)) { service.fetchAttributes(null); if (matchName.equals(service.getName())) { return (LocalAbstractComponent) service; } } break; default: } try { Thread.sleep(500); } catch (InterruptedException e) { // PASS } } launch.terminate(); throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Failed to find device/serivce after launch", null)); } }