Project

General

Profile

1
package eu.dnetlib.parthenos.virtuoso;
2

    
3
import java.io.IOException;
4
import java.io.OutputStream;
5
import java.io.StringWriter;
6
import java.util.HashMap;
7
import java.util.Iterator;
8
import java.util.List;
9
import java.util.Map;
10

    
11
import com.google.common.collect.Iterators;
12
import com.google.common.collect.Lists;
13
import eu.dnetlib.parthenos.CRM;
14
import eu.dnetlib.parthenos.publisher.ParthenosPublisherException;
15
import freemarker.template.Configuration;
16
import freemarker.template.Template;
17
import freemarker.template.TemplateException;
18
import org.apache.commons.io.IOUtils;
19
import org.apache.commons.logging.Log;
20
import org.apache.commons.logging.LogFactory;
21
import org.apache.http.HttpEntity;
22
import org.apache.http.HttpHeaders;
23
import org.apache.http.NameValuePair;
24
import org.apache.http.client.entity.UrlEncodedFormEntity;
25
import org.apache.http.client.methods.CloseableHttpResponse;
26
import org.apache.http.client.methods.HttpPost;
27
import org.apache.http.impl.client.CloseableHttpClient;
28
import org.apache.http.impl.client.HttpClients;
29
import org.apache.http.message.BasicNameValuePair;
30
import org.apache.http.util.EntityUtils;
31
import org.apache.jena.query.Query;
32
import org.apache.jena.query.QueryExecutionFactory;
33
import org.apache.jena.query.QueryFactory;
34
import org.apache.jena.query.ResultSet;
35
import org.apache.jena.sparql.engine.http.QueryEngineHTTP;
36
import org.springframework.beans.factory.annotation.Autowired;
37
import org.springframework.beans.factory.annotation.Value;
38
import org.springframework.web.bind.annotation.RequestMapping;
39
import org.springframework.web.bind.annotation.RequestMethod;
40
import org.springframework.web.bind.annotation.RequestParam;
41
import org.springframework.web.bind.annotation.RestController;
42

    
43
/**
44
 * Created by Alessia Bardi on 31/01/2018.
45
 * Read-only API for virtuoso.
46
 *
47
 * //TODO: error handling
48
 * //TODO: pagination
49
 * //TODO swagger documentation?
50
 *
51
 * @author Alessia Bardi
52
 */
53
@RestController
54
public class VirtuosoReadAPI {
55

    
56
	private static final Log log = LogFactory.getLog(VirtuosoReadAPI.class);
57

    
58
	@Value("${virtuoso.sparqlurl}")
59
	private String sparqlUrl;
60
	@Value("${virtuoso.pwd}")
61
	private String username;
62
	@Value("${virtuoso.pwd}")
63
	private String password;
64
	@Value("${virtuoso.uri.base.default}")
65
	private String defaultBaseURI;
66
	@Value("${virtuoso.connectionstring")
67
	private String virtuosoConnectionString;
68

    
69
	@Autowired
70
	private Configuration freemarkerConfig;
71

    
72
	@RequestMapping(value = "/virtuoso/graphs", produces = { "application/json" }, method = RequestMethod.GET)
73
	public List<String> getGraphURLs(@RequestParam final String api) {
74
		String queryForGraphs =
75
				"SELECT DISTINCT ?g WHERE { ?g <dnetcollectedFrom> <"+defaultBaseURI+api+"> }";
76
		Query query = QueryFactory.create(queryForGraphs);
77
		final QueryEngineHTTP serviceRequest = QueryExecutionFactory.createServiceRequest(sparqlUrl, query);
78
		ResultSet graphs = serviceRequest.execSelect();
79
		Iterator<String> s = Iterators.transform(graphs, qs -> qs.getResource("g").getURI());
80
		List<String> res = Lists.newArrayList(s);
81
		serviceRequest.close();
82
		return res;
83
	}
84

    
85
	@RequestMapping(value = "/virtuoso/subjects", produces = { "application/json" }, method = RequestMethod.GET)
86
	public List<String> getSubjectsForGraph(@RequestParam final String graph) {
87
		// Based on FORTH observation, resources without a URL should not be interesting by themselves.
88
		// Here we filter out those + resources whose type is not in the parthenos namespace.
89

    
90
		//old query selecting any subjects in the given graph
91
		//String queryForSubjectsTemplate = "SELECT DISTINCT ?s WHERE {GRAPH <%s> {?s ?p ?o .}}";
92
		String queryForSubjectsTemplate = "SELECT DISTINCT ?s WHERE {GRAPH <%s> {?s ?p ?o . ?s a ?t. FILTER (isURI(?s) && ?t != <%s> && ?t != <%s>) }}";
93
		String q = String.format(queryForSubjectsTemplate, graph, CRM.E55_Type.getURI(), CRM.E41_Appellation.getURI());
94
		final QueryEngineHTTP serviceRequest =
95
				QueryExecutionFactory.createServiceRequest(sparqlUrl, QueryFactory.create(q));
96
		ResultSet subjects = serviceRequest.execSelect();
97
		Iterator<String> s = Iterators.transform(subjects, qs -> qs.getResource("s").getURI());
98
		List<String> res = Lists.newArrayList(s);
99
		serviceRequest.close();
100
		return res;
101
	}
102

    
103

    
104
	@RequestMapping(value = "/virtuoso/subjectsWithType", produces = { "application/json" }, method = RequestMethod.GET)
105
	public List<String> getSubjectsForGraphWithType(@RequestParam final String graph, @RequestParam final String typeNamespace, @RequestParam final String typeName) {
106
		String fullTypeName = typeNamespace + typeName;
107
		log.debug(String.format("Getting subjects of type %s for graph %s", fullTypeName, graph));
108

    
109
		String queryForSubjectsTemplate = "SELECT ?s WHERE {GRAPH <%s> {?s a <%s>. FILTER (isURI(?s)) }}";
110
		String q = String.format(queryForSubjectsTemplate, graph, fullTypeName);
111
		final QueryEngineHTTP serviceRequest =
112
				QueryExecutionFactory.createServiceRequest(sparqlUrl, QueryFactory.create(q));
113
		ResultSet subjects = serviceRequest.execSelect();
114
		Iterator<String> s = Iterators.transform(subjects, qs -> qs.getResource("s").getURI());
115
		List<String> res = Lists.newArrayList(s);
116
		serviceRequest.close();
117
		return res;
118
	}
119

    
120
	@RequestMapping(value = "/virtuoso/subject", produces = { "application/rdf+xml", "application/xml" }, method = RequestMethod.GET)
121
	public void getSubject(@RequestParam final String subjectURL, final OutputStream responseStream) throws IOException, ParthenosPublisherException {
122
		//String describeQuery = "DESCRIBE <" + subjectURL + ">";
123
		//log.debug(describeQuery);
124
		//Can't use DESCRIBE query: server decides what to return (i.e. not all triples) and cannot find out if and how Virtuoso can be configured
125

    
126
		String q = "DEFINE input:inference 'parthenos_rules' CONSTRUCT {<%s> ?p ?o . } WHERE { <%s> ?p ?o .}";
127
		String query = String.format(q, subjectURL, subjectURL);
128
		sendConstructResponse(query, responseStream);
129
	}
130

    
131
	@RequestMapping(value = "/virtuoso/subjectForJRR", produces = { "application/rdf+xml", "application/xml" }, method = RequestMethod.GET)
132
	public void getSubjectForJRR(@RequestParam final String subjectURL, @RequestParam final String typeName, final OutputStream responseStream)
133
			throws IOException, TemplateException, ParthenosPublisherException {
134
		String templateName = typeName+".sparql";
135
		Template temp = freemarkerConfig.getTemplate(templateName);
136
		Map<String, String> values = new HashMap<>();
137
		values.put("subjectURL", subjectURL);
138

    
139
		StringWriter sw = new StringWriter();
140
		temp.process(values, sw);
141
		String q = sw.toString();
142
		log.debug("Querying for "+subjectURL+" with query "+templateName);
143
		sendConstructResponse(q, responseStream);
144
	}
145

    
146

    
147
	protected void sendConstructResponse(final String query, final OutputStream responseStream) throws IOException, ParthenosPublisherException {
148
		String res = executeSparqlPost(query);
149
		IOUtils.write(res, responseStream);
150
	}
151

    
152
	protected String executeSparqlPost(final String query) throws IOException, ParthenosPublisherException {
153
		try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
154
			HttpPost httpPost = new HttpPost(sparqlUrl);
155
			httpPost.setHeader(HttpHeaders.ACCEPT, "application/rdf+xml");
156
			List<NameValuePair> nvps = Lists.newArrayList();
157
			nvps.add(new BasicNameValuePair("query", query));
158
			httpPost.setEntity(new UrlEncodedFormEntity(nvps));
159

    
160
			try (CloseableHttpResponse response2 = httpclient.execute(httpPost)) {
161
				log.debug(response2.getStatusLine());
162
				HttpEntity entity2 = response2.getEntity();
163
				String res = IOUtils.toString(entity2.getContent());
164
				EntityUtils.consume(entity2);
165
				int statusCode = response2.getStatusLine().getStatusCode();
166
				switch(statusCode){
167
				case 200:
168
					return res;
169
				default:
170
					String errorMessage = String.format("ERROR HTTP STATUS CODE %d, REASON PHRASE: %s\n ERROR BODY: %s", statusCode, response2.getStatusLine(), res);
171
					log.error(errorMessage);
172
					throw new ParthenosPublisherException(errorMessage);
173
				}
174
			}
175
		}
176
	}
177
	
178

    
179

    
180

    
181

    
182
}
(3-3/3)