Index: fedd/compose.py
===================================================================
--- fedd/compose.py	(revision c7a982bfe326b9a262838d58b2b2b8a88c955331)
+++ fedd/compose.py	(revision 5f6c3afc72d2405ddde8d347f99c23c616705beb)
@@ -5,7 +5,9 @@
 import os
 import random
+import copy
 
 from optparse import OptionParser, OptionValueError
 from federation.remote_service import service_caller
+from federation.service_error import service_error
 from federation import topdl
 
@@ -22,28 +24,4 @@
 	return "%s:%s:%s:%s" % (self.name, self.required,
 		",".join(self.provides), ",".join(self.accepts))
-
-
-def add_to_map(exp, mark, provides, accepts):
-    """
-    Create a constraint from a string of the form
-	name:required:provides:accepts
-    where name is the name of the node in the current topology, if the second
-    field is "required" this is a required constraint, a list of attributes
-    that this connection point provides and a list that it accepts.  The
-    provides and accepts lists are comma-separated.  Note that the string
-    passed in as exp should have whitespace removed. The constraint is added to
-    the mark dict under the name key and entries in the provides and accepts
-    are made to teh constraing for each attribute that it provides or accepts.
-    """
-    nn, r, p, a = exp.split(":")
-    p = p.split(",")
-    a = a.split(",")
-    c = constraint(name=nn, required=(r == 'required'), provides=p, accepts=a)
-    mark[nn] = c
-
-    for attr, dict in ((p, provides), (a, accepts)):
-	for a in attr:
-	    if a not in dict: dict[a] = [c]
-	    else: dict[a].append(c)
 
 def make_new_name(names, prefix="name"):
@@ -169,8 +147,5 @@
     req = { 'description' : { 'ns2description': desc }, }
 
-    try:
-	r = service_caller('Ns2Topdl')(uri, req, cert)
-    except:
-	return None
+    r = service_caller('Ns2Topdl')(uri, req, cert)
 
     if r.has_key('Ns2TopdlResponseBody'):
@@ -231,5 +206,5 @@
     top.incorporate_elements()
 
-def import_tcl_constraints(marks, provides, accepts, contents):
+def import_ns2_constraints(contents):
     """
     Contents is a list containing the lines of an annotated ns2 file.  This
@@ -237,11 +212,57 @@
     constraints in the namespace of the tcl experiment, as well as inserting
     them in the accepts and provides indices.
-    """
+
+    Constraints are given in lines of the form
+	name:required:provides:accepts
+    where name is the name of the node in the current topology, if the second
+    field is "required" this is a required constraint, a list of attributes
+    that this connection point provides and a list that it accepts.  The
+    provides and accepts lists are comma-separated.  The constraint is added to
+    the marks dict under the name key and that dict is returned.
+    """
+
+    marks = { }
     for l in contents:
 	m = const_re.search(l)
 	if m:
-	    add_to_map(re.sub('\s', '', m.group(1)), marks, provides, 
-		    accepts)
-
+	    exp = re.sub('\s', '', m.group(1))
+	    nn, r, p, a = exp.split(":")
+	    p = p.split(",")
+	    a = a.split(",")
+	    c = constraint(name=nn, required=(r == 'required'),
+		    provides=p, accepts=a)
+	    marks[nn] = c
+    return marks
+
+def import_ns2_component(fn):
+    """
+    Pull a composition component in from an ns2 description.  The Constraints
+    are parsed from the comments using import_ns2_constraints and the topology
+    is created using a call to a fedd exporting the Ns2Topdl function.  A
+    topdl.Topology object rempresenting the component's topology and a dict
+    mapping constraints from the components names to the conttraints is
+    returned.  If either the file read or the conversion fails, appropriate
+    Exceptions are thrown.
+    """
+    f = open(fn, "r")
+    contents = [ l for l in f ]
+
+    marks = import_ns2_constraints(contents)
+    top = remote_ns2topdl(opts.url, "".join(contents), cert)
+    if not top:
+	raise RuntimeError("Cannot create topology from: %s" % fn)
+
+    return (top, marks)
+
+def index_constraints(constraints, provides, accepts):
+    """
+    Add constraints to the provides and accepts indices based on what the
+    attributes of the contstraints.
+    """
+    for c in constraints:
+	for attr, dict in ((c.provides, provides), (c.accepts, accepts)):
+	    for a in attr:
+		if a not in dict: dict[a] = [c]
+		else: dict[a].append(c)
 
 def multi_callback(option, opt_str, value, parser):
@@ -299,23 +320,21 @@
 accepts = { }
 for fn, cnt in files:
-    marks = { }
     try:
-	f = open(fn, "r")
-	contents = [ l for l in f ]
+	top, marks = import_ns2_component(fn)
+    except service_error, e:
+	print >>sys.stderr, "Remote error on %s: %s" % (fn, e)
+	continue
     except EnvironmentError, e:
 	print >>sys.stderr, "Error on %s: %s" % (fn, e)
 	continue
 
-    top = remote_ns2topdl(opts.url, "".join(contents), cert)
-    if not top:
-	sys.exit("Cannot create topology from: %s" % fn)
-
     for i in range(0, cnt):
 	t = top.clone()
-	import_tcl_constraints(marks, provides, accepts, contents)
-	add_interfaces(t, marks)
-	localize_names(t, names, marks)
+	m = copy.deepcopy(marks)
+	index_constraints(m.values(), provides, accepts)
+	add_interfaces(t, m)
+	localize_names(t, names, m)
 	t.incorporate_elements()
-	constraints.extend(marks.values())
+	constraints.extend(m.values())
 
 # Let the user know if they messed up on specifying constraints.
