/*******************************************************************************
* 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;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.ArrayUtils;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.transaction.util.TransactionUtil;
import org.eclipse.jdt.annotation.Nullable;
import org.jacorb.naming.Name;
import org.omg.CORBA.Any;
import org.omg.CORBA.SystemException;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.CosNaming.NamingContextPackage.AlreadyBound;
import org.omg.CosNaming.NamingContextPackage.CannotProceed;
import org.omg.CosNaming.NamingContextPackage.InvalidName;
import org.omg.CosNaming.NamingContextPackage.NotFound;
import CF.DataType;
import CF.PortPackage.InvalidPort;
import CF.PortPackage.OccupiedPort;
import gov.redhawk.ide.debug.LocalSca;
import gov.redhawk.ide.debug.LocalScaComponent;
import gov.redhawk.ide.debug.LocalScaWaveform;
import gov.redhawk.ide.debug.NotifyingNamingContext;
import gov.redhawk.ide.debug.ScaDebugFactory;
import gov.redhawk.ide.debug.ScaDebugPlugin;
import gov.redhawk.ide.debug.internal.cf.extended.impl.ApplicationImpl;
import gov.redhawk.model.sca.RefreshDepth;
import gov.redhawk.model.sca.ScaComponent;
import gov.redhawk.model.sca.ScaPackage;
import gov.redhawk.model.sca.ScaPort;
import gov.redhawk.model.sca.ScaUsesPort;
import gov.redhawk.model.sca.commands.ScaModelCommand;
import gov.redhawk.sca.util.SubMonitor;
import mil.jpeojtrs.sca.partitioning.PartitioningPackage;
import mil.jpeojtrs.sca.sad.SadComponentInstantiation;
import mil.jpeojtrs.sca.sad.SadConnectInterface;
import mil.jpeojtrs.sca.sad.SoftwareAssembly;
import mil.jpeojtrs.sca.spd.SoftPkg;
import mil.jpeojtrs.sca.util.AnyUtils;
import mil.jpeojtrs.sca.util.CFErrorFormatter;
import mil.jpeojtrs.sca.util.DceUuidUtil;
import mil.jpeojtrs.sca.util.QueryParser;
import mil.jpeojtrs.sca.util.ScaEcoreUtils;
import mil.jpeojtrs.sca.util.ScaFileSystemConstants;
public class LocalApplicationFactory {
private final Map<String, String> implMap;
private final LocalSca localSca;
private final String mode;
private final ILaunch launch;
private final Map<String, List<DataType>> componentPropertyMap;
private final NotifyingNamingContext namingContext;
private static final EStructuralFeature[] SPD_PATH = new EStructuralFeature[] { PartitioningPackage.Literals.COMPONENT_INSTANTIATION__PLACEMENT,
PartitioningPackage.Literals.COMPONENT_PLACEMENT__COMPONENT_FILE_REF, PartitioningPackage.Literals.COMPONENT_FILE_REF__FILE,
PartitioningPackage.Literals.COMPONENT_FILE__SOFT_PKG };
public LocalApplicationFactory(final Map<String, String> implMap, final LocalSca localSca, final String mode, final ILaunch launch,
final Map<String, List<DataType>> componentPropertyMap) {
this.implMap = implMap;
this.localSca = localSca;
this.mode = mode;
this.launch = launch;
this.namingContext = this.localSca.getRootContext();
this.componentPropertyMap = componentPropertyMap;
}
public static NotifyingNamingContext createWaveformContext(NotifyingNamingContext parent, String name) throws CoreException {
// We need to escape all '.' characters in namespaced waveforms as they have specific meaning in the CORBA
// naming service
String adjustedName = name.replaceAll("\\.", "\\\\.");
// The framework always adds "_n" to the end of the requested name, where n is one of {1, 2, 3, ...}.
NamingContextExt retVal = null;
for (int i = 1; retVal == null; i++) {
try {
retVal = NamingContextExtHelper.narrow(parent.bind_new_context(Name.toName(adjustedName + "_" + i)));
} catch (AlreadyBound e) {
// PASS - we'll try another name
} catch (NotFound | CannotProceed | InvalidName e) {
throw new CoreException(
new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Failed to create application: " + adjustedName + " " + e.getMessage(), e));
}
}
return parent.findContext(retVal);
}
// Need to escape all '.' characters in namespaced waveforms
public static void bindApp(final ApplicationImpl app) throws CoreException {
app.getStreams().getOutStream().println("Binding application...");
try {
NamingContextExt context = app.getWaveformContext();
NameComponent[] name = Name.toName(app.name().replaceAll("\\.", "\\\\."));
org.omg.CORBA.Object obj = app.getLocalWaveform().getCorbaObj();
context.bind(name, obj);
app.getStreams().getOutStream().println("Done Binding application.");
} catch (final NotFound | CannotProceed | InvalidName | AlreadyBound | SystemException e) {
throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Failed to bind application to context " + e.getMessage(), e));
}
}
/**
* Carries out all the steps necessary to launch a waveform in the sandbox, including:
* <ul>
* <li>Launching components</li>
* <li>Configuring components</li>
* <li>Making waveform connections</li>
* </ul>
* @param sad
* @param name
* @param monitor
* @return
* @throws CoreException
*/
public LocalScaWaveform create(final SoftwareAssembly sad, String name, final IProgressMonitor monitor) throws CoreException {
final SubMonitor progress = SubMonitor.convert(monitor, 100);
// Try and narrow to the given name. If an already bound exception occurs, append _ + i to the end and try again
// until we've found a good name.
ApplicationImpl app = null;
LocalScaWaveform waveform = null;
try {
progress.subTask("Create application");
final String appId = DceUuidUtil.createDceUUID();
final String profile = sad.eResource().getURI().path();
waveform = ScaDebugFactory.eINSTANCE.createLocalScaWaveform();
waveform.setProfile(profile);
waveform.setLaunch(this.launch);
waveform.setMode(this.launch.getLaunchMode());
waveform.setNamingContext(LocalApplicationFactory.createWaveformContext(namingContext, name));
app = new ApplicationImpl(waveform, appId, name);
app.setLaunching(true);
this.launch.addProcess(app);
waveform.setLocalApp(app);
progress.subTask("Bind application");
LocalApplicationFactory.bindApp(app);
progress.worked(1);
URI uri = sad.eResource().getURI();
if (uri.isFile()) {
waveform.setProfileURI(uri);
} else {
Map<String, String> query = new HashMap<String, String>(QueryParser.parseQuery(uri.query()));
query.put(ScaFileSystemConstants.QUERY_PARAM_WF, waveform.getIor());
String queryStr = QueryParser.createQuery(query);
URI sadUri = URI.createHierarchicalURI(uri.scheme(), uri.authority(), uri.device(), uri.segments(), queryStr, uri.fragment());
waveform.setProfileURI(sadUri);
}
waveform.fetchProfileObject(null);
final LocalScaWaveform tmpWaveform = waveform;
ScaModelCommand.execute(this.localSca, new ScaModelCommand() {
@Override
public void execute() {
LocalApplicationFactory.this.localSca.getWaveforms().add(tmpWaveform);
}
});
progress.worked(1);
progress.subTask("Launch components");
launchComponents(progress.newChild(90), app, sad);
progress.subTask("Create connections");
createConnections(app, sad);
progress.worked(3);
} catch (final SystemException e) {
throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, "Failed to create application: " + name + " " + e.getMessage(), e));
} finally {
if (app != null) {
app.setLaunching(false);
}
}
if (app != null && waveform != null) {
progress.subTask("Refresh");
SubMonitor subTask = progress.newChild(1);
try {
waveform.refresh(subTask, RefreshDepth.FULL);
} catch (InterruptedException e) {
// PASS
}
app.getStreams().getOutStream().println("Done");
}
progress.done();
return waveform;
}
/**
* Launches each component of the waveform
*
* @param monitor
* @param app
* @param sad
* @param config
* @throws CoreException
*/
protected void launchComponents(IProgressMonitor monitor, final ApplicationImpl app, final SoftwareAssembly sad) throws CoreException {
final List<SadComponentInstantiation> instantiations = sad.getAllComponentInstantiations();
final SubMonitor progress = SubMonitor.convert(monitor, instantiations.size());
app.getStreams().getOutStream().println("Launching components...");
for (final SadComponentInstantiation comp : instantiations) {
progress.subTask(String.format("Launch component instance '%s'", comp.getUsageName()));
URI spdUri = getSpdURI(comp);
if (spdUri != null) {
List<DataType> componentProps = this.componentPropertyMap.get(comp.getId());
if (componentProps == null) {
componentProps = new ArrayList<DataType>();
}
LocalScaComponent localComp = app.launch(comp.getUsageName(), comp.getId(), componentProps.toArray(new DataType[0]), spdUri, getImplId(comp),
this.mode);
if (localComp != null) {
TransactionalEditingDomain localEditingDomain = TransactionUtil.getEditingDomain(localComp);
if (localEditingDomain != null) {
localEditingDomain.getCommandStack().execute(
SetCommand.create(localEditingDomain, localComp, ScaPackage.Literals.SCA_COMPONENT__COMPONENT_INSTANTIATION, comp));
}
}
} else {
String errorMsg = String.format("Failed to find SPD for component: %s", comp.getUsageName());
throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, errorMsg));
}
app.getStreams().getOutStream().println("\n");
progress.worked(1);
}
}
private void createConnections(final ApplicationImpl app, final SoftwareAssembly sad) throws CoreException {
if (sad.getConnections() != null) {
app.getStreams().getOutStream().println("Making connections...");
for (final SadConnectInterface connection : sad.getConnections().getConnectInterface()) {
app.getStreams().getOutStream().println("Creating connection " + connection.getId());
org.omg.CORBA.Object target = null;
if (connection.getProvidesPort() != null) {
final String providesId = connection.getProvidesPort().getProvidesIdentifier();
if (connection.getProvidesPort().getComponentInstantiationRef() != null) {
final String componentRefId = connection.getProvidesPort().getComponentInstantiationRef().getRefid();
final ScaComponent componentForPort = app.getLocalWaveform().getScaComponent(componentRefId);
if (componentForPort == null) {
String errorMsg = String.format("Couldn't find component instance '%s' to make waveform connection '%s'", componentRefId,
connection.getId());
throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, errorMsg));
}
if (componentForPort.getScaPort(providesId) == null) {
app.getStreams().getErrStream().println(
componentForPort.getInstantiationIdentifier() + " does not contain a port of name: " + providesId);
} else {
target = componentForPort.getScaPort(providesId).getCorbaObj();
}
}
} else if (connection.getComponentSupportedInterface() != null
&& connection.getComponentSupportedInterface().getComponentInstantiationRef() != null) {
final String componentRefId = connection.getComponentSupportedInterface().getComponentInstantiationRef().getRefid();
final ScaComponent component = app.getLocalWaveform().getScaComponent(componentRefId);
if (component == null) {
String errorMsg = String.format("Couldn't find component instance '%s' to make waveform connection '%s'", componentRefId,
connection.getId());
throw new CoreException(new Status(IStatus.ERROR, ScaDebugPlugin.ID, errorMsg));
}
target = component.getCorbaObj();
} else {
app.getStreams().getErrStream().println("Unsupported target connection type for connection: " + connection.getId());
}
if (target != null) {
final String usesID = connection.getUsesPort().getUsesIdentifier();
if (connection.getUsesPort().getComponentInstantiationRef() == null) {
app.getStreams().getErrStream().println("Failed to create connection " + connection.getId());
continue;
}
final ScaComponent component = app.getLocalWaveform().getScaComponent(connection.getUsesPort().getComponentInstantiationRef().getRefid());
final ScaPort< ? , ? > port = component.getScaPort(usesID);
if (port == null) {
app.getStreams().getErrStream().println(component.getInstantiationIdentifier() + " does not contain a port of name: " + usesID);
} else if (port instanceof ScaUsesPort) {
final ScaUsesPort usesPort = (ScaUsesPort) port;
try {
usesPort.connectPort(target, connection.getId());
} catch (final InvalidPort e) {
app.getStreams().getErrStream().println(CFErrorFormatter.format(e, "connection " + connection.getId()));
} catch (final OccupiedPort e) {
app.getStreams().getErrStream().println(CFErrorFormatter.format(e, "connection " + connection.getId()));
}
}
}
}
app.getStreams().getOutStream().println("Done making connections");
}
}
private String getImplId(final SadComponentInstantiation comp) {
String retVal = null;
if (this.implMap != null) {
retVal = this.implMap.get(comp.getId());
}
if (retVal == null) {
retVal = comp.getPlacement().getComponentFileRef().getFile().getSoftPkg().getImplementation().get(0).getId();
}
return retVal;
}
@Nullable
private URI getSpdURI(@Nullable final SadComponentInstantiation comp) {
final SoftPkg spd = ScaEcoreUtils.getFeature(comp, LocalApplicationFactory.SPD_PATH);
if (spd != null && spd.eResource() != null) {
return spd.eResource().getURI();
} else {
return null;
}
}
public static String toString(DataType t) {
Object value = AnyUtils.convertAny(t.value);
if (value instanceof DataType[]) {
StringBuilder builder = new StringBuilder();
builder.append("\t" + t.id + " = {");
for (DataType child : (DataType[]) value) {
builder.append("\n\t\t" + LocalApplicationFactory.toString(child));
}
builder.append("\n\t}");
return builder.toString();
} else if (value instanceof DataType) {
return "\t" + t.id + " = {" + LocalApplicationFactory.toString((DataType) value) + "}";
} else if (value instanceof Any[]) {
StringBuilder builder = new StringBuilder();
builder.append("\t" + t.id + " = [] {");
int i = 0;
for (Any child : (Any[]) value) {
builder.append("\n\t\t[" + i++ + "] = ");
Object childValue = AnyUtils.convertAny(child);
String valueStr;
if (childValue instanceof DataType) {
valueStr = LocalApplicationFactory.toString((DataType) childValue);
} else if (childValue instanceof DataType[]) {
StringBuilder valueStrBuilder = new StringBuilder();
valueStrBuilder.append("{");
for (DataType childType : (DataType[]) childValue) {
valueStrBuilder.append("\n\t\t" + LocalApplicationFactory.toString(childType));
}
valueStrBuilder.append("\n\t\t}");
valueStr = valueStrBuilder.toString();
} else {
valueStr = String.valueOf(childValue);
}
builder.append(valueStr);
}
builder.append("\n\t}");
return builder.toString();
} else if (value != null && value.getClass().isArray()) {
return "\t" + t.id + " = " + ArrayUtils.toString(value);
} else {
return "\t" + t.id + " = " + value;
}
}
}