package won.node.facet.impl; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Resource; import org.apache.jena.rdf.model.StmtIterator; import org.apache.jena.riot.Lang; import org.apache.jena.riot.RDFDataMgr; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import won.node.service.DataAccessService; import won.protocol.exception.*; import won.protocol.message.WonMessage; import won.protocol.model.Connection; import won.protocol.model.Need; import won.protocol.model.NeedState; import won.protocol.repository.DatasetHolderRepository; import won.protocol.util.RdfUtils; import won.protocol.vocabulary.WON; import java.io.StringWriter; import java.net.URI; import java.util.concurrent.ExecutorService; /** * User: Danijel * Date: 4.6.14. */ public abstract class AbstractBAFacet implements Facet { private final Logger logger = LoggerFactory.getLogger(getClass()); protected won.node.service.impl.URIService URIService; protected ExecutorService executorService; protected DataAccessService dataService; @Autowired protected DatasetHolderRepository datasetHolderRepository; /** * * This function is invoked when an owner sends an open message to a won node and usually executes registered facet specific code. * It is used to open a connection which is identified by the connection object con. A rdf graph can be sent along with the request. * * @param con the connection object * @param content a rdf graph describing properties of the event. The null releative URI ('<>') inside that graph, * as well as the base URI of the graph will be attached to the resource identifying the event. * @throws won.protocol.exception.NoSuchConnectionException if connectionURI does not refer to an existing connection * @throws won.protocol.exception.IllegalMessageForConnectionStateException if the message is not allowed in the current state of the connection */ @Override public void openFromOwner(final Connection con, final Model content, final WonMessage wonMessage) throws NoSuchConnectionException, IllegalMessageForConnectionStateException { //inform the other side if (con.getRemoteConnectionURI() != null) { executorService.execute(new Runnable() { @Override public void run() { // try { // needFacingConnectionClient.open(con, content, wonMessage); // } catch (Exception e) { // logger.warn("caught Exception in openFromOwner",e); // } } }); } } /** * * This function is invoked when an owner sends a close message to a won node and usually executes registered facet specific code. *It is used to close a connection which is identified by the connection object con. A rdf graph can be sent along with the request. * * @param con the connection object * @param content a rdf graph describing properties of the event. The null releative URI ('<>') inside that graph, * as well as the base URI of the graph will be attached to the resource identifying the event. * @throws NoSuchConnectionException if connectionURI does not refer to an existing connection * @throws IllegalMessageForConnectionStateException if the message is not allowed in the current state of the connection */ @Override public void closeFromOwner(final Connection con, final Model content, final WonMessage wonMessage) throws NoSuchConnectionException, IllegalMessageForConnectionStateException { //inform the other side if (con.getRemoteConnectionURI() != null) { executorService.execute(new Runnable() { @Override public void run() { // try { // needFacingConnectionClient.close(con, content, wonMessage); // } catch (Exception e) { // logger.warn("caught Exception in closeFromOwner: ",e); // } } }); } } /** * * This function is invoked when an won node sends an open message to another won node and usually executes registered facet specific code. * It is used to open a connection which is identified by the connection object con. A rdf graph can be sent along with the request. * * @param con the connection object * @param content a rdf graph describing properties of the event. The null releative URI ('<>') inside that graph, * as well as the base URI of the graph will be attached to the resource identifying the event. * @throws NoSuchConnectionException if connectionURI does not refer to an existing connection * @throws IllegalMessageForConnectionStateException if the message is not allowed in the current state of the connection */ @Override public void openFromNeed(final Connection con, final Model content, final WonMessage wonMessage) throws NoSuchConnectionException, IllegalMessageForConnectionStateException { //inform the need side executorService.execute(new Runnable() { @Override public void run() { try { //ownerFacingConnectionClient.open(con.getConnectionURI(), content, wonMessage); } catch (Exception e) { logger.warn("caught Exception in openFromNeed:", e); } } }); } /** * * This function is invoked when an won node sends a close message to another won node and usually executes registered facet specific code. * It is used to close a connection which is identified by the connection object con. A rdf graph can be sent along with the request. * * @param con the connection object * @param content a rdf graph describing properties of the event. The null releative URI ('<>') inside that graph, * as well as the base URI of the graph will be attached to the resource identifying the event. * @throws NoSuchConnectionException if connectionURI does not refer to an existing connection * @throws IllegalMessageForConnectionStateException if the message is not allowed in the current state of the connection */ @Override public void closeFromNeed(final Connection con, final Model content, final WonMessage wonMessage) throws NoSuchConnectionException, IllegalMessageForConnectionStateException { //inform the need side executorService.execute(new Runnable() { @Override public void run() { try { //ownerFacingConnectionClient.close(con.getConnectionURI(), content, wonMessage); } catch (Exception e) { logger.warn("caught Exception in closeFromNeed:", e); } } }); } public void storeBAStateForConnection(Connection con, URI stateUri) { Model connectionBAStateContent = ModelFactory.createDefaultModel(); connectionBAStateContent.setNsPrefix("",con.getConnectionURI().toString()); Resource baseResource = connectionBAStateContent.createResource(con.getConnectionURI().toString()); baseResource.addProperty(WON_TX.BA_STATE, connectionBAStateContent.createResource(stateUri.toString())); logger.debug("linked data:"+ RdfUtils.toString(connectionBAStateContent)); con.getDatasetHolder().getDataset().setDefaultModel(connectionBAStateContent); datasetHolderRepository.save(con.getDatasetHolder()); } /** * This function is invoked when a matcher sends a hint message to a won node and * usually executes registered facet specific code. * It notifies the need of a matching otherNeed with the specified match score. Originator * identifies the entity making the call. Normally, originator is a matching service. * A rdf graph can be sent along with the request. * * @param con the connection object * @param score match score between 0.0 (bad) and 1.0 (good). Implementations treat lower values as 0.0 and higher values as 1.0. * @param originator an URI identifying the calling entity * @param content (optional) an optional RDF graph containing more detailed information about the hint. The null releative URI ('<>') inside that graph, * as well as the base URI of the graph will be attached to the resource identifying the match event. * @throws won.protocol.exception.NoSuchNeedException * if needURI is not a known need URI * @throws won.protocol.exception.IllegalMessageForNeedStateException * if the need is not active */ @Override public void hint( final Connection con, final double score, final URI originator, final Model content, final WonMessage wonMessage) throws NoSuchNeedException, IllegalMessageForNeedStateException { final Model remoteFacetModel = changeHasRemoteFacetToHasFacet(content); executorService.execute(new Runnable() { @Override public void run() { //here, we don't really need to handle exceptions, as we don't want to flood matching services with error messages // try { //// ownerProtocolOwnerService.hint( //// con.getNeedURI(), //// con.getRemoteNeedURI(), //// score, //// originator, //// remoteFacetModel, //// wonMessage); // } catch (NoSuchNeedException e) { // logger.warn("error sending hint message to owner - no such need:", e); // } catch (IllegalMessageForNeedStateException e) { // logger.warn("error sending hint content to owner - illegal need state:", e); // } catch (Exception e) { // logger.warn("error sending hint content to owner:", e); // } } }); } /** * * This function is invoked when an won node sends an connect message to another won node and usually executes registered facet specific code. * The connection is identified by the connection object con. A rdf graph can be sent along with the request. * * @param con the connection object * @param content a rdf graph describing properties of the event. The null releative URI ('<>') inside that graph, * as well as the base URI of the graph will be attached to the resource identifying the event. * @throws NoSuchConnectionException if connectionURI does not refer to an existing connection * @throws IllegalMessageForConnectionStateException if the message is not allowed in the current state of the connection */ @Override public void connectFromNeed(final Connection con, final Model content, final WonMessage wonMessage) throws NoSuchNeedException, IllegalMessageForNeedStateException, ConnectionAlreadyExistsException { final Connection connectionForRunnable = con; executorService.execute(new Runnable() { @Override public void run() { // try { // ownerProtocolOwnerService.connect( // con.getNeedURI(), // con.getRemoteNeedURI(), // connectionForRunnable.getConnectionURI(), // content, // wonMessage); // } catch (WonProtocolException e) { // // we can't connect the connection. we send a deny back to the owner // // TODO should we introduce a new protocol method connectionFailed (because it's not an owner deny but some protocol-level error)? // // For now, we call the close method as if it had been called from the owner side // // TODO: even with this workaround, it would be good to send a content along with the close (so we can explain what happened). // logger.warn("could not connectFromNeed, sending close back. Exception was: ",e); //// try { //// // ToDo (FS): in this case a close wonMessage should be created and send instead!! //// ownerFacingConnectionCommunicationService.close( //// connectionForRunnable.getConnectionURI(), //// content, //// wonMessage); //// } catch (Exception e1) { //// logger.warn("caught Exception sending close back from connectFromNeed:", e1); //// } // } } }); } /** * * This function is invoked when an owner sends an open message to the won node and usually executes registered facet specific code. * The connection is identified by the connection object con. A rdf graph can be sent along with the request. * * @param con the connection object * @param content a rdf graph describing properties of the event. The null releative URI ('<>') inside that graph, * as well as the base URI of the graph will be attached to the resource identifying the event. * @throws NoSuchConnectionException if connectionURI does not refer to an existing connection * @throws IllegalMessageForConnectionStateException if the message is not allowed in the current state of the connection */ @Override public void connectFromOwner(final Connection con, final Model content, final WonMessage wonMessage) throws NoSuchNeedException, IllegalMessageForNeedStateException, ConnectionAlreadyExistsException { final Model remoteFacetModel = changeHasRemoteFacetToHasFacet(content); final Connection connectionForRunnable = con; //send to need executorService.execute(new Runnable() { @Override public void run() { // try { // ListenableFuture<URI> remoteConnectionURI = needProtocolNeedService.connect( // con.getRemoteNeedURI(), // con.getNeedURI(), // connectionForRunnable.getConnectionURI(), // remoteFacetModel, // wonMessage); // dataService.updateRemoteConnectionURI(con, remoteConnectionURI.get()); // // } catch (WonProtocolException e) { // // we can't connect the connection. we send a close back to the owner // // TODO should we introduce a new protocol method connectionFailed (because it's not an owner deny but some protocol-level error)? // // For now, we call the close method as if it had been called from the remote side // // TODO: even with this workaround, it would be good to send a content along with the close (so we can explain what happened). // logger.warn("could not connectFromOwner, sending close back. Exception was: ",e); //// try { //// needFacingConnectionCommunicationService.close( //// connectionForRunnable.getConnectionURI(), //// content, //// wonMessage); //// } catch (Exception e1) { //// logger.warn("caught Exception sending close back from connectFromOwner::", e1); //// } // } catch (Exception e) { // logger.warn("caught Exception in connectFromOwner: ",e); // } } }); } /** * Creates a copy of the specified model, replacing won:hasRemoteFacet by won:hasFacet and vice versa. * @param model * @return */ private Model changeHasRemoteFacetToHasFacet(Model model) { Resource baseRes = model.getResource(model.getNsPrefixURI("")); StmtIterator stmtIterator = baseRes.listProperties(WON.HAS_REMOTE_FACET); if (!stmtIterator.hasNext()) throw new IllegalArgumentException("at least one facet must be specified with won:hasRemoteFacet"); final Model newModel = ModelFactory.createDefaultModel(); newModel.setNsPrefix("", model.getNsPrefixURI("")); newModel.add(model); newModel.removeAll(null, WON.HAS_REMOTE_FACET, null); newModel.removeAll(null, WON.HAS_FACET, null); Resource newBaseRes = newModel.createResource(newModel.getNsPrefixURI("")); //replace won:hasFacet while (stmtIterator.hasNext()) { Resource facet = stmtIterator.nextStatement().getObject().asResource(); newBaseRes.addProperty(WON.HAS_FACET, facet); } //replace won:hasRemoteFacet stmtIterator = baseRes.listProperties(WON.HAS_FACET); if (stmtIterator != null) { while (stmtIterator.hasNext()) { Resource facet = stmtIterator.nextStatement().getObject().asResource(); newBaseRes.addProperty(WON.HAS_REMOTE_FACET, facet); } } if (logger.isDebugEnabled()){ StringWriter modelAsString = new StringWriter(); RDFDataMgr.write(modelAsString, model, Lang.TTL); StringWriter newModelAsString = new StringWriter(); RDFDataMgr.write(newModelAsString, model, Lang.TTL); logger.debug("changed hasRemoteFacet to hasFacet. Old: \n{},\n new: \n{}",modelAsString.toString(), newModelAsString.toString()); } return newModel; } private boolean isNeedActive(final Need need) { return NeedState.ACTIVE == need.getState(); } public void setDataService(DataAccessService dataService) { this.dataService = dataService; } public void setExecutorService(ExecutorService executorService) { this.executorService = executorService; } public void setURIService(won.node.service.impl.URIService URIService) { this.URIService = URIService; } }