Пример #1
0
    def __init__(self, parent_id, element, fei, context):
        """Constructor."""

        self.fe_name = self.__class__.__name__.lower()
        LOG.debug("Creating %s", self.fe_name)

        self.state = 'ready'
        self.id = fei
        self.parent_id = parent_id
        self.children = []
        self.faults = None
        if self.is_ctx_allowed:
            self.context = Context(context)
        else:
            self.context = context

        el_index = 0
        for child in element:
            if child.tag in self.allowed_child_types:
                fexpr = _create_fe_from_element(self.id, child,
                                                "%s_%d" % (fei, el_index),
                                                self.context)
                self.children.append(fexpr)
                el_index = el_index + 1
            else:
                self._parse_non_child(child)
Пример #2
0
 def restart():
     self.context = Context(self._parent_ctx)
     for child in self._element:
         if child.tag == 'context':
             self.context.parse(child)
             break
     self.reset_children()
     channel.send(
         Message(name='start',
                 target=self.children[0].id,
                 origin=self.id))
Пример #3
0
class Foreach(FlowExpression):
    """Foreach activity."""

    allowed_child_types = _get_supported_activities()
    is_cond_allowed = True

    def __init__(self, parent_id, element, fei, context):
        """Constructor."""

        FlowExpression.__init__(self, parent_id, element, fei, context)
        # these are needed to reset local context upon every iteration
        self._parent_ctx = context
        self._element = element

    def _activate(self, msg):
        if self._is_start_message(msg):
            selection = self._parent_ctx
            for key in self._element.attrib["select"].split('.'):
                selection = selection.get(key)
            assert isinstance(selection, list), "%s is not list" % selection
            self.context.set('inst:selection', selection)
            self.context.set('inst:iteration', 1)
            if len(selection) > 0:
                self.context.set('inst:current', selection[0])

    def handle_message(self, channel, msg):
        """Handle message."""
        def guard():
            selection_size = len(self.context.get('inst:selection'))
            if self.context.get('inst:iteration') < selection_size:
                return True
            else:
                return False

        def restart():
            iteration = self.context.get('inst:iteration')
            selection = self.context.get('inst:selection')
            self.context = Context(self._parent_ctx)
            for child in self._element:
                if child.tag == 'context':
                    self.context.parse(child)
                    break
            self.reset_children()
            self.context.set('inst:iteration', iteration + 1)
            self.context.set('inst:current', selection[iteration])
            self.context.set('inst:selection', selection)
            channel.send(
                Message(name='start',
                        target=self.children[0].id,
                        origin=self.id))

        return FlowExpression.handle_message(self, channel, msg) or \
                self._activate(msg) or \
                self._was_activated(channel, msg, guard) or \
                self._was_sequence_completed(channel, msg,
                                             lambda: not guard(),
                                             restart) or \
                self._was_consumed_by_child(channel, msg) or 'ignored'
Пример #4
0
 def restart():
     iteration = self.context.get('inst:iteration')
     selection = self.context.get('inst:selection')
     self.context = Context(self._parent_ctx)
     for child in self._element:
         if child.tag == 'context':
             self.context.parse(child)
             break
     self.reset_children()
     self.context.set('inst:iteration', iteration + 1)
     self.context.set('inst:current', selection[iteration])
     self.context.set('inst:selection', selection)
     channel.send(
         Message(name='start',
                 target=self.children[0].id,
                 origin=self.id))
Пример #5
0
 def setUp(self):
     """Set up SUT."""
     xml_element = ET.fromstring(procdsc)
     self.procexpr = Process('', xml_element, 'fake-id', Context())
     self.seqexpr = self.procexpr.children[0]
     self.whileexpr = self.seqexpr.children[0]
     self.allexpr = self.whileexpr.children[1]
Пример #6
0
    def setUp(self):
        """Set up test case."""

        xmlelement = ET.fromstring(procdsc)
        self.root = Process('', xmlelement, 'fake-id', Context())
        self.root.state = 'active'
        self.ch = Mock()
Пример #7
0
    def __init__(self, parent_id, element, fei, context):
        """Constructor."""

        self.fe_name = self.__class__.__name__.lower()
        LOG.debug("Creating %s", self.fe_name)

        self.state = 'ready'
        self.id = fei
        self.parent_id = parent_id
        self.children = []
        self.faults = None
        if self.is_ctx_allowed:
            self.context = Context(context)
        else:
            self.context = context

        el_index = 0
        for child in element:
            if child.tag in self.allowed_child_types:
                fexpr = _create_fe_from_element(self.id, child,
                                                "%s_%d" % (fei, el_index),
                                                self.context)
                self.children.append(fexpr)
                el_index = el_index + 1
            else:
                self._parse_non_child(child)
Пример #8
0
class While(FlowExpression):
    """While activity."""

    allowed_child_types = _get_supported_activities()
    is_cond_allowed = True

    def __init__(self, parent_id, element, fei, context):
        """Constructor."""

        self.conditions = []
        FlowExpression.__init__(self, parent_id, element, fei, context)
        # these are needed to reset local context upon every iteration
        self._parent_ctx = context
        self._element = element

    def evaluate(self):
        """Check if conditions are met."""

        for cond in self.conditions:
            if eval(cond, {"context": self.context.as_dictionary()}):
                LOG.debug("Condition %s evaluated to True", cond)
                return True
            else:
                LOG.debug("Condition %s evaluated to False", cond)

        return False

    def handle_message(self, channel, msg):
        """Handle message."""
        def restart():
            self.context = Context(self._parent_ctx)
            for child in self._element:
                if child.tag == 'context':
                    self.context.parse(child)
                    break
            self.reset_children()
            channel.send(
                Message(name='start',
                        target=self.children[0].id,
                        origin=self.id))

        return FlowExpression.handle_message(self, channel, msg) or \
                self._was_activated(channel, msg, self.evaluate) or \
                self._was_sequence_completed(channel, msg,
                                             lambda: not self.evaluate(),
                                             restart) or \
                self._was_consumed_by_child(channel, msg) or 'ignored'
Пример #9
0
class Foreach(FlowExpression):
    """Foreach activity."""

    allowed_child_types = _get_supported_activities()
    is_cond_allowed = True

    def __init__(self, parent_id, element, fei, context):
        """Constructor."""

        FlowExpression.__init__(self, parent_id, element, fei, context)
        # these are needed to reset local context upon every iteration
        self._parent_ctx = context
        self._element = element

    def _activate(self, msg):
        if self._is_start_message(msg):
            selection = self._parent_ctx
            for key in self._element.attrib["select"].split('.'):
                selection = selection.get(key)
            assert isinstance(selection, list), "%s is not list" % selection
            self.context.set('inst:selection', selection)
            self.context.set('inst:iteration', 1)
            if len(selection) > 0:
                self.context.set('inst:current', selection[0])

    def handle_message(self, channel, msg):
        """Handle message."""

        def guard():
            selection_size = len(self.context.get('inst:selection'))
            if self.context.get('inst:iteration') < selection_size:
                return True
            else:
                return False

        def restart():
            iteration = self.context.get('inst:iteration')
            selection = self.context.get('inst:selection')
            self.context = Context(self._parent_ctx)
            for child in self._element:
                if child.tag == 'context':
                    self.context.parse(child)
                    break
            self.reset_children()
            self.context.set('inst:iteration', iteration + 1)
            self.context.set('inst:current', selection[iteration])
            self.context.set('inst:selection', selection)
            channel.send(Message(name='start', target=self.children[0].id,
                                 origin=self.id))

        return FlowExpression.handle_message(self, channel, msg) or \
                self._activate(msg) or \
                self._was_activated(channel, msg, guard) or \
                self._was_sequence_completed(channel, msg,
                                             lambda: not guard(),
                                             restart) or \
                self._was_consumed_by_child(channel, msg) or 'ignored'
Пример #10
0
    def setUp(self):
        """Set up test case."""

        xmlelement = ET.fromstring(procdsc)
        self.root = Process('', xmlelement, 'fake-id', Context())
        self.seq = self.root.children[0]
        self.faults = self.seq.faults
        self.ch = Mock()
Пример #11
0
class While(FlowExpression):
    """While activity."""

    allowed_child_types = _get_supported_activities()
    is_cond_allowed = True

    def __init__(self, parent_id, element, fei, context):
        """Constructor."""

        self.conditions = []
        FlowExpression.__init__(self, parent_id, element, fei, context)
        # these are needed to reset local context upon every iteration
        self._parent_ctx = context
        self._element = element

    def evaluate(self):
        """Check if conditions are met."""

        for cond in self.conditions:
            if eval(cond, {"context": self.context.as_dictionary()}):
                LOG.debug("Condition %s evaluated to True", cond)
                return True
            else:
                LOG.debug("Condition %s evaluated to False", cond)

        return False

    def handle_message(self, channel, msg):
        """Handle message."""

        def restart():
            self.context = Context(self._parent_ctx)
            for child in self._element:
                if child.tag == 'context':
                    self.context.parse(child)
                    break
            self.reset_children()
            channel.send(Message(name='start', target=self.children[0].id,
                                 origin=self.id))

        return FlowExpression.handle_message(self, channel, msg) or \
                self._was_activated(channel, msg, self.evaluate) or \
                self._was_sequence_completed(channel, msg,
                                             lambda: not self.evaluate(),
                                             restart) or \
                self._was_consumed_by_child(channel, msg) or 'ignored'
Пример #12
0
    def setUp(self):
        """Set up test case."""

        self.ch = Mock()
        self.old_tag2class_map = \
                bureaucrat.flowexpression._create_fe_from_element
        bureaucrat.flowexpression._create_fe_from_element = tag2class_map
        self.root = ComplexExpression('', ET.fromstring(procdsc),
                                      'fake-id', Context())
Пример #13
0
 def restart():
     self.context = Context(self._parent_ctx)
     for child in self._element:
         if child.tag == 'context':
             self.context.parse(child)
             break
     self.reset_children()
     channel.send(Message(name='start', target=self.children[0].id,
                          origin=self.id))
Пример #14
0
    def test_constructor_no_children(self):
        """Test FlowExpression.__init__() for simple expression."""

        def fake_iterator(parent):
            while False:
                yield None
        self.xml_element.__iter__ = fake_iterator
        fexpr = FlowExpression('fake-id', self.xml_element, 'fake-id_0',
                               Context())
        self.assertTrue(fexpr.id == 'fake-id_0')
Пример #15
0
    def test_constructor(self):
        """Test FlowExpression.__init__() for complex expression."""

        self.xml_element.__iter__ = fake_et_iterator
        self.xml_element.tag = 'complexexpression'

        with patch('bureaucrat.flowexpression._create_fe_from_element') as t2cmap:
            t2cmap = tag2class_map
            fexpr = ComplexExpression('fake-id', self.xml_element, 'fake-id_0',
                                   Context())
            self.assertTrue(fexpr.id == 'fake-id_0')
            self.assertTrue(len(fexpr.children) == 1)
Пример #16
0
 def restart():
     iteration = self.context.get('inst:iteration')
     selection = self.context.get('inst:selection')
     self.context = Context(self._parent_ctx)
     for child in self._element:
         if child.tag == 'context':
             self.context.parse(child)
             break
     self.reset_children()
     self.context.set('inst:iteration', iteration + 1)
     self.context.set('inst:current', selection[iteration])
     self.context.set('inst:selection', selection)
     channel.send(Message(name='start', target=self.children[0].id,
                          origin=self.id))
Пример #17
0
    def load(process_id):
        """Return existing workflow instance loaded from storage."""

        LOG.debug("Load a process definition from %s", process_id)
        storage = Storage.instance()
        pdef = storage.load("definition", process_id)
        xmlelement = ET.fromstring(pdef)
        assert xmlelement.tag == 'process'

        parent_id = ''
        if "parent" in xmlelement.attrib:
            parent_id = xmlelement.attrib["parent"]

        process = Process(parent_id, xmlelement, process_id, Context())
        process.reset_state(json.loads(storage.load("process", process.id)))
        return Workflow(process)
Пример #18
0
    def create_from_string(pdef, pid):
        """Create Workflow instance from process definition string."""

        LOG.debug("Creating workflow instance from string.")

        xmlelement = ET.fromstring(pdef)
        assert xmlelement.tag == 'process'

        Storage.instance().save("definition", pid, pdef)

        parent_id = ''
        if "parent" in xmlelement.attrib:
            parent_id = xmlelement.attrib["parent"]

        process = Process(parent_id, xmlelement, pid, Context())
        workflow = Workflow(process)
        workflow.save()
        return workflow
Пример #19
0
 def setUp(self):
     xml_element = ET.fromstring(self.processdsc)
     self.fexpr = While('fake-id', xml_element, 'fake-id_0', Context())
     self.ch = Mock()
Пример #20
0
 def setUp(self):
     xml_element = ET.fromstring(processdsc)
     self.fexpr = Process('', xml_element, 'fake-id', Context())
     self.ch = Mock()
Пример #21
0
class FlowExpression(object):
    """Flow expression."""

    allowed_child_types = ()
    is_ctx_allowed = True  # TODO: refactor to introduce simple and complex activities
    is_cond_allowed = False
    is_handler = False

    def __init__(self, parent_id, element, fei, context):
        """Constructor."""

        self.fe_name = self.__class__.__name__.lower()
        LOG.debug("Creating %s", self.fe_name)

        self.state = 'ready'
        self.id = fei
        self.parent_id = parent_id
        self.children = []
        self.faults = None
        if self.is_ctx_allowed:
            self.context = Context(context)
        else:
            self.context = context

        el_index = 0
        for child in element:
            if child.tag in self.allowed_child_types:
                fexpr = _create_fe_from_element(self.id, child,
                                                "%s_%d" % (fei, el_index),
                                                self.context)
                self.children.append(fexpr)
                el_index = el_index + 1
            else:
                self._parse_non_child(child)

    def __str__(self):
        """String representation."""
        return "%s" % self.id

    def __repr__(self):
        """Instance representation."""
        return "<%s[%s, state='%s']>" % (self.__class__.__name__, self,
                                         self.state)

    def _parse_non_child(self, element):
        """Parse disallowed child element.

        Some flow expressions can contain child elements in their definition
        which are just attributes of them, not other flow expression.
        As an example consider <condition> inside <case>.
        """
        if element.tag == 'context' and self.is_ctx_allowed:
            self.context.parse(element)
            for child in element:
                if child.tag == 'faults':
                    self.faults = Faults(self.id, child, "%s_faults" % self.id,
                                         self.context)
                    break

        elif element.tag == 'condition' and self.is_cond_allowed:
            html_parser = HTMLParser()
            self.conditions.append(html_parser.unescape(element.text))
        else:
            raise FlowExpressionError("'%s' is disallowed child type" % \
                                      element.tag)

    def snapshot(self):
        """Return flow expression snapshot."""
        snapshot = {
            "id": self.id,
            "state": self.state,
            "type": self.fe_name,
            "children": [child.snapshot() for child in self.children]
        }
        if self.is_ctx_allowed:
            snapshot["context"] = self.context.localprops
            if self.faults:
                snapshot["faults"] = self.faults.snapshot()
        return snapshot

    def reset_state(self, state):
        """Reset activity's state."""
        LOG.debug("Resetting %s's state", self.fe_name)
        assert state["type"] == self.fe_name
        assert state["id"] == self.id
        self.state = state["state"]
        if self.is_ctx_allowed:
            self.context.localprops = state["context"]
            if self.faults:
                self.faults.reset_state(state["faults"])
        for child, childstate in zip(self.children, state["children"]):
            child.reset_state(childstate)

    def handle_message(self, channel, msg):
        """Handle message.

        This is common code used by all derived classes.
        """

        result = ''

        if self.state == 'completed':
            LOG.debug("%r is done already, %r is ignored", self, msg)
            result = 'ignored'
        elif msg.target is not None and \
                not msg.target.startswith(self.id):
            LOG.debug("%r is not for %r", msg, self)
            result = 'ignored'
        elif (self.is_ctx_allowed or self.is_handler) and \
                self.state == 'active' and \
                msg.name == 'fault' and msg.target == self.id:
            self.state = 'aborting'
            self.context.throw(code=msg.payload.get('code', 'GenericError'),
                               message=msg.payload.get('message', ''))
            all_in_final_state = True
            for child in self.children:
                if not is_state_final(child.state):
                    all_in_final_state = False
                    channel.send(
                        Message(name='terminate',
                                target=child.id,
                                origin=self.id))
            if all_in_final_state:
                self.state = 'aborted'
                channel.send(
                    Message(name='fault',
                            target=self.parent_id,
                            origin=self.id,
                            payload=msg.payload))
            result = 'consumed'
        elif self.is_ctx_allowed and self.state == 'active' and \
                msg.name == 'terminate' and msg.target == self.id:
            self.state = 'aborting'
            for child in self.children:
                if not is_state_final(child.state):
                    channel.send(
                        Message(name='terminate',
                                target=child.id,
                                origin=self.id))
            result = 'consumed'
        elif self.is_ctx_allowed and self.state == 'ready' and \
                msg.name == 'terminate' and msg.target == self.id:
            self.state = 'canceling'
            for child in self.children:
                if not is_state_final(child.state):
                    channel.send(
                        Message(name='terminate',
                                target=child.id,
                                origin=self.id))
            result = 'consumed'
        elif (self.is_ctx_allowed or self.is_handler) and \
                self.state == 'aborting' and \
                (msg.name == 'canceled' or msg.name == 'aborted' or \
                 msg.name == 'completed') and \
                msg.target == self.id:
            if msg.name == 'completed' and msg.origin.endswith("faults"):
                self.state = 'completed'
                del self.context._props['inst:fault']
                channel.send(
                    Message(name='completed',
                            target=self.parent_id,
                            origin=self.id))
            elif sum([
                    int(is_state_final(child.state)) for child in self.children
            ]) == len(self.children):
                # all children are in a final state
                if self.faults and self.faults.state == 'ready':
                    channel.send(
                        Message(name='start',
                                target=self.faults.id,
                                origin=self.id))
                else:
                    self.state = 'aborted'
                    channel.send(
                        Message(name='fault',
                                target=self.parent_id,
                                origin=self.id,
                                payload=self.context.get('inst:fault')))
            result = 'consumed'
        elif self.is_ctx_allowed and self.state == 'aborting' and \
                msg.name == 'fault' and msg.target == self.id:
            self.state = 'aborted'
            channel.send(
                Message(name='fault',
                        target=self.parent_id,
                        origin=self.id,
                        payload=self.context.get('inst:fault')))
            result = 'consumed'
        elif self.is_ctx_allowed and self.state == 'canceling' and \
                (msg.name == 'canceled' or msg.name == 'aborted' or \
                 msg.name == 'completed') and msg.target == self.id:
            if sum([
                    int(is_state_final(child.state)) for child in self.children
            ]) == len(self.children):
                # all children are in a final state
                channel.send(
                    Message(name='canceled',
                            target=self.parent_id,
                            origin=self.id))
                self.state = 'canceled'
            result = 'consumed'
        elif not self.is_ctx_allowed and self.state == 'active' and \
                msg.name == 'terminate' and msg.target == self.id:
            self.state = 'aborted'
            channel.send(
                Message(name='aborted', target=self.parent_id, origin=self.id))
            result = 'consumed'
        elif not self.is_ctx_allowed and self.state == 'ready' and \
                msg.name == 'terminate' and msg.target == self.id:
            self.state = 'canceled'
            channel.send(
                Message(name='canceled', target=self.parent_id,
                        origin=self.id))
            result = 'consumed'
        elif not self.is_ctx_allowed and self.state == 'aborting' and \
                msg.name == 'terminate' and msg.target == self.id:
            self.state = 'aborted'
            channel.send(
                Message(name='aborted', target=self.parent_id, origin=self.id))
            result = 'consumed'

        return result

    def _was_activated(self, channel, msg, guard=lambda: True):
        """Check if it's activation message."""

        if self._is_start_message(msg):

            if not guard():
                LOG.debug("Conditions for %r don't hold", self)
                self.state = 'completed'
                channel.send(
                    Message(name='completed',
                            origin=self.id,
                            target=self.parent_id))
                return 'consumed'

            if len(self.children) > 0:
                self.state = 'active'
                channel.send(
                    Message(name='start',
                            origin=self.id,
                            target=self.children[0].id))
            else:
                self.state = 'completed'
                channel.send(
                    Message(name='completed',
                            origin=self.id,
                            target=self.parent_id))
            return 'consumed'
        else:
            return ''

    def _was_sequence_completed(self,
                                channel,
                                msg,
                                guard=lambda: True,
                                compensate=lambda: None):
        """Check if all children in sequence were completed."""

        res = ''
        if self._is_complete_message(msg):
            for index, child in zip(range(0, len(self.children)),
                                    self.children):
                if child.id == msg.origin:
                    if (index + 1) < len(self.children):
                        channel.send(
                            Message(name='start',
                                    origin=self.id,
                                    target="%s_%d" % (self.id, index + 1)))
                    else:
                        if guard():
                            self.state = 'completed'
                            channel.send(
                                Message(name='completed',
                                        origin=self.id,
                                        target=self.parent_id))
                        else:
                            compensate()
                    res = 'consumed'
                    break
        return res

    def _was_consumed_by_child(self, channel, msg):
        """Check if a child consumed the message."""

        for child in self.children:
            if msg.target.startswith(child.id):
                return child.handle_message(channel, msg)

        if self.state == 'aborting' and self.faults and \
           msg.target.startswith(self.faults.id):
            return self.faults.handle_message(channel, msg)

    def _is_start_message(self, msg):
        """Return True if the message destined to start the activity."""
        return self.state == 'ready' and msg.name == 'start' \
                and msg.target == self.id

    def _is_complete_message(self, msg):
        """Return True if the message is from completed child."""
        return self.state == 'active' and msg.name == 'completed' \
                and msg.target == self.id

    def reset_children(self):
        """Reset children state to ready."""

        for child in self.children:
            child.state = 'ready'
            child.reset_children()
Пример #22
0
 def setUp(self):
     """Set up SUT."""
     xml_element = ET.fromstring(processdsc)
     self.fexpr = Delay('fake-id', xml_element, 'fake-id_0', Context())
     self.ch = Mock()
Пример #23
0
 def setUp(self):
     xml_element = ET.fromstring(processdsc)
     self.fexpr = All('fake-id', xml_element, 'fake-id_0', Context())
     self.ch = Mock()
     self.ch.send = Mock()
Пример #24
0
class FlowExpression(object):
    """Flow expression."""

    allowed_child_types = ()
    is_ctx_allowed = True # TODO: refactor to introduce simple and complex activities
    is_cond_allowed = False
    is_handler = False

    def __init__(self, parent_id, element, fei, context):
        """Constructor."""

        self.fe_name = self.__class__.__name__.lower()
        LOG.debug("Creating %s", self.fe_name)

        self.state = 'ready'
        self.id = fei
        self.parent_id = parent_id
        self.children = []
        self.faults = None
        if self.is_ctx_allowed:
            self.context = Context(context)
        else:
            self.context = context

        el_index = 0
        for child in element:
            if child.tag in self.allowed_child_types:
                fexpr = _create_fe_from_element(self.id, child,
                                                "%s_%d" % (fei, el_index),
                                                self.context)
                self.children.append(fexpr)
                el_index = el_index + 1
            else:
                self._parse_non_child(child)

    def __str__(self):
        """String representation."""
        return "%s" % self.id

    def __repr__(self):
        """Instance representation."""
        return "<%s[%s, state='%s']>" % (self.__class__.__name__, self,
                                         self.state)

    def _parse_non_child(self, element):
        """Parse disallowed child element.

        Some flow expressions can contain child elements in their definition
        which are just attributes of them, not other flow expression.
        As an example consider <condition> inside <case>.
        """
        if element.tag == 'context' and self.is_ctx_allowed:
            self.context.parse(element)
            for child in element:
                if child.tag == 'faults':
                    self.faults = Faults(self.id, child, "%s_faults" % self.id,
                                         self.context)
                    break

        elif element.tag == 'condition' and self.is_cond_allowed:
            html_parser = HTMLParser()
            self.conditions.append(html_parser.unescape(element.text))
        else:
            raise FlowExpressionError("'%s' is disallowed child type" % \
                                      element.tag)

    def snapshot(self):
        """Return flow expression snapshot."""
        snapshot = {
            "id": self.id,
            "state": self.state,
            "type": self.fe_name,
            "children": [child.snapshot() for child in self.children]
        }
        if self.is_ctx_allowed:
            snapshot["context"] = self.context.localprops
            if self.faults:
                snapshot["faults"] = self.faults.snapshot()
        return snapshot

    def reset_state(self, state):
        """Reset activity's state."""
        LOG.debug("Resetting %s's state", self.fe_name)
        assert state["type"] == self.fe_name
        assert state["id"] == self.id
        self.state = state["state"]
        if self.is_ctx_allowed:
            self.context.localprops = state["context"]
            if self.faults:
                self.faults.reset_state(state["faults"])
        for child, childstate in zip(self.children, state["children"]):
            child.reset_state(childstate)

    def handle_message(self, channel, msg):
        """Handle message.

        This is common code used by all derived classes.
        """

        result = ''

        if self.state == 'completed':
            LOG.debug("%r is done already, %r is ignored", self, msg)
            result = 'ignored'
        elif msg.target is not None and \
                not msg.target.startswith(self.id):
            LOG.debug("%r is not for %r", msg, self)
            result = 'ignored'
        elif (self.is_ctx_allowed or self.is_handler) and \
                self.state == 'active' and \
                msg.name == 'fault' and msg.target == self.id:
            self.state = 'aborting'
            self.context.throw(code=msg.payload.get('code', 'GenericError'),
                               message=msg.payload.get('message', ''))
            all_in_final_state = True
            for child in self.children:
                if not is_state_final(child.state):
                    all_in_final_state = False
                    channel.send(Message(name='terminate', target=child.id,
                                         origin=self.id))
            if all_in_final_state:
                self.state = 'aborted'
                channel.send(Message(name='fault', target=self.parent_id,
                                     origin=self.id, payload=msg.payload))
            result = 'consumed'
        elif self.is_ctx_allowed and self.state == 'active' and \
                msg.name == 'terminate' and msg.target == self.id:
            self.state = 'aborting'
            for child in self.children:
                if not is_state_final(child.state):
                    channel.send(Message(name='terminate', target=child.id,
                                         origin=self.id))
            result = 'consumed'
        elif self.is_ctx_allowed and self.state == 'ready' and \
                msg.name == 'terminate' and msg.target == self.id:
            self.state = 'canceling'
            for child in self.children:
                if not is_state_final(child.state):
                    channel.send(Message(name='terminate', target=child.id,
                                         origin=self.id))
            result = 'consumed'
        elif (self.is_ctx_allowed or self.is_handler) and \
                self.state == 'aborting' and \
                (msg.name == 'canceled' or msg.name == 'aborted' or \
                 msg.name == 'completed') and \
                msg.target == self.id:
            if msg.name == 'completed' and msg.origin.endswith("faults"):
                self.state = 'completed'
                del self.context._props['inst:fault']
                channel.send(Message(name='completed', target=self.parent_id,
                                     origin=self.id))
            elif sum([int(is_state_final(child.state))
                    for child in self.children]) == len(self.children):
                # all children are in a final state
                if self.faults and self.faults.state == 'ready':
                    channel.send(Message(name='start', target=self.faults.id,
                                         origin=self.id))
                else:
                    self.state = 'aborted'
                    channel.send(Message(name='fault', target=self.parent_id,
                                         origin=self.id,
                                         payload=self.context.get('inst:fault')))
            result = 'consumed'
        elif self.is_ctx_allowed and self.state == 'aborting' and \
                msg.name == 'fault' and msg.target == self.id:
            self.state = 'aborted'
            channel.send(Message(name='fault', target=self.parent_id,
                                 origin=self.id,
                                 payload=self.context.get('inst:fault')))
            result = 'consumed'
        elif self.is_ctx_allowed and self.state == 'canceling' and \
                (msg.name == 'canceled' or msg.name == 'aborted' or \
                 msg.name == 'completed') and msg.target == self.id:
            if sum([int(is_state_final(child.state))
                    for child in self.children]) == len(self.children):
                # all children are in a final state
                channel.send(Message(name='canceled', target=self.parent_id,
                                     origin=self.id))
                self.state = 'canceled'
            result = 'consumed'
        elif not self.is_ctx_allowed and self.state == 'active' and \
                msg.name == 'terminate' and msg.target == self.id:
            self.state = 'aborted'
            channel.send(Message(name='aborted', target=self.parent_id,
                                 origin=self.id))
            result = 'consumed'
        elif not self.is_ctx_allowed and self.state == 'ready' and \
                msg.name == 'terminate' and msg.target == self.id:
            self.state = 'canceled'
            channel.send(Message(name='canceled', target=self.parent_id,
                                 origin=self.id))
            result = 'consumed'
        elif not self.is_ctx_allowed and self.state == 'aborting' and \
                msg.name == 'terminate' and msg.target == self.id:
            self.state = 'aborted'
            channel.send(Message(name='aborted', target=self.parent_id,
                                 origin=self.id))
            result = 'consumed'

        return result

    def _was_activated(self, channel, msg, guard=lambda: True):
        """Check if it's activation message."""

        if self._is_start_message(msg):

            if not guard():
                LOG.debug("Conditions for %r don't hold", self)
                self.state = 'completed'
                channel.send(Message(name='completed', origin=self.id,
                                     target=self.parent_id))
                return 'consumed'

            if len(self.children) > 0:
                self.state = 'active'
                channel.send(Message(name='start', origin=self.id,
                                     target=self.children[0].id))
            else:
                self.state = 'completed'
                channel.send(Message(name='completed', origin=self.id,
                                     target=self.parent_id))
            return 'consumed'
        else:
            return ''

    def _was_sequence_completed(self, channel, msg, guard=lambda: True,
                                compensate=lambda: None):
        """Check if all children in sequence were completed."""

        res = ''
        if self._is_complete_message(msg):
            for index, child in zip(range(0, len(self.children)),
                                    self.children):
                if child.id == msg.origin:
                    if (index + 1) < len(self.children):
                        channel.send(Message(name='start', origin=self.id,
                                             target="%s_%d" % (self.id,
                                                               index + 1)))
                    else:
                        if guard():
                            self.state = 'completed'
                            channel.send(Message(name='completed',
                                                 origin=self.id,
                                                 target=self.parent_id))
                        else:
                            compensate()
                    res = 'consumed'
                    break
        return res

    def _was_consumed_by_child(self, channel, msg):
        """Check if a child consumed the message."""

        for child in self.children:
            if msg.target.startswith(child.id):
                return child.handle_message(channel, msg)

        if self.state == 'aborting' and self.faults and \
           msg.target.startswith(self.faults.id):
            return self.faults.handle_message(channel, msg)

    def _is_start_message(self, msg):
        """Return True if the message destined to start the activity."""
        return self.state == 'ready' and msg.name == 'start' \
                and msg.target == self.id

    def _is_complete_message(self, msg):
        """Return True if the message is from completed child."""
        return self.state == 'active' and msg.name == 'completed' \
                and msg.target == self.id

    def reset_children(self):
        """Reset children state to ready."""

        for child in self.children:
            child.state = 'ready'
            child.reset_children()