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