001/** 002 * Copyright (C) 2007 - 2016, Jens Lehmann 003 * 004 * This file is part of DL-Learner. 005 * 006 * DL-Learner is free software; you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation; either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * DL-Learner is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package org.dllearner.kb.sparql; 020 021import java.io.ByteArrayInputStream; 022import java.io.ByteArrayOutputStream; 023import java.io.File; 024import java.io.IOException; 025import java.io.UnsupportedEncodingException; 026import java.nio.charset.Charset; 027 028import javax.xml.ws.http.HTTPException; 029 030import org.apache.log4j.Logger; 031import org.dllearner.utilities.Files; 032import org.dllearner.utilities.JamonMonitorLogger; 033 034import org.apache.jena.query.ResultSet; 035import org.apache.jena.query.ResultSetFactory; 036import org.apache.jena.query.ResultSetFormatter; 037import org.apache.jena.query.ResultSetRewindable; 038import org.apache.jena.sparql.engine.http.QueryEngineHTTP; 039import com.jamonapi.Monitor; 040 041/** 042 * Represents one SPARQL query. It includes support for stopping the SPARQL 043 * query (which may be necessary if a timeout is reached) and is designed to be 044 * able to run a query in a separate thread. 045 * 046 * @author Jens Lehmann 047 * @author Sebastian Hellmann 048 * 049 */ 050public class SparqlQuery { 051 052 private static boolean logDeletedOnStart = false; 053 054 private static Logger logger = Logger.getLogger(SparqlQuery.class); 055 056 // additional file for logging SPARQL queries etc. 057 private static String sparqlLog = "log/sparql.txt"; 058 059 // whether the query is currently running 060 private boolean isRunning = false; 061 062 // whether the query has been executed 063 private boolean wasExecuted = false; 064 065 private String sparqlQueryString; 066 067 private QueryEngineHTTP queryExecution; 068 069 private SparqlEndpoint sparqlEndpoint; 070 071 private ResultSetRewindable rs; 072 073 /** 074 * Standard constructor. 075 * 076 * @param sparqlQueryString 077 * A SPARQL query string 078 * @param sparqlEndpoint 079 * An Endpoint object 080 */ 081 public SparqlQuery(String sparqlQueryString, SparqlEndpoint sparqlEndpoint) { 082 // QUALITY there seems to be a bug in ontowiki 083 this.sparqlQueryString = sparqlQueryString.replaceAll("\n", " "); 084 this.sparqlEndpoint = sparqlEndpoint; 085 } 086 087 /** 088 * Sends a SPARQL query using the Jena library. 089 * 090 */ 091 public ResultSetRewindable send() { 092 return send(true); 093 } 094 095 public ResultSetRewindable send(boolean writeLog) { 096 isRunning = true; 097 098 String service = sparqlEndpoint.getURL().toString(); 099 100 if(writeLog){ 101 writeToSparqlLog("***********\nNew Query:"); 102 SparqlQuery.writeToSparqlLog("wget -S -O - '\n" + sparqlEndpoint.getHTTPRequest()); 103 writeToSparqlLog(sparqlQueryString); 104 } 105 106 queryExecution = new QueryEngineHTTP(service, sparqlQueryString); 107 108 // add default and named graphs 109 for (String dgu : sparqlEndpoint.getDefaultGraphURIs()) { 110 queryExecution.addDefaultGraph(dgu); 111 } 112 for (String ngu : sparqlEndpoint.getNamedGraphURIs()) { 113 queryExecution.addNamedGraph(ngu); 114 } 115 116 Monitor httpTime = JamonMonitorLogger.getTimeMonitor(SparqlQuery.class, "sparql query time").start(); 117 118 try { 119 logger.debug("sending query: length: " + sparqlQueryString.length() + " | ENDPOINT: " 120 + sparqlEndpoint.getURL().toString()); 121 122 // we execute the query and store the result in a rewindable result set 123 ResultSet tmp = queryExecution.execSelect(); 124 rs = ResultSetFactory.makeRewindable(tmp); 125 } catch (HTTPException e) { 126 logger.debug("HTTPException in SparqlQuery\n" + e.toString()); 127 logger.debug("query was " + sparqlQueryString); 128 if(writeLog){ 129 writeToSparqlLog("ERROR: HTTPException occured" + e.toString()); 130 } 131 isRunning = false; 132 throw e; 133 // TODO: RuntimeException is very general; is it possible to catch more specific exceptions? 134 } catch (RuntimeException e) { 135 if (logger.isDebugEnabled()) { 136 logger.debug("RuntimeException in SparqlQuery (see /log/sparql.txt): " 137 + e.toString()); 138 int length = Math.min(sparqlQueryString.length(), 300); 139 logger.debug("query was (max. 300 chars displayed) " 140 + sparqlQueryString.substring(0, length - 1).replaceAll("\n", " ")); 141 } 142 if(writeLog){ 143 writeToSparqlLog("ERROR: HTTPException occured: " + e.toString()); 144 } 145 isRunning = false; 146 throw e; 147 } 148 149 httpTime.stop(); 150 isRunning = false; 151 wasExecuted = true; 152 return rs; 153 } 154 155 public boolean sendAsk() { 156 isRunning = true; 157 String service = sparqlEndpoint.getURL().toString(); 158 queryExecution = new QueryEngineHTTP(service, sparqlQueryString); 159 boolean result = queryExecution.execAsk(); 160 isRunning = false; 161 return result; 162 } 163 164 /** 165 * Stops the execution of the query. 166 */ 167 public void stop() { 168 queryExecution.abort(); 169 isRunning = false; 170 } 171 172 /** 173 * Gets the String representation of the SPARQL query. 174 * 175 * @return sparqlQueryString 176 */ 177 public String getSparqlQueryString() { 178 return sparqlQueryString; 179 } 180 181 /** 182 * @return sparqlEndpoint object 183 */ 184 public SparqlEndpoint getSparqlEndpoint() { 185 return sparqlEndpoint; 186 } 187 188 /** 189 * 190 * @return boolean 191 */ 192 public boolean isRunning() { 193 return isRunning; 194 } 195 196 /** 197 * Return the result in JSON format. 198 * 199 * @return A JSON string converted from the result set or null 200 * if the query has not been executed. 201 */ 202 public String getJson() { 203 if(wasExecuted) { 204 return convertResultSetToJSON(rs); 205 } else { 206 return null; 207 } 208 } 209 210 /** 211 * Converts the result set to an XML string. 212 * 213 * @return An XML String 214 */ 215 public String getXMLString() { 216 if(wasExecuted) { 217 return convertResultSetToXMLString(rs); 218 } else { 219 return null; 220 } 221 } 222 223 /** 224 * Special log for debugging SPARQL query execution. It lives here: 225 * "log/sparql.txt" if the directory doesn't exist, there could be an error. 226 * 227 * @param s 228 * the String to log 229 */ 230 private static void writeToSparqlLog(String s) { 231 new File("log").mkdirs(); 232 File f = new File(sparqlLog); 233 if(!f.canWrite() ){ 234 try { 235 f.createNewFile(); 236 } catch (IOException e) { 237 e.printStackTrace(); 238 } 239// logger.info("could not write SPARQL log to : " + f.getAbsolutePath()); 240// return ; 241 } 242 243 if (!logDeletedOnStart) { 244 Files.createFile(f, s + "\n"); 245 logDeletedOnStart = true; 246 } else { 247 Files.appendToFile(f, s + "\n"); 248 } 249 250 } 251 252 /** 253 * Converts Jena result set to XML. To make a ResultSet rewindable use: 254 * ResultSetRewindable rsRewind = 255 * ResultSetFactory.makeRewindable(resultSet); 256 * 257 * @param resultSet 258 * The result set to transform, must be rewindable to prevent 259 * errors. 260 * @return String xml 261 */ 262 public static String convertResultSetToXMLString(ResultSetRewindable resultSet) { 263 String retVal = ResultSetFormatter.asXMLString(resultSet); 264 resultSet.reset(); 265 return retVal; 266 } 267 268 /** 269 * Converts Jena result set to JSON. 270 * 271 * @param resultSet 272 * The result set to transform, must be rewindable to prevent 273 * errors. 274 * @return JSON representation of the result set. 275 */ 276 public static String convertResultSetToJSON(ResultSet resultSet) { 277 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 278 ResultSetFormatter.outputAsJSON(baos, resultSet); 279// resultSet.reset(); 280 try { 281 return baos.toString("UTF-8"); 282 } catch (UnsupportedEncodingException e) { 283 // should never happen as UTF-8 is supported 284 throw new Error(e); 285 } 286 } 287 288 /** 289 * Converts from JSON to internal Jena format. 290 * 291 * @param json 292 * A JSON representation if a SPARQL query result. 293 * @return A Jena ResultSet. 294 */ 295 public static ResultSetRewindable convertJSONtoResultSet(String json) { 296 ByteArrayInputStream bais = new ByteArrayInputStream(json 297 .getBytes(Charset.forName("UTF-8"))); 298 // System.out.println("JSON " + json); 299 return ResultSetFactory.makeRewindable(ResultSetFactory.fromJSON(bais)); 300 } 301 302 /** 303 * Converts from JSON to xml format. 304 * 305 * @param json 306 * A JSON representation if a SPARQL query result. 307 * @return A Jena ResultSet. 308 */ 309 public static String convertJSONtoXML(String json) { 310 return convertResultSetToXMLString(convertJSONtoResultSet(json)); 311 } 312 313}