package won.protocol.message; import org.apache.jena.query.Dataset; import org.apache.jena.query.DatasetFactory; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Resource; import won.protocol.exception.WonMessageBuilderException; import won.protocol.util.CheapInsecureRandomString; import won.protocol.util.DefaultPrefixUtils; import won.protocol.util.RdfUtils; import won.protocol.util.WonRdfUtils; import won.protocol.vocabulary.RDFG; import won.protocol.vocabulary.WON; import won.protocol.vocabulary.WONMSG; import java.net.URI; import java.util.*; /** * Class to build a WonMessage based on the specific properties. * * @author Fabian Salcher */ public class WonMessageBuilder { public static final String CONTENT_URI_SUFFIX = "#content-"; public static final String SIGNATURE_URI_SUFFIX = "#signature-"; public static final String ENVELOPE_URI_SUFFIX = "#envelope-"; private static final CheapInsecureRandomString randomString = new CheapInsecureRandomString(); private static final int RANDOM_SUFFIX_LENGTH = 5; private final URI messageURI; private URI senderURI; private URI senderNeedURI; private URI senderNodeURI; private URI receiverURI; private URI receiverNeedURI; private URI receiverNodeURI; private WonMessageType wonMessageType; private WonMessageDirection wonMessageDirection; //a message may refer to a number of other messages (e.g. previously sent messages) private Set<URI> refersToURIs = new HashSet<>(); //if the message is a response message, it MUST have exactly one isResponseToMessageURI set. private URI isResponseToMessageURI; //if the message is a response message, and the original message has a correspondingRemoteMessage, set it here private URI isRemoteResponseToMessageURI; private WonMessageType isResponseToMessageType; //some message exist twice: once on the receiver's won node and once on the sender's. //this property allows to link to the corresponding remote message private URI correspondingRemoteMessageURI; private Map<URI, Model> contentMap = new HashMap<>(); private Map<URI, Model> signatureMap = new HashMap<>(); private WonMessage wrappedMessage; private WonMessage forwardedMessage; private Long sentTimestamp; private Long receivedTimestamp; public WonMessageBuilder(URI messageURI){ this.messageURI = messageURI; } private WonMessageBuilder(){ throw new UnsupportedOperationException("A messageURI must be provided when creating the WonMessageBuilder"); } public WonMessage build() throws WonMessageBuilderException { return build(DatasetFactory.createGeneral()); } /** * Builds a WonMessage by adding data to the specified dataset. * The dataset may be null or empty. * @param dataset * @return * @throws WonMessageBuilderException */ public WonMessage build(final Dataset dataset) throws WonMessageBuilderException { if (dataset == null) { throw new IllegalArgumentException("specified dataset must not be null. If a new dataset is to be created for the message, build() should be called."); } if (messageURI == null){ throw new WonMessageBuilderException("No messageURI specified"); } Model envelopeGraph = ModelFactory.createDefaultModel(); DefaultPrefixUtils.setDefaultPrefixes(envelopeGraph); //create a new envelope graph uri and add the envelope graph to the dataset //... and make sure that the graph URI will be new by also checking inside the wrapped message String envelopeGraphURI = RdfUtils.createNewGraphURI(messageURI.toString(), ENVELOPE_URI_SUFFIX,4, graphUri -> { if (dataset.containsNamedModel(graphUri)) return false; if (wrappedMessage == null) return true; if (wrappedMessage.getEnvelopeGraphURIs().contains(graphUri)) return false; if (wrappedMessage.getContentGraphURIs().contains(graphUri)) return false; return true; }).toString(); dataset.addNamedModel(envelopeGraphURI, envelopeGraph); // message URI Resource messageEventResource = envelopeGraph.createResource(messageURI.toString(), this.wonMessageDirection.getResource()); //the [envelopeGraphURI] rdf:type msg:EnvelopeGraph makes it easy to select graphs by type Resource envelopeGraphResource = envelopeGraph.createResource(envelopeGraphURI, WONMSG.ENVELOPE_GRAPH); envelopeGraphResource.addProperty(RDFG.SUBGRAPH_OF, messageEventResource); addWrappedOrForwardedMessage(dataset, envelopeGraph, envelopeGraphResource, messageURI); //make sure the envelope type has been set if (this.wonMessageDirection == null) { throw new IllegalStateException("envelopeType must be set!"); } if (wonMessageType != null) { messageEventResource.addProperty(WONMSG.HAS_MESSAGE_TYPE_PROPERTY, wonMessageType.getResource()); } messageEventResource.addLiteral(WONMSG.PROTOCOL_VERSION, envelopeGraph.createTypedLiteral("1.0")); // add sender if (senderURI != null) messageEventResource.addProperty( WONMSG.SENDER_PROPERTY, envelopeGraph.createResource(senderURI.toString())); if (senderNeedURI != null) messageEventResource.addProperty( WONMSG.SENDER_NEED_PROPERTY, envelopeGraph.createResource(senderNeedURI.toString())); if (senderNodeURI != null) messageEventResource.addProperty( WONMSG.SENDER_NODE_PROPERTY, envelopeGraph.createResource(senderNodeURI.toString())); // add receiver if (receiverURI != null) messageEventResource.addProperty( WONMSG.RECEIVER_PROPERTY, envelopeGraph.createResource(receiverURI.toString())); if (receiverNeedURI != null) messageEventResource.addProperty( WONMSG.RECEIVER_NEED_PROPERTY, envelopeGraph.createResource(receiverNeedURI.toString())); if (receiverNodeURI != null) messageEventResource.addProperty( WONMSG.RECEIVER_NODE_PROPERTY, envelopeGraph.createResource(receiverNodeURI.toString())); // add refersTo for (URI refersToURI : refersToURIs) { messageEventResource.addProperty( WONMSG.REFERS_TO_PROPERTY, envelopeGraph.createResource(refersToURI.toString())); } if (isResponseToMessageURI != null) { if (wonMessageType != WonMessageType.SUCCESS_RESPONSE && wonMessageType != WonMessageType.FAILURE_RESPONSE ){ throw new IllegalArgumentException("isResponseToMessageURI can only be used for SUCCESS_RESPONSE and " + "FAILURE_RESPONSE types"); } if (isResponseToMessageType == null){ throw new IllegalArgumentException("response messages must specify the type of message they are a response to" + ". Use setIsResponseToMessageType(type)"); } messageEventResource.addProperty( WONMSG.IS_RESPONSE_TO, envelopeGraph.createResource(isResponseToMessageURI.toString())); messageEventResource.addProperty( WONMSG.IS_RESPONSE_TO_MESSAGE_TYPE, this.isResponseToMessageType.getResource()); if (isRemoteResponseToMessageURI != null) { messageEventResource.addProperty( WONMSG.IS_REMOTE_RESPONSE_TO, envelopeGraph.createResource(isRemoteResponseToMessageURI.toString())); } } if (correspondingRemoteMessageURI != null) { messageEventResource.addProperty( WONMSG.HAS_CORRESPONDING_REMOTE_MESSAGE, envelopeGraph.createResource(correspondingRemoteMessageURI.toString())); } if (sentTimestamp != null) { messageEventResource.addProperty( WONMSG.HAS_SENT_TIMESTAMP, envelopeGraph.createTypedLiteral(this.sentTimestamp)); } if (receivedTimestamp != null) { messageEventResource.addProperty( WONMSG.HAS_RECEIVED_TIMESTAMP, envelopeGraph.createTypedLiteral(this.receivedTimestamp)); } for (URI contentURI : contentMap.keySet()) { String contentUriString = contentURI.toString(); dataset.addNamedModel(contentUriString, contentMap.get(contentURI)); messageEventResource.addProperty( WONMSG.HAS_CONTENT_PROPERTY, messageEventResource .getModel().createResource(contentUriString)); //add the [content-graph] rdfg:subGraphOf [message-uri] triple to the envelope envelopeGraph.createStatement(envelopeGraph.getResource(contentURI.toString()), RDFG.SUBGRAPH_OF, messageEventResource); Model signatureGraph = signatureMap.get(contentURI); if (signatureGraph != null) { throw new UnsupportedOperationException("signatures are not supported yet"); /* in principle, this should work, but its untested: uniqueContentUri = RdfUtils.createNewGraphURI(contentURI.toString(), SIGNATURE_URI_SUFFIX, 5, dataset).toString(); //the signature refers to the name of the other graph. We changed that name //so we have to replace the resource referencing it, too: signatureGraph = RdfUtils.replaceResource(signatureGraph.getResource(contentURI.toString()), signatureGraph.getResource(uniqueContentUri)); dataset.addNamedModel(uniqueContentUri, signatureGraph); */ } //now replace the content URIs } return new WonMessage(dataset); } public void addWrappedOrForwardedMessage(final Dataset dataset, final Model envelopeGraph, final Resource envelopeGraphResource, URI messageURI) { //add wrapped message first, including all its named graphs. //This way, we can later avoid clashed when generating new graph URIs if (this.wrappedMessage != null){ if (this.forwardedMessage != null) throw new IllegalStateException("cannot wrap and forward with the same " + "builder"); addAsContainedEnvelope(dataset, envelopeGraph, envelopeGraphResource, wrappedMessage, messageURI); } //add forwarded message next, including all its named graphs. //This way, we can later avoid clashed when generating new graph URIs if (this.forwardedMessage != null){ if (this.wrappedMessage != null) throw new IllegalStateException("cannot wrap and forward with the same " + "builder"); addAsContainedEnvelope(dataset, envelopeGraph, envelopeGraphResource, forwardedMessage, messageURI); } } public void addAsContainedEnvelope(final Dataset dataset, final Model envelopeGraph, final Resource envelopeGraphResource, WonMessage messageToAdd, URI messageURI) { String messageUriString = messageURI.toString(); //the [wrappedMessage.envelopeGraphURI] rdf:type msg:EnvelopeGraph triple in the default graph is required to // find the wrapped envelope graph. envelopeGraphResource.addProperty(WONMSG.CONTAINS_ENVELOPE, envelopeGraph.getResource(messageToAdd .getOuterEnvelopeGraphURI().toString())); //copy all named graphs to the new message dataset for (Iterator<String> names = messageToAdd.getCompleteDataset().listNames(); names.hasNext(); ){ String graphUri = names.next(); Model modelToAdd = messageToAdd.getCompleteDataset().getNamedModel(graphUri); dataset.addNamedModel(graphUri, modelToAdd); //define that the added graph is a subgraph of the message if that is not yet //expressed in the graph itself if (!modelToAdd .contains( modelToAdd.getResource(graphUri), RDFG.SUBGRAPH_OF, modelToAdd.getResource(messageUriString))){ envelopeGraph.createStatement(envelopeGraph.getResource(graphUri), RDFG.SUBGRAPH_OF, envelopeGraph.getResource(messageUriString)); } } } /** * Adds the complete message content to the message that will be built, * referencing toWrap's envelope in the envelope of the new message. * The message that will be built has the same messageURI as the wrapped message. * * @param * @return */ public static WonMessageBuilder wrap(WonMessage toWrap){ WonMessageBuilder builder = new WonMessageBuilder(toWrap.getMessageURI()) .setWonMessageDirection(toWrap.getEnvelopeType()); //make a copy to avoid modification in current message in case wrapped message //is modified externally builder.wrappedMessage = WonRdfUtils.MessageUtils.copyByDatasetSerialization(toWrap); return builder; } // complete MessageType specific setters public static WonMessageBuilder setMessagePropertiesForOpen( URI messageURI, URI localConnection, URI localNeed, URI localWonNode, URI remoteConnection, URI remoteNeed, URI remoteWonNode, String welcomeMessage) { return new WonMessageBuilder(messageURI) .setWonMessageDirection(WonMessageDirection.FROM_OWNER) .setWonMessageType(WonMessageType.OPEN) .setSenderURI(localConnection) .setSenderNeedURI(localNeed) .setSenderNodeURI(localWonNode) .setReceiverURI(remoteConnection) .setReceiverNeedURI(remoteNeed) .setReceiverNodeURI(remoteWonNode) .setTextMessage(welcomeMessage) .setSentTimestampToNow(); } public static WonMessageBuilder setMessagePropertiesForClose( URI messageURI, URI localConnection, URI localNeed, URI localWonNode, URI remoteConnection, URI remoteNeed, URI remoteWonNode, String farewellMessage) { return setMessagePropertiesForClose(messageURI, WonMessageDirection.FROM_OWNER, localConnection, localNeed, localWonNode, remoteConnection, remoteNeed, remoteWonNode, farewellMessage); } public static WonMessageBuilder setMessagePropertiesForClose( URI messageURI, WonMessageDirection direction, URI localConnection, URI localNeed, URI localWonNode, URI remoteConnection, URI remoteNeed, URI remoteWonNode, String farewellMessage) { return new WonMessageBuilder(messageURI) .setWonMessageDirection(direction) .setWonMessageType(WonMessageType.CLOSE) .setSenderURI(localConnection) .setSenderNeedURI(localNeed) .setSenderNodeURI(localWonNode) .setReceiverURI(remoteConnection) .setReceiverNeedURI(remoteNeed) .setReceiverNodeURI(remoteWonNode) .setTextMessage(farewellMessage) .setSentTimestampToNow(); } public static WonMessageBuilder setMessagePropertiesForClose( URI messageURI, URI localConnection, URI localNeed, URI localWonNode, String farewellMessage) { return setMessagePropertiesForClose(messageURI, WonMessageDirection.FROM_OWNER, localConnection, localNeed, localWonNode, farewellMessage); } public static WonMessageBuilder setMessagePropertiesForClose( URI messageURI, WonMessageDirection direction, URI localConnection, URI localNeed, URI localWonNode, String farewellMessage) { return setMessagePropertiesForClose(messageURI, WonMessageDirection.FROM_OWNER, localConnection, localNeed, localWonNode, localConnection, localNeed, localWonNode, farewellMessage); } /** * Sets the MessageProperties for Closing open connections (happens when the need is closed and the system is closing * all the corresponding connections when no connection is present from the remoteNeed * @param messageURI * @param localConnection * @param localNeed * @param localWonNode * @return */ public static WonMessageBuilder setMessagePropertiesForLocalOnlyClose( URI messageURI, URI localConnection, URI localNeed, URI localWonNode ) { return new WonMessageBuilder(messageURI) .setWonMessageDirection(WonMessageDirection.FROM_SYSTEM) .setWonMessageType(WonMessageType.CLOSE) .setSenderURI(localConnection) .setSenderNeedURI(localNeed) .setSenderNodeURI(localWonNode) .setSentTimestampToNow(); } public static WonMessageBuilder setMessagePropertiesForDeactivate( URI messageURI, URI localNeed, URI localWonNode) { return new WonMessageBuilder(messageURI) .setWonMessageDirection(WonMessageDirection.FROM_OWNER) .setWonMessageType(WonMessageType.DEACTIVATE) .setSenderNeedURI(localNeed) .setSenderNodeURI(localWonNode) .setReceiverNeedURI(localNeed) .setReceiverNodeURI(localWonNode) .setSentTimestampToNow(); } public static WonMessageBuilder setMessagePropertiesForConnect( URI messageURI, URI localFacet, URI localNeed, URI localWonNode, URI remoteFacet, URI remoteNeed, URI remoteWonNode, String welcomeMessage) { // create facet model Model facetModel = WonRdfUtils.FacetUtils.createFacetModelForHintOrConnect(localFacet, remoteFacet); RdfUtils.replaceBaseResource(facetModel, facetModel.createResource(messageURI.toString())); if (welcomeMessage != null){ WonRdfUtils.MessageUtils.addMessage(facetModel, welcomeMessage); } return new WonMessageBuilder(messageURI) .setWonMessageDirection(WonMessageDirection.FROM_OWNER) .setWonMessageType(WonMessageType.CONNECT) .setSenderNeedURI(localNeed) .setSenderNodeURI(localWonNode) .setReceiverNeedURI(remoteNeed) .setReceiverNodeURI(remoteWonNode) .addContent(facetModel, null) .setSentTimestampToNow(); } public static WonMessageBuilder setMessagePropertiesForCreate( URI messageURI, URI needURI, URI wonNodeURI) { return new WonMessageBuilder(messageURI) .setWonMessageDirection(WonMessageDirection.FROM_OWNER) .setWonMessageType(WonMessageType.CREATE_NEED) .setSenderNeedURI(needURI) .setSenderNodeURI(wonNodeURI) .setReceiverNodeURI(wonNodeURI) .setSentTimestampToNow(); } public static WonMessageBuilder setMessagePropertiesForHint( URI messageURI, URI needURI, URI needFacetURI, URI wonNodeURI, URI otherNeedURI, URI otherNeedFacet, URI matcherURI, double score) { Model contentModel = WonRdfUtils.FacetUtils.createFacetModelForHintOrConnect(needFacetURI, otherNeedFacet); Resource msgResource = contentModel.createResource(messageURI.toString()); RdfUtils.replaceBaseResource(contentModel, msgResource); contentModel.add(msgResource, WON.HAS_MATCH_SCORE, contentModel.createTypedLiteral(score)); contentModel.add(msgResource, WON.HAS_MATCH_COUNTERPART, contentModel.createResource(otherNeedURI.toString())); return new WonMessageBuilder(messageURI) .setWonMessageDirection(WonMessageDirection.FROM_EXTERNAL) .setWonMessageType(WonMessageType.HINT_MESSAGE) .setSenderNodeURI(matcherURI) .setReceiverNeedURI(needURI) .setReceiverNodeURI(wonNodeURI) .setSentTimestampToNow() .addContent(contentModel, null); } public static WonMessageBuilder setMessagePropertiesForHintFeedback( URI messageURI, URI connectionURI, URI needURI, URI wonNodeURI, boolean booleanFeedbackValue) { Model contentModel = WonRdfUtils.MessageUtils.binaryFeedbackMessage(connectionURI, booleanFeedbackValue); Resource msgResource = contentModel.createResource(messageURI.toString()); RdfUtils.replaceBaseResource(contentModel, msgResource); return new WonMessageBuilder(messageURI) .setWonMessageDirection(WonMessageDirection.FROM_OWNER) .setWonMessageType(WonMessageType.HINT_FEEDBACK_MESSAGE) .setReceiverNodeURI(wonNodeURI) .setReceiverURI(connectionURI) .setReceiverNeedURI(needURI) .setSenderNodeURI(wonNodeURI) .setSenderNeedURI(needURI) .setSenderURI(connectionURI) .setSentTimestampToNow() .addContent(contentModel, null); } public static WonMessageBuilder setMessagePropertiesForHintNotification( URI messageURI, URI needURI, URI needFacetURI, URI needConnectionURI, URI wonNodeURI, URI otherNeedURI, URI otherNeedFacet, URI matcherURI, double score) { Model contentModel = WonRdfUtils.FacetUtils.createFacetModelForHintOrConnect(needFacetURI, otherNeedFacet); Resource msgResource = contentModel.createResource(messageURI.toString()); RdfUtils.replaceBaseResource(contentModel, msgResource); contentModel.add(msgResource, WON.HAS_MATCH_SCORE, contentModel.createTypedLiteral(score)); contentModel.add(msgResource, WON.HAS_MATCH_COUNTERPART, contentModel.createResource(otherNeedURI.toString())); return new WonMessageBuilder(messageURI) .setWonMessageDirection(WonMessageDirection.FROM_EXTERNAL) .setWonMessageType(WonMessageType.HINT_MESSAGE) .setSenderNodeURI(matcherURI) .setReceiverURI(needConnectionURI) .setReceiverNeedURI(needURI) .setReceiverNodeURI(wonNodeURI) .addContent(contentModel, null) .setSentTimestampToNow(); } public static WonMessageBuilder setMessagePropertiesForConnectionMessage( URI messageURI, URI localConnection, URI localNeed, URI localWonNode, URI remoteConnection, URI remoteNeed, URI remoteWonNode, Model content) { return new WonMessageBuilder(messageURI) .setWonMessageDirection(WonMessageDirection.FROM_OWNER) .setWonMessageType(WonMessageType.CONNECTION_MESSAGE) .setSenderURI(localConnection) .setSenderNeedURI(localNeed) .setSenderNodeURI(localWonNode) .setReceiverURI(remoteConnection) .setReceiverNeedURI(remoteNeed) .setReceiverNodeURI(remoteWonNode) .addContent(content, null) .setSentTimestampToNow(); } public static WonMessageBuilder setMessagePropertiesForConnectionMessage( URI messageURI, URI localConnection, URI localNeed, URI localWonNode, URI remoteConnection, URI remoteNeed, URI remoteWonNode, String textMessage) { return new WonMessageBuilder(messageURI) .setWonMessageDirection(WonMessageDirection.FROM_OWNER) .setWonMessageType(WonMessageType.CONNECTION_MESSAGE) .setSenderURI(localConnection) .setSenderNeedURI(localNeed) .setSenderNodeURI(localWonNode) .setReceiverURI(remoteConnection) .setReceiverNeedURI(remoteNeed) .setReceiverNodeURI(remoteWonNode) .setTextMessage(textMessage) .setSentTimestampToNow(); } public static WonMessageBuilder setMessagePropertiesForNeedCreatedNotification( URI messageURI, URI localNeed, URI localWonNode) { return new WonMessageBuilder(messageURI) .setWonMessageType(WonMessageType.NEED_CREATED_NOTIFICATION) .setWonMessageDirection(WonMessageDirection.FROM_EXTERNAL) .setSenderNeedURI(localNeed) .setSenderNodeURI(localWonNode) .setSentTimestampToNow(); } public static WonMessageBuilder setPropertiesForPassingMessageToRemoteNode(final WonMessage ownerOrSystemMsg, URI newMessageUri){ return setPropertiesForPassingMessageToRemoteNodeAndCopyLocalMessage(ownerOrSystemMsg, newMessageUri); } @Deprecated /** * The message to remote node will contain envelope with properties extracted from corresponding * local message, as well as copy of the local message content with replaced uris (remote message uris). * The method should no longer be used since introducing signatures, which led to preserving * exact copy of local envelopes and local contents in remote message (no uris are replaced) * */ private static WonMessageBuilder setPropertiesForPassingMessageToRemoteNodeAndCopyLocalMessageReplacingUris(final WonMessage ownerOrSystemMsg, URI newMessageUri){ return new WonMessageBuilder(newMessageUri) .setSentTimestamp(new Date().getTime()) .copyContentFromMessageReplacingMessageURI(ownerOrSystemMsg) .copyEnvelopeFromWonMessage(ownerOrSystemMsg) .setCorrespondingRemoteMessageURI(ownerOrSystemMsg.getMessageURI()) .setWonMessageDirection(WonMessageDirection.FROM_EXTERNAL); } /** * Adds the complete message content to the message that will be built, * referencing toForward's envelope in the envelope of the new message. * * @param * @return */ private WonMessageBuilder forward(WonMessage toForward){ //make a copy to avoid modification in current message in case wrapped message //is modified externally this.forwardedMessage = WonRdfUtils.MessageUtils.copyByDatasetSerialization(toForward); return this; } /** * The message to remote node will contain envelope with sentTimestamp, remoteMessageUri, direction, * as well as exact copy of the local message envelopes and contents. * * @param ownerOrSystemMsg * @param newMessageUri * @return */ private static WonMessageBuilder setPropertiesForPassingMessageToRemoteNodeAndCopyLocalMessage(final WonMessage ownerOrSystemMsg, URI newMessageUri){ return new WonMessageBuilder(newMessageUri) .setSentTimestamp(new Date().getTime()) .forward(ownerOrSystemMsg) // copy .setCorrespondingRemoteMessageURI(ownerOrSystemMsg.getMessageURI()) .setWonMessageDirection(WonMessageDirection.FROM_EXTERNAL); } @Deprecated public static WonMessageBuilder setPropertiesForPassingMessageToOwner(final WonMessage externalMsg){ return WonMessageBuilder.wrap(externalMsg) .setReceivedTimestamp(new Date().getTime()); } public static WonMessageBuilder setPropertiesForNodeResponse(WonMessage originalMessage, boolean isSuccess, URI messageURI){ WonMessageBuilder messageBuilder = new WonMessageBuilder(messageURI) .setWonMessageType(isSuccess? WonMessageType.SUCCESS_RESPONSE : WonMessageType .FAILURE_RESPONSE); WonMessageDirection origDirection = originalMessage.getEnvelopeType(); if (WonMessageDirection.FROM_EXTERNAL == origDirection){ //if the message is an external message, the original receiver becomes //the sender of the response. messageBuilder .setSenderNodeURI(originalMessage.getReceiverNodeURI()) .setSenderNeedURI(originalMessage.getReceiverNeedURI()) .setSenderURI(originalMessage.getReceiverURI()) .setIsRemoteResponseToMessageURI(originalMessage.getCorrespondingRemoteMessageURI()); } else if (WonMessageDirection.FROM_OWNER == origDirection|| WonMessageDirection.FROM_SYSTEM == origDirection ){ //if the message comes from the owner, the original sender is also //the sender of the response messageBuilder .setSenderNodeURI(originalMessage.getSenderNodeURI()) .setSenderNeedURI(originalMessage.getSenderNeedURI()) .setSenderURI(originalMessage.getSenderURI()); } messageBuilder .setReceiverNeedURI(originalMessage.getSenderNeedURI()) .setReceiverNodeURI(originalMessage.getSenderNodeURI()) .setReceiverURI(originalMessage.getSenderURI()) .setIsResponseToMessageURI(originalMessage.getMessageURI()) .setIsResponseToMessageType(originalMessage.getMessageType()) .setWonMessageDirection(WonMessageDirection.FROM_SYSTEM); return messageBuilder; } public WonMessageBuilder setSenderURI(URI senderURI) { this.senderURI = senderURI; return this; } public WonMessageBuilder setSenderNeedURI(URI senderNeedURI) { this.senderNeedURI = senderNeedURI; return this; } public WonMessageBuilder setSenderNodeURI(URI senderNodeURI) { this.senderNodeURI = senderNodeURI; return this; } public WonMessageBuilder setReceiverURI(URI receiverURI) { this.receiverURI = receiverURI; return this; } public WonMessageBuilder setReceiverNeedURI(URI receiverNeedURI) { this.receiverNeedURI = receiverNeedURI; return this; } public WonMessageBuilder setReceiverNodeURI(URI receiverNodeURI) { this.receiverNodeURI = receiverNodeURI; return this; } public WonMessageBuilder setWonMessageType(WonMessageType wonMessageType) { this.wonMessageType = wonMessageType; return this; } public WonMessageBuilder setWonMessageDirection(WonMessageDirection wonMessageDirection){ this.wonMessageDirection = wonMessageDirection; return this; } /** * Adds the specified content graph, and the specified signature graph, using the specified * contentURI as the graph name. The contentURI will be made unique inside the message dataset * by appending characters at the end. * @param content * @return */ public WonMessageBuilder addContent(Model content, Model signature) { URI contentGraphUri = RdfUtils.createNewGraphURI(messageURI.toString(), CONTENT_URI_SUFFIX, 4, new RdfUtils.GraphNameCheck() { @Override public boolean isGraphUriOk(final String graphUri) { return !contentMap.keySet().contains(URI.create(graphUri)); } }); contentMap.put(contentGraphUri, content); if (signature != null) signatureMap.put(contentGraphUri, signature); return this; } /** * Retrieves one of the possibly multiple Models that does not have a signature yet. If there is none * (all are signed or none is found at all), a new model is created, added to the internal contentMap * and returned here. * */ public Model getUnsignedContentGraph(){ if (contentMap.isEmpty()){ //no content graphs yet. Make one and return it. Model contentGraph = ModelFactory.createDefaultModel(); RdfUtils.replaceBaseURI(contentGraph, this.messageURI.toString()); addContent(contentGraph, null); return contentGraph; } //content map is not empty. find one without a signature: for (Map.Entry<URI, Model> entry: contentMap.entrySet()){ if (!signatureMap.containsKey(entry.getKey())) return entry.getValue(); } //all content graphs are signed. add a new one. Model contentGraph = ModelFactory.createDefaultModel(); RdfUtils.replaceBaseURI(contentGraph, this.messageURI.toString()); addContent(contentGraph, null); return contentGraph; } public WonMessageBuilder addRefersToURI(URI refersTo) { refersToURIs.add(refersTo); return this; } public WonMessageBuilder setIsResponseToMessageURI(URI isResponseToMessageURI){ this.isResponseToMessageURI = isResponseToMessageURI; return this; } public WonMessageBuilder setIsRemoteResponseToMessageURI(final URI isRemoteResponseToMessageURI) { this.isRemoteResponseToMessageURI = isRemoteResponseToMessageURI; return this; } public WonMessageBuilder setIsResponseToMessageType(final WonMessageType isResponseToMessageType) { this.isResponseToMessageType = isResponseToMessageType; return this; } public WonMessageBuilder setCorrespondingRemoteMessageURI(URI correspondingRemoteMessageURI){ this.correspondingRemoteMessageURI = correspondingRemoteMessageURI; return this; } public WonMessageBuilder setSentTimestamp(final long sentTimestamp) { this.sentTimestamp = sentTimestamp; return this; } public WonMessageBuilder setReceivedTimestamp(Long receivedTimestamp) { this.receivedTimestamp = receivedTimestamp; return this; } public WonMessageBuilder setSentTimestampToNow() { this.sentTimestamp = System.currentTimeMillis(); return this; } public WonMessageBuilder setReceivedTimestampToNow() { this.receivedTimestamp = System.currentTimeMillis(); return this; } /** * Adds a won:hasTextMessage triple to one of the unsigned content graphs in this builder. Creates a new * unsigned content graph if none is found. * @param textMessage may be null in which case the builder is not modified * @return */ public WonMessageBuilder setTextMessage(String textMessage){ if (textMessage != null) { WonRdfUtils.MessageUtils.addMessage(getUnsignedContentGraph(), textMessage); } return this; } /** * Copies the envelope properties from the specified message to this message. * * Note that this does not copy the original envelope graph, only the * standard envelope properties. * * @param wonMessage * @return */ public static WonMessageBuilder copyEnvelopeFromWonMessage(final WonMessage wonMessage) { WonMessageBuilder builder = new WonMessageBuilder(wonMessage.getMessageURI()) .setWonMessageType(wonMessage.getMessageType()) .setReceiverURI(wonMessage.getReceiverURI()) .setReceiverNeedURI(wonMessage.getReceiverNeedURI()) .setReceiverNodeURI(wonMessage.getReceiverNodeURI()) .setSenderURI(wonMessage.getSenderURI()) .setSenderNeedURI(wonMessage.getSenderNeedURI()) .setSenderNodeURI(wonMessage.getSenderNodeURI()); if (wonMessage.getIsResponseToMessageType() != null){ builder.setIsResponseToMessageType(wonMessage.getIsResponseToMessageType()); } if (wonMessage.getIsResponseToMessageURI() != null){ builder.setIsResponseToMessageURI(wonMessage.getIsResponseToMessageURI()); } if (wonMessage.getIsRemoteResponseToMessageURI() != null){ builder.setIsRemoteResponseToMessageURI(wonMessage.getIsRemoteResponseToMessageURI()); } return builder; } /** * Copies all content graphs from the specified message, replacing all occurrences * of the specified message's URI with the messageURI of this builder. * @param wonMessage * @return */ public WonMessageBuilder copyContentFromMessageReplacingMessageURI(final WonMessage wonMessage) { return copyContentFromMessage(wonMessage, true); } /** * Copies all content graphs from the specified message to this builder. * * @param wonMessage * @return */ public WonMessageBuilder copyContentFromMessage(final WonMessage wonMessage) { return copyContentFromMessage(wonMessage, false); } /** * Copies all content graphs from the specified message to this builder. * * If replaceMessageUri is true, replaces all occurrences * of the specified message's URI with the messageURI of this builder. * * @param wonMessage * @return */ public WonMessageBuilder copyContentFromMessage(final WonMessage wonMessage, boolean replaceMessageUri) { Dataset messageContent = wonMessage.getMessageContent(); for (Iterator<String> nameIt = messageContent.listNames(); nameIt.hasNext(); ){ String modelUri = nameIt.next(); Model model = messageContent.getNamedModel(modelUri); String otherMessageUri = wonMessage.getMessageURI().toString(); if (replaceMessageUri) { //replace the messageURI of the specified message with that of this builder, just in case //there are triples in the model referring to it model = RdfUtils.replaceResource(model.getResource(otherMessageUri), model.getResource(this.messageURI.toString())); } //change the model name: replace the message uri of the specified message with our uri //we have to do that in any case as the content graph's URI must be one within the //'URI space' of the message String newModelUri = this.messageURI.toString()+"/copied"; addContent(model,null); } return this; } public static WonMessage forwardReceivedNodeToNodeMessageAsNodeToNodeMessage(final URI newMessageUri, final WonMessage wonMessage, final URI connectionURI, final URI needURI, final URI wonNodeUri, final URI remoteConnectionURI, final URI remoteNeedURI, final URI remoteWonNodeUri) { WonMessageBuilder builder = new WonMessageBuilder(newMessageUri) .setWonMessageType(wonMessage.getMessageType()) .forward(wonMessage) .copyContentFromMessageReplacingMessageURI(wonMessage) .setSentTimestamp(System.currentTimeMillis()) .setReceiverURI(remoteConnectionURI) .setReceiverNeedURI(remoteNeedURI) .setReceiverNodeURI(remoteWonNodeUri) .setIsRemoteResponseToMessageURI(wonMessage.getIsRemoteResponseToMessageURI()) .setIsResponseToMessageURI(wonMessage.getIsResponseToMessageURI()) .setIsResponseToMessageType(wonMessage.getIsResponseToMessageType()) .setWonMessageDirection(WonMessageDirection.FROM_EXTERNAL); return builder.build(); } }