package ru.semiot.services.analyzing.cep; import com.hp.hpl.jena.datatypes.RDFDatatype; import com.hp.hpl.jena.graph.Node; import com.hp.hpl.jena.graph.NodeFactory; import com.hp.hpl.jena.graph.Triple; import com.hp.hpl.jena.query.QueryExecution; import com.hp.hpl.jena.query.QueryExecutionFactory; import com.hp.hpl.jena.query.ResultSet; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.ModelFactory; import com.hp.hpl.jena.rdf.model.Statement; import com.hp.hpl.jena.rdf.model.StmtIterator; import com.hp.hpl.jena.sparql.core.Var; import com.hp.hpl.jena.sparql.engine.binding.Binding; import com.hp.hpl.jena.sparql.engine.binding.BindingFactory; import com.hp.hpl.jena.sparql.engine.binding.BindingMap; import eu.larkc.csparql.cep.api.RdfQuadruple; import eu.larkc.csparql.cep.api.RdfStream; import eu.larkc.csparql.common.RDFTable; import eu.larkc.csparql.core.engine.CsparqlEngine; import eu.larkc.csparql.core.engine.CsparqlEngineImpl; import eu.larkc.csparql.core.engine.CsparqlQueryResultProxy; import java.io.StringReader; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Observable; import java.util.Observer; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.Default; import javax.inject.Inject; import javax.inject.Named; import javax.xml.bind.DatatypeConverter; import org.apache.jena.atlas.web.auth.HttpAuthenticator; import org.apache.jena.atlas.web.auth.SimpleAuthenticator; import org.json.JSONArray; import org.json.JSONObject; import org.slf4j.LoggerFactory; import static ru.semiot.services.analyzing.ServiceConfig.config; import ru.semiot.services.analyzing.database.EventsDataBase; import ru.semiot.services.analyzing.database.QueryDataBase; import ru.semiot.services.analyzing.wamp.Subsciber; import ru.semiot.services.analyzing.wamp.WAMPClient; @Named @Default @ApplicationScoped public class CSPARQL implements Engine { private final org.slf4j.Logger logger = LoggerFactory .getLogger(CSPARQL.class); private final String STREAM_URI = "http://ex.org/streams/test"; private final CsparqlEngine engine; private final RdfStream stream; private final Map<Integer, CsparqlQueryResultProxy> queries; private final HttpAuthenticator httpAuthenticator; public CSPARQL() { engine = new CsparqlEngineImpl(); stream = new RdfStream(STREAM_URI); engine.initialize(); engine.registerStream(stream); queries = new HashMap<>(); httpAuthenticator = new SimpleAuthenticator(config.storeUsername(), config.storePassword().toCharArray()); logger.info("C-SPARQL is initialized"); } @Inject QueryDataBase db; @Inject EventsDataBase dbe; @Inject Subsciber subscriber; @Override public void appendData(Model description) { StmtIterator iterator = description.listStatements(); String timestamp = description.getGraph() .find(Node.ANY, NodeFactory.createURI("http://purl.oclc.org/NET/ssnx/ssn#observationResultTime"), Node.ANY) .next().getObject().getLiteralValue().toString(); Calendar calendar = DatatypeConverter.parseDateTime(timestamp); while (iterator.hasNext()) { Statement stmt = iterator.next(); stream.put(new RdfQuadruple(stmt.getSubject().getURI(), stmt.getPredicate().getURI(), stmt.getObject().toString(), calendar.getTimeInMillis())); } } @Override public boolean registerQuery(int query_id) { String query = db.getQuery(query_id).getString("text"); if (!subscribeTopics(query_id, true)) { return false; } try { logger.debug("Try to register query:\n" + query); CsparqlQueryResultProxy proxy = engine.registerQuery(query, false); if (proxy != null) { queries.put(query_id, proxy); logger.debug("Query is appended. Try to append Observer"); proxy.addObserver(new Observer() { @Override public void update(Observable o, Object arg) { final RDFTable rdfTable = (RDFTable) arg; final List<Binding> bindings = new ArrayList<>(); final String[] vars = rdfTable.getNames().toArray(new String[]{}); rdfTable.stream().forEach((t) -> { bindings.add(toBinding(vars, t.toString(), "\t")); }); sendToWAMP(getString(vars, bindings), query_id); appendEventsToStore(getString(vars, bindings), query_id); //subscribeTopics(query_id, true); } }); return true; } else { return false; } } catch (Exception ex) { logger.debug("Error in C-SPARQL query! Message: " + ex.getMessage()); subscribeTopics(query_id, false); return false; } } private boolean subscribeTopics(int query_id, boolean subsc) { try { String sparql = db.getQuery(query_id).getString("sparql"); QueryExecution sparqlService = QueryExecutionFactory.sparqlService(config.storeUrl(), sparql); sparqlService.setTimeout(600000); ResultSet execution = sparqlService.execSelect(); List<String> lst = execution.getResultVars(); if (lst.isEmpty() || lst.size() > 1) { logger.debug("Sparql query is bad!"); return false; } String var = lst.get(0); List<String> topics = new ArrayList<>(); while (execution.hasNext()) { topics.add(execution.next().get(var).asLiteral().getString()); } if (topics.isEmpty()) { return false; } if (subsc) { subscriber.subscribeTopics(topics, query_id); } else { subscriber.unsubscribeTopics(topics, query_id); } return true; } catch (Throwable ex) { logger.debug("Error in sparql query! Message: " + ex.getMessage()); return false; } } @Override public void removeQuery(int query_id) { if (queries.containsKey(query_id)) { logger.info("Removing query"); engine.unregisterQuery(queries.get(query_id).getId()); subscribeTopics(query_id, false); queries.remove(query_id); } else { logger.error("Query not found!"); logger.debug(queries.keySet().toString()); } } public void sendToWAMP(String message, int query_id) { logger.debug("Get alert!\n " + message); WAMPClient.getInstance().publish(config.topicsAlert() + "." + query_id, message); } private String getString(String[] vars, List<Binding> bindings) { StringBuilder message = new StringBuilder(); for (Binding binding : bindings) { for (String varName : vars) { final Var var = Var.alloc(varName); message.append(asURI(binding.get(var).toString(false))).append("\t"); } message.append('\n'); } return message.toString(); } private final String QUOTE = "\""; private final String GT = ">"; private final String LT = "<"; private Binding toBinding(String[] vars, String string, String separator) { String[] values = string.split(separator); final BindingMap binding = BindingFactory.create(); for (int i = 0; i < vars.length; i++) { binding.add(Var.alloc(vars[i]), toNode(values[i])); } return binding; } private Node toNode(String value) { if (value.startsWith("http://") || value.startsWith("https://")) { return NodeFactory.createURI(value); } else if (value.contains("^^")) { String[] parts = value.split("\\^\\^"); RDFDatatype dtype = NodeFactory.getType(toUri(parts[1])); return NodeFactory.createLiteral(unquoting(parts[0]), dtype); } else { return NodeFactory.createLiteral(value); } } private String unquoting(final String string) { final StringBuilder builder = new StringBuilder(string); if (builder.indexOf(QUOTE) == 0) { builder.deleteCharAt(0); } if (builder.lastIndexOf(QUOTE) == builder.length() - 1) { builder.deleteCharAt(builder.length() - 1); } return builder.toString(); } private String toUri(final String string) { final StringBuilder builder = new StringBuilder(string); if (builder.indexOf(LT) == 0) { builder.deleteCharAt(0); } if (builder.lastIndexOf(GT) == builder.length() - 1) { builder.deleteCharAt(builder.length() - 1); } return builder.toString(); } private void appendEventsToStore(String events, int query_id) { if (events != null && !events.isEmpty()) { JSONArray array = toJSONfromRDF(events); dbe.appendEvents(query_id, array.toString()); } } private JSONArray toJSONfromRDF(String message) { Model description = ModelFactory.createDefaultModel().read( new StringReader(message.replaceAll("\t\n", ".\n")), null, "TURTLE"); JSONArray array = new JSONArray(); JSONObject sensor; List<Triple> sensorsList = description.getGraph() .find(Node.ANY, NodeFactory.createURI("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), NodeFactory.createURI("http://example.com/#Diff")) .toList(); for (Triple t : sensorsList) { sensor = new JSONObject(); String sensorURI = t.getSubject().getURI(); String diff = description.getGraph().find(t.getSubject(), NodeFactory.createURI("http://example.com/#hasDiff"), Node.ANY) .next().getObject().getLiteral().getValue().toString(); String absTemp = description.getGraph().find(t.getSubject(), NodeFactory.createURI("http://example.com/#hasAbsTemp"), Node.ANY) .next().getObject().getLiteral().getValue().toString(); String absAvg = description.getGraph().find(t.getSubject(), NodeFactory.createURI("http://example.com/#hasAvg"), Node.ANY) .next().getObject().getLiteral().getValue().toString(); String group = description.getGraph().find(t.getSubject(), NodeFactory.createURI("http://example.com/#InGroup"), Node.ANY) .next().getObject().getLiteral().getValue().toString(); sensor.put("sensor", sensorURI); sensor.put("group", Integer.parseInt(group)); sensor.put("avg", Double.parseDouble(absAvg)); sensor.put("diff", Double.parseDouble(diff)); sensor.put("temp", Double.parseDouble(absTemp)); array.put(sensor); } return array; } private String asURI(final String string) { final StringBuilder builder = new StringBuilder(); if (string.startsWith("http://") || string.startsWith("https://")) { //If it is URI (something like http://ex.com/#hasValue) builder.append(LT).append(string).append(GT); } else if (string.contains("^^")) { //If it is literal (something like 1^^http://www.w3.org/2001/XMLSchema#integer or text^^http://www.w3.org/2001/XMLSchema#string) String[] parts = string.split("\\^\\^"); builder.append(QUOTE).append(parts[0]).append(QUOTE).append("^^").append(LT).append(parts[1]).append(GT); } else { builder.append(string); } return builder.toString(); } }