Search in sources :

Example 1 with MessageEffectsBuilder

use of won.protocol.agreement.effect.MessageEffectsBuilder in project webofneeds by researchstudio-sat.

the class AgreementProtocolState method recalculate.

/**
 * Calculates all agreements present in the specified conversation dataset.
 */
private void recalculate(Dataset conversationDataset) {
    if (logger.isDebugEnabled()) {
        logger.debug("starting conversation analysis for high-level protocols");
    }
    pendingProposals.begin(ReadWrite.WRITE);
    agreements.begin(ReadWrite.WRITE);
    cancelledAgreements.begin(ReadWrite.WRITE);
    rejected.begin(ReadWrite.WRITE);
    claims.begin(ReadWrite.WRITE);
    conversationDataset.begin(ReadWrite.READ);
    this.messagesByURI = ConversationMessagesReader.readConversationMessages(conversationDataset);
    Set<ConversationMessage> roots = new HashSet<ConversationMessage>();
    Collection<ConversationMessage> messages = messagesByURI.values();
    Set<DeadReferenceConversationMessage> messagesWithDeadReferences = new HashSet<>();
    // filter out messages we don't care about
    messages = messages.stream().filter(m -> !m.getMessageType().isHintMessage()).collect(Collectors.toList());
    // iterate over messages and interconnect them
    messages.stream().forEach(message -> {
        message.getPrevious().stream().filter(uri -> !uri.equals(message.getMessageURI())).forEach(uri -> {
            ConversationMessage other = messagesByURI.get(uri);
            if (other != null) {
                message.addPreviousRef(other);
                other.addPreviousInverseRef(message);
            } else {
                messagesWithDeadReferences.add(new DeadReferenceConversationMessage(message, "msg:previousMessage", uri));
            }
        });
        message.getForwarded().stream().filter(uri -> !uri.equals(message.getMessageURI())).forEach(uri -> {
            ConversationMessage other = messagesByURI.get(uri);
            if (other != null) {
                message.addForwardedRef(other);
                other.addForwardedInverseRef(message);
            } else {
                messagesWithDeadReferences.add(new DeadReferenceConversationMessage(message, "msg:forwardedMessage", uri));
            }
        });
        message.getAccepts().stream().filter(uri -> !uri.equals(message.getMessageURI())).forEach(uri -> {
            ConversationMessage other = messagesByURI.get(uri);
            if (other != null) {
                message.addAcceptsRef(other);
                other.addAcceptsInverseRef(message);
            } else {
                messagesWithDeadReferences.add(new DeadReferenceConversationMessage(message, "agr:accepts", uri));
            }
        });
        message.getProposes().stream().filter(uri -> !uri.equals(message.getMessageURI())).forEach(uri -> {
            ConversationMessage other = messagesByURI.get(uri);
            if (other != null) {
                message.addProposesRef(other);
                other.addProposesInverseRef(message);
            } else {
                messagesWithDeadReferences.add(new DeadReferenceConversationMessage(message, "agr:proposes", uri));
            }
        });
        message.getClaims().stream().filter(uri -> !uri.equals(message.getMessageURI())).forEach(uri -> {
            ConversationMessage other = messagesByURI.get(uri);
            if (other != null) {
                message.addClaimsRef(other);
                other.addClaimsInverseRef(message);
            } else {
                messagesWithDeadReferences.add(new DeadReferenceConversationMessage(message, "agr:claims", uri));
            }
        });
        message.getRejects().stream().filter(uri -> !uri.equals(message.getMessageURI())).forEach(uri -> {
            ConversationMessage other = messagesByURI.get(uri);
            if (other != null) {
                message.addRejectsRef(other);
                other.addRejectsInverseRef(message);
            } else {
                messagesWithDeadReferences.add(new DeadReferenceConversationMessage(message, "agr:rejects", uri));
            }
        });
        message.getProposesToCancel().stream().filter(uri -> !uri.equals(message.getMessageURI())).forEach(uri -> {
            ConversationMessage other = messagesByURI.get(uri);
            if (other != null) {
                message.addProposesToCancelRef(other);
                other.addProposesToCancelInverseRef(message);
            } else {
                messagesWithDeadReferences.add(new DeadReferenceConversationMessage(message, "agr:proposesToCancel", uri));
            }
        });
        message.getRetracts().stream().filter(uri -> !uri.equals(message.getMessageURI())).forEach(uri -> {
            ConversationMessage other = messagesByURI.get(uri);
            if (other != null) {
                message.addRetractsRef(other);
                other.addRetractsInverseRef(message);
            } else {
                messagesWithDeadReferences.add(new DeadReferenceConversationMessage(message, "mod:retracts", uri));
            }
        });
        if (message.getRespondingTo() != null && !message.getRespondingTo().equals(message.getMessageURI())) {
            ConversationMessage other = messagesByURI.get(message.getRespondingTo());
            if (other != null) {
                if (other.getSenderAtomURI().equals(message.getSenderAtomURI())) {
                    if (other.getRespondingToInverseRef() != null && !message.equals(other.getRespondingToInverseRef())) {
                        throw new InconsistentConversationDataException("Message " + other.getMessageURI() + " has more than one response: " + other.getRespondingToInverseRef().getMessageURI() + " and " + message.getMessageURI());
                    }
                    message.setRespondingToRef(other);
                    other.setRespondingToInverseRef(message);
                } else {
                    // change from respondingTo to remotelyRespondingTo
                    if (other.getRemotelyRespondingToInverseRef() != null && !message.equals(other.getRemotelyRespondingToInverseRef())) {
                        throw new InconsistentConversationDataException("Message " + other.getMessageURI() + " has more than one remote response: " + other.getRemotelyRespondingToInverseRef().getMessageURI() + " and " + message.getMessageURI());
                    }
                    message.setRemotelyRespondingTo(message.getRespondingTo());
                    // clear original reference
                    message.setRespondingTo(null);
                    message.setRemotelyRespondingToRef(other);
                    other.setRemotelyRespondingToInverseRef(message);
                }
            } else {
                messagesWithDeadReferences.add(new DeadReferenceConversationMessage(message, "msg:respondingTo", message.getRespondingTo()));
            }
        }
        if (message.getPrevious().isEmpty()) {
            roots.add(message);
        }
    });
    // 
    // now revisit all messages with dead references. Throw an exception if the
    // message is not a forwarded message
    messagesWithDeadReferences.stream().forEach(deadRef -> {
        if (deadRef.message.getMessageType() == WonMessageType.FAILURE_RESPONSE) {
            // eg because it failed consistency checks
            return;
        }
        if (deadRef.message.isForwardedMessage()) {
            // if it points to a missing message
            return;
        }
        throw new IncompleteConversationDataException(deadRef.message.getMessageURI(), deadRef.deadReference, deadRef.predicate);
    });
    if (logger.isDebugEnabled()) {
        messages.stream().forEach(m -> logger.debug(m.toString()));
    }
    // link messages to deliveryChains
    deliveryChains = messages.stream().map(m -> {
        if (logger.isDebugEnabled()) {
            logger.debug("deliveryChain for message {}: {}", m.getMessageURI(), m.getDeliveryChain());
        }
        return m.getDeliveryChain();
    }).collect(Collectors.toSet());
    // find interleaved delivery chains
    deliveryChains.stream().forEach(dc -> deliveryChains.stream().forEach(dc2 -> {
        dc.determineRelationshipWith(dc2);
    }));
    // apply acknowledgment protocol to whole conversation first:
    conversation = acknowledgedSelection(conversationDataset, messages);
    // on top of this, apply modification and agreement protocol on a per-message
    // basis, starting with the root(s)
    // expect proposals and agreements to be empty
    PriorityQueue<ConversationMessage> currentMessages = new PriorityQueue<ConversationMessage>();
    currentMessages.addAll(messages);
    // we need to use a priority queue for the messages, which is
    // sorted by temporal ordering. Each time we process a message, we
    // add the subsequent ones to the queue, the retrieve the
    // oldest from the queue for the next iteration.
    Set<ConversationMessage> processed = new HashSet<>();
    List<ConversationMessage> processedInOrder = null;
    if (logger.isDebugEnabled()) {
        processedInOrder = new ArrayList<>();
    }
    ConversationMessage last = null;
    while (!currentMessages.isEmpty()) {
        ConversationMessage msg = currentMessages.poll();
        if (processed.contains(msg)) {
            continue;
        }
        processed.add(msg);
        MessageEffectsBuilder effectsBuilder = new MessageEffectsBuilder(msg.getMessageURI());
        if (logger.isDebugEnabled() && processedInOrder != null) {
            processedInOrder.add(msg);
        }
        last = msg;
        if (!msg.isHeadOfDeliveryChain()) {
            continue;
        }
        if (!msg.isAgreementProtocolMessage()) {
            continue;
        }
        if (msg.isRetractsMessage()) {
            removeContentGraphs(conversation, msg);
            if (logger.isDebugEnabled()) {
                msg.getRetractsRefs().forEach(other -> {
                    logger.debug("{} retracts {}", msg.getMessageURI(), other.getMessageURI());
                });
            }
            msg.getRetractsRefs().stream().filter(other -> msg != other).filter(other -> other.getSenderAtomURI().equals(msg.getSenderAtomURI())).filter(other -> other.isHeadOfDeliveryChain()).filter(other -> msg.isAfter(other)).forEach(other -> {
                if (logger.isDebugEnabled()) {
                    logger.debug("{} retracts {}: valid, computing effects", msg.getMessageURI(), other.getMessageURI());
                }
                boolean changedSomething = false;
                changedSomething = removeContentGraphs(conversation, other) || changedSomething;
                retractedUris.add(other.getMessageURI());
                if (other.isProposesMessage() || other.isProposesToCancelMessage()) {
                    changedSomething = retractProposal(other.getMessageURI()) || changedSomething;
                }
                if (other.isClaimsMessage()) {
                    changedSomething = retractClaim(other.getMessageURI()) || changedSomething;
                }
                if (changedSomething) {
                    effectsBuilder.retracts(other.getMessageURI());
                }
            });
            if (logger.isDebugEnabled()) {
                logger.debug("agreement data: {}", agrDataToString());
            }
        }
        if (msg.isRejectsMessage()) {
            removeContentGraphs(conversation, msg);
            if (logger.isDebugEnabled()) {
                msg.getRejectsRefs().forEach(other -> {
                    logger.debug("{} rejects {}", msg.getMessageURI(), other.getMessageURI());
                });
            }
            msg.getRejectsRefs().stream().filter(other -> msg != other).filter(other -> other.isProposesMessage() || other.isProposesToCancelMessage() || other.isClaimsMessage()).filter(other -> other.isHeadOfDeliveryChain()).filter(other -> !other.getSenderAtomURI().equals(msg.getSenderAtomURI())).filter(other -> msg.isAfter(other)).filter(other -> {
                // Resolution: neither statement has any effect.
                return !msg.accepts(other);
            }).forEach(other -> {
                if (logger.isDebugEnabled()) {
                    logger.debug("{} rejects {}: valid, computing effects", msg.getMessageURI(), other.getMessageURI());
                }
                boolean changedSomething = false;
                if (other.isProposesMessage() || other.isProposesToCancelMessage()) {
                    changedSomething = rejectProposal(other.getMessageURI()) || changedSomething;
                }
                if (other.isClaimsMessage()) {
                    changedSomething = rejectClaim(other.getMessageURI()) || changedSomething;
                }
                if (changedSomething) {
                    effectsBuilder.rejects(other.getMessageURI());
                }
            });
            if (logger.isDebugEnabled()) {
                logger.debug("agreement data: {}", agrDataToString());
            }
        }
        if (msg.isProposesMessage()) {
            if (logger.isDebugEnabled()) {
                msg.getProposesRefs().forEach(other -> {
                    logger.debug("{} proposes {}", msg.getMessageURI(), other.getMessageURI());
                });
            }
            Model proposalContent = ModelFactory.createDefaultModel();
            msg.getProposesRefs().stream().filter(other -> msg != other).filter(other -> other.isHeadOfDeliveryChain()).filter(other -> msg.isAfter(other)).forEach(other -> {
                if (logger.isDebugEnabled()) {
                    logger.debug("{} proposes {}: valid, computing effects", msg.getMessageURI(), other.getMessageURI());
                }
                boolean changedSomething = propose(conversationDataset, other.getContentGraphs(), proposalContent);
                if (changedSomething) {
                    effectsBuilder.proposes(other.getMessageURI());
                }
            });
            pendingProposals.addNamedModel(msg.getMessageURI().toString(), proposalContent);
            if (logger.isDebugEnabled()) {
                logger.debug("agreement data: {}", agrDataToString());
            }
        }
        if (msg.isProposesToCancelMessage()) {
            if (logger.isDebugEnabled()) {
                msg.getProposesToCancelRefs().forEach(other -> {
                    logger.debug("{} proposesToCancel {}", msg.getMessageURI(), other.getMessageURI());
                });
            }
            Model proposeToCancelContent = ModelFactory.createDefaultModel();
            msg.getProposesToCancelRefs().stream().filter(other -> msg != other).filter(other -> other.isHeadOfDeliveryChain()).filter(toCancel -> msg.isAfter(toCancel)).forEach(other -> {
                if (logger.isDebugEnabled()) {
                    logger.debug("{} proposesToCancel {}: valid, computing effects", msg.getMessageURI(), other.getMessageURI());
                }
                boolean changedSomething = propose(conversationDataset, other.getContentGraphs(), proposeToCancelContent);
                if (changedSomething) {
                    effectsBuilder.proposesToCancel(other.getMessageURI());
                }
            });
            pendingProposals.addNamedModel(msg.getMessageURI().toString(), proposeToCancelContent);
            if (logger.isDebugEnabled()) {
                logger.debug("agreement data: {}", agrDataToString());
            }
        }
        if (msg.isClaimsMessage()) {
            if (logger.isDebugEnabled()) {
                msg.getClaimsRefs().forEach(other -> {
                    logger.debug("{} claims {}", msg.getMessageURI(), other.getMessageURI());
                });
            }
            Model claimContent = ModelFactory.createDefaultModel();
            msg.getClaimsRefs().stream().filter(other -> msg != other).filter(other -> other.isHeadOfDeliveryChain()).filter(other -> msg.isAfter(other)).forEach(other -> {
                if (logger.isDebugEnabled()) {
                    logger.debug("{} claims {}: valid, computing effects", msg.getMessageURI(), other.getMessageURI());
                }
                boolean changedSomething = claim(conversationDataset, other.getContentGraphs(), claimContent);
                if (changedSomething) {
                    effectsBuilder.claims(other.getMessageURI());
                }
            });
            claims.addNamedModel(msg.getMessageURI().toString(), claimContent);
            if (logger.isDebugEnabled()) {
                logger.debug("agreement data: {}", agrDataToString());
            }
        }
        if (msg.isAcceptsMessage()) {
            if (logger.isDebugEnabled()) {
                msg.getAcceptsRefs().forEach(other -> {
                    logger.debug("{} accepts {}", msg.getMessageURI(), other.getMessageURI());
                });
            }
            msg.getAcceptsRefs().stream().filter(other -> msg != other).filter(other -> other.isHeadOfDeliveryChain()).filter(other -> !other.getSenderAtomURI().equals(msg.getSenderAtomURI())).filter(other -> msg.isAfter(other)).filter(other -> {
                // Resolution: neither statement has any effect.
                return !msg.rejects(other);
            }).forEach(other -> {
                if (logger.isDebugEnabled()) {
                    logger.debug("{} accepts {}: valid, computing effects", msg.getMessageURI(), other.getMessageURI());
                }
                boolean changedSomething = false;
                if (other.isProposesMessage() || other.isProposesToCancelMessage()) {
                    changedSomething = acceptProposal(other.getMessageURI()) || changedSomething;
                }
                if (other.isClaimsMessage()) {
                    changedSomething = acceptClaim(other.getMessageURI()) || changedSomething;
                }
                if (changedSomething) {
                    effectsBuilder.accepts(other.getMessageURI(), other.getProposesToCancel().stream().collect(Collectors.toSet()));
                }
            });
            if (logger.isDebugEnabled()) {
                logger.debug("agreement data: {}", agrDataToString());
            }
        }
        msg.setEffects(effectsBuilder.build());
        if (logger.isDebugEnabled() && !msg.getEffects().isEmpty()) {
            logger.debug("Effects of message {} : {}", msg.getMessageURI(), msg.getEffects());
        }
    }
    if (logger.isDebugEnabled()) {
        logger.debug("messages in the order they were processed:");
        if (processedInOrder != null) {
            processedInOrder.stream().forEach(x -> logger.debug(x.toString()));
        }
        logger.debug("finished conversation analysis for high-level protocols");
    }
    pendingProposals.commit();
    agreements.commit();
    cancelledAgreements.commit();
    rejected.commit();
    claims.commit();
    conversationDataset.end();
}
Also used : LinkedDataSource(won.protocol.util.linkeddata.LinkedDataSource) java.util(java.util) Logger(org.slf4j.Logger) WonLinkedDataUtils(won.protocol.util.linkeddata.WonLinkedDataUtils) WonMessageType(won.protocol.message.WonMessageType) MethodHandles(java.lang.invoke.MethodHandles) LoggerFactory(org.slf4j.LoggerFactory) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) MessageEffectsBuilder(won.protocol.agreement.effect.MessageEffectsBuilder) Collectors(java.util.stream.Collectors) ProposalType(won.protocol.agreement.effect.ProposalType) WonMessageDirection(won.protocol.message.WonMessageDirection) WonRdfUtils(won.protocol.util.WonRdfUtils) Model(org.apache.jena.rdf.model.Model) Stream(java.util.stream.Stream) RdfUtils(won.protocol.util.RdfUtils) DatasetFactory(org.apache.jena.query.DatasetFactory) ReadWrite(org.apache.jena.query.ReadWrite) ModelFactory(org.apache.jena.rdf.model.ModelFactory) URI(java.net.URI) Dataset(org.apache.jena.query.Dataset) MessageEffect(won.protocol.agreement.effect.MessageEffect) MessageEffectsBuilder(won.protocol.agreement.effect.MessageEffectsBuilder) Model(org.apache.jena.rdf.model.Model)

Aggregations

MethodHandles (java.lang.invoke.MethodHandles)1 URI (java.net.URI)1 java.util (java.util)1 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)1 Collectors (java.util.stream.Collectors)1 Stream (java.util.stream.Stream)1 Dataset (org.apache.jena.query.Dataset)1 DatasetFactory (org.apache.jena.query.DatasetFactory)1 ReadWrite (org.apache.jena.query.ReadWrite)1 Model (org.apache.jena.rdf.model.Model)1 ModelFactory (org.apache.jena.rdf.model.ModelFactory)1 Logger (org.slf4j.Logger)1 LoggerFactory (org.slf4j.LoggerFactory)1 MessageEffect (won.protocol.agreement.effect.MessageEffect)1 MessageEffectsBuilder (won.protocol.agreement.effect.MessageEffectsBuilder)1 ProposalType (won.protocol.agreement.effect.ProposalType)1 WonMessageDirection (won.protocol.message.WonMessageDirection)1 WonMessageType (won.protocol.message.WonMessageType)1 RdfUtils (won.protocol.util.RdfUtils)1 WonRdfUtils (won.protocol.util.WonRdfUtils)1