/*
* Copyright 2012 Research Studios Austria Forschungsges.m.b.H.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package won.node.camel.processor.general;
import org.apache.jena.query.Dataset;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import won.cryptography.rdfsign.SigningStage;
import won.protocol.message.WonMessage;
import won.protocol.message.WonMessageType;
import won.protocol.message.WonSignatureData;
import won.protocol.message.processor.WonMessageProcessor;
import won.protocol.message.processor.exception.WonMessageProcessingException;
import won.protocol.message.processor.impl.WonMessageSignerVerifier;
import won.protocol.model.DatasetHolder;
import won.protocol.model.MessageEventPlaceholder;
import won.protocol.repository.DatasetHolderRepository;
import won.protocol.repository.MessageEventRepository;
import won.protocol.vocabulary.WONMSG;
import java.net.URI;
import java.util.List;
import java.util.stream.Collectors;
import static won.protocol.message.WonMessageType.*;
/**
* Created by fkleedorfer on 22.07.2016.
*/
public class ReferenceToUnreferencedMessageAddingProcessor implements WonMessageProcessor
{
private final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private MessageEventRepository messageEventRepository;
@Autowired
private DatasetHolderRepository datasetHolderRepository;
@Override
//we use READ_COMMITTED because we want to wait for an exclusive lock will accept data written by a concurrent transaction that commits before we read
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
public WonMessage process(final WonMessage message) throws WonMessageProcessingException {
//find all unreferenced messages for the current message's parent
List<MessageEventPlaceholder> messageEventPlaceholders = messageEventRepository
.findByParentURIAndNotReferencedByOtherMessageForUpdate(message.getSenderURI());
//initialize a variable for the result
WonMessageType messageType = message.getMessageType();
if (
messageType == CONNECT ||
messageType == OPEN ||
messageType == HINT_MESSAGE) {
//when we're starting a conversation, link to the create message of the need.
messageEventPlaceholders.addAll(messageEventRepository
.findByParentURIAndMessageTypeForUpdate(message.getReceiverNeedURI(), WonMessageType
.CREATE_NEED));
messageEventPlaceholders.addAll(messageEventRepository
.findByParentURIAndMessageTypeForUpdate(message.getSenderNeedURI(), WonMessageType
.CREATE_NEED));
}
//the message should not sign itself, just in case:
messageEventPlaceholders = messageEventPlaceholders
.stream()
.filter(mep -> !message.getMessageURI().equals(mep.getMessageURI()) && ! mep.getMessageURI().equals(message.getCorrespondingRemoteMessageURI()))
.collect(
Collectors.toList());
Dataset messageDataset = message.getCompleteDataset();
for (MessageEventPlaceholder messageEventPlaceholder: messageEventPlaceholders){
//generate signature references for them
WonMessage msgToLinkTo = loadWonMessageforURI(messageEventPlaceholder.getMessageURI());
SigningStage signingStage = new SigningStage(msgToLinkTo);
WonSignatureData wonSignatureData = signingStage.getOutermostSignature();
checkWellformedness(message, msgToLinkTo, wonSignatureData);
//add them to to outermost envelope in the current message
WonMessageSignerVerifier
.addSignature(wonSignatureData, message
.getOuterEnvelopeGraphURI().toString(), messageDataset, true);
message.addMessageProperty(WONMSG.HAS_PREVIOUS_MESSAGE_PROPERTY, msgToLinkTo.getMessageURI());
//update the message that now is referenced
//TODO: if at a later processing stage, the current message raises an error, this flag must be reset to false,
// otherwise the current message may end up not being persisted but the previous message that is referenced here
// will not be referenced by any subsequent messages.
messageEventPlaceholder.setReferencedByOtherMessage(true);
if (logger.isDebugEnabled()){
logger.debug("adding reference to message {} into message {} ", messageEventPlaceholder.getMessageURI(), message.getMessageURI());
}
}
//persist the message
messageEventRepository.save(messageEventPlaceholders);
return message;
}
public void checkWellformedness(final WonMessage message, final WonMessage msgToLinkTo, final WonSignatureData signatureReferences) {
//there must be exactly one unreferenced signature, otherwise msgToLinkTo is not well formed
if (signatureReferences == null){
throw new IllegalStateException(String.format("Message %s is not well formed: found no unreferenced " +
"signatures while trying to link to it from message %s",
msgToLinkTo.getMessageURI(), message.getMessageURI()));
}
}
private WonMessage loadWonMessageforURI(final URI messageURI) {
DatasetHolder datasetHolder = datasetHolderRepository.findOneByUriForUpdate(messageURI);
if (datasetHolder == null || datasetHolder.getDataset() == null){
throw new IllegalStateException( String.format("could not load dataset for message %s",messageURI));
}
return new WonMessage(datasetHolder.getDataset());
}
}