blob: f27b14a3138e3e2752b25607644275acec20a558 [file] [log] [blame]
Nils Diewaldf6b351c2014-09-04 21:34:05 +00001package de.ids_mannheim.korap.server;
2
Nils Diewaldff6f7662014-09-21 15:08:52 +00003import java.io.*;
4
Nils Diewaldf6b351c2014-09-04 21:34:05 +00005import javax.ws.rs.GET;
6import javax.ws.rs.POST;
Nils Diewaldff6f7662014-09-21 15:08:52 +00007import javax.ws.rs.PUT;
Nils Diewaldf6b351c2014-09-04 21:34:05 +00008import javax.ws.rs.Path;
9import javax.ws.rs.PathParam;
10import javax.ws.rs.Produces;
11import javax.ws.rs.Consumes;
12import javax.ws.rs.core.MediaType;
13import javax.ws.rs.core.Response;
14import javax.ws.rs.core.Context;
15import javax.ws.rs.core.UriInfo;
16import javax.ws.rs.core.MultivaluedMap;
Nils Diewaldff6f7662014-09-21 15:08:52 +000017import javax.ws.rs.ext.ReaderInterceptor;
18import javax.ws.rs.ext.ReaderInterceptorContext;
19import javax.ws.rs.ext.WriterInterceptor;
20import javax.ws.rs.ext.WriterInterceptorContext;
21import javax.ws.rs.WebApplicationException;
Nils Diewaldf6b351c2014-09-04 21:34:05 +000022
23import de.ids_mannheim.korap.KorapNode;
24import de.ids_mannheim.korap.KorapIndex;
25import de.ids_mannheim.korap.KorapSearch;
Nils Diewald7cbcfe92014-09-22 22:01:51 +000026import de.ids_mannheim.korap.KorapCollection;
Nils Diewaldf6b351c2014-09-04 21:34:05 +000027import de.ids_mannheim.korap.KorapMatch;
28import de.ids_mannheim.korap.KorapResult;
Nils Diewaldc471b182014-11-19 22:51:15 +000029import de.ids_mannheim.korap.response.KorapResponse;
Nils Diewaldff6f7662014-09-21 15:08:52 +000030import de.ids_mannheim.korap.index.FieldDocument;
Nils Diewaldf6b351c2014-09-04 21:34:05 +000031import de.ids_mannheim.korap.util.QueryException;
Nils Diewaldf04e1002014-09-24 22:52:59 +000032import de.ids_mannheim.korap.index.MatchCollector;
33import de.ids_mannheim.korap.index.collector.MatchCollectorDB;
Nils Diewaldf6b351c2014-09-04 21:34:05 +000034
35import java.util.List;
36import java.util.regex.Pattern;
37import java.util.regex.Matcher;
Nils Diewaldff6f7662014-09-21 15:08:52 +000038import java.util.zip.GZIPInputStream;
39import java.util.zip.GZIPOutputStream;
Nils Diewaldf6b351c2014-09-04 21:34:05 +000040
Nils Diewaldf04e1002014-09-24 22:52:59 +000041import org.slf4j.Logger;
42import org.slf4j.LoggerFactory;
43
44import java.sql.SQLException;
Nils Diewald8d8641b2014-09-28 17:37:53 +000045import java.sql.Connection;
Nils Diewald6aa929e2014-09-17 13:30:34 +000046
Nils Diewaldff6f7662014-09-21 15:08:52 +000047
Nils Diewaldf6b351c2014-09-04 21:34:05 +000048/**
49 * Root resource (exposed at root path)
Nils Diewald979b2fe2014-09-29 16:21:41 +000050 * The responses only represent JSON responses, although HTML responses
51 * may be handy.
Nils Diewaldf6b351c2014-09-04 21:34:05 +000052 *
53 * @author Nils Diewald
54 *
55 * Look at http://www.mkyong.com/webservices/jax-rs/json-example-with-jersey-jackson/
56 */
57@Path("/")
58public class Resource {
59
Nils Diewaldff6f7662014-09-21 15:08:52 +000060 private String version;
61
Nils Diewald979b2fe2014-09-29 16:21:41 +000062 // Initiate Logger
Nils Diewaldf04e1002014-09-24 22:52:59 +000063 private final static Logger log = LoggerFactory.getLogger(KorapNode.class);
Nils Diewaldf6b351c2014-09-04 21:34:05 +000064
Nils Diewald979b2fe2014-09-29 16:21:41 +000065 // Slightly based on String::BooleanSimple
66 static Pattern p = Pattern.compile(
67 "\\s*(?i:false|no|inactive|disabled|off|n|neg(?:ative)?|not|null|undef)\\s*"
68 );
69
70 // Check if a string is meant to represent null
Nils Diewaldf6b351c2014-09-04 21:34:05 +000071 private static boolean isNull (String value) {
72 if (value == null)
73 return true;
74
75 Matcher m = p.matcher(value);
76 if (m.matches())
77 return true;
78
79 return false;
80 };
81
Nils Diewald979b2fe2014-09-29 16:21:41 +000082
83 /**
84 * Return information on the node, like name etc.
85 */
Nils Diewaldf04e1002014-09-24 22:52:59 +000086 @GET
Nils Diewaldf04e1002014-09-24 22:52:59 +000087 @Produces(MediaType.APPLICATION_JSON)
88 public String info () {
89 KorapIndex index = KorapNode.getIndex();
Nils Diewaldc471b182014-11-19 22:51:15 +000090 KorapResponse kresp = new KorapResponse();
91 kresp.setNode(KorapNode.getName());
92 kresp.setName(index.getName());
93 kresp.setVersion(index.getVersion());
94
Nils Diewaldf04e1002014-09-24 22:52:59 +000095 kresp.setListener(KorapNode.getListener());
Nils Diewald8d8641b2014-09-28 17:37:53 +000096 long texts = -1;
Nils Diewaldc471b182014-11-19 22:51:15 +000097 /*
98 kresp.addMessage(
99 "Number of documents in the index",
100 String.parseLong(index.numberOf("documents"))
101 );
102 */
103 kresp.addMessage(680, "Server is up and running!");
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000104 return kresp.toJsonString();
Nils Diewaldf04e1002014-09-24 22:52:59 +0000105 };
106
107
Nils Diewaldf6b351c2014-09-04 21:34:05 +0000108 /**
Nils Diewaldff6f7662014-09-21 15:08:52 +0000109 * Add new documents to the index
110 *
111 * @param json JSON-LD string with search and potential meta filters.
112 */
113 /*
114 * Support GZip:
Nils Diewaldc82379b2014-10-02 14:58:18 +0000115 * oR MAYBE IT'S ALREADY SUPPORTED ....
Nils Diewaldff6f7662014-09-21 15:08:52 +0000116 * http://stackoverflow.com/questions/19765582/how-to-make-jersey-use-gzip-compression-for-the-response-message-body
117 */
118 @PUT
Nils Diewald7cbcfe92014-09-22 22:01:51 +0000119 @Path("/index/{textID}")
Nils Diewaldff6f7662014-09-21 15:08:52 +0000120 @Produces(MediaType.APPLICATION_JSON)
121 @Consumes(MediaType.APPLICATION_JSON)
122 public String add (@PathParam("textID") Integer uid,
123 @Context UriInfo uri,
124 String json) {
125 /*
126 * See
127 * http://www.mkyong.com/webservices/jax-rs/file-upload-example-in-jersey/
128 */
129
130 // Todo: Parameter for server node
131
132 // Get index
133 KorapIndex index = KorapNode.getIndex();
Nils Diewaldff6f7662014-09-21 15:08:52 +0000134
Nils Diewaldc471b182014-11-19 22:51:15 +0000135 KorapResponse kresp = new KorapResponse();
136 kresp.setNode(KorapNode.getName());
137
138 if (index == null) {
139 kresp.addError(601, "Unable to find index");
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000140 return kresp.toJsonString();
Nils Diewaldc471b182014-11-19 22:51:15 +0000141 };
142
143 kresp.setVersion(index.getVersion());
144 kresp.setName(index.getName());
Nils Diewaldff6f7662014-09-21 15:08:52 +0000145
146 String ID = "Unknown";
Nils Diewaldff6f7662014-09-21 15:08:52 +0000147 try {
148 FieldDocument fd = index.addDoc(uid, json);
149 ID = fd.getID();
Nils Diewaldff6f7662014-09-21 15:08:52 +0000150 }
151 // Set HTTP to ???
Nils Diewaldc471b182014-11-19 22:51:15 +0000152 // TODO: This may be a field error!
Nils Diewaldff6f7662014-09-21 15:08:52 +0000153 catch (IOException e) {
Nils Diewaldc471b182014-11-19 22:51:15 +0000154 kresp.addError(602, "Unable to add document to index");
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000155 return kresp.toJsonString();
Nils Diewaldff6f7662014-09-21 15:08:52 +0000156 };
157
158 // Set HTTP to 200
Nils Diewaldc471b182014-11-19 22:51:15 +0000159 kresp.addMessage(681, "Document was added successfully", ID);
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000160 return kresp.toJsonString();
Nils Diewaldff6f7662014-09-21 15:08:52 +0000161 };
162
163
164 // TODO: Commit changes to the index before the server dies!
165 /**
166 * Commit data changes to the index
167 */
168 @POST
Nils Diewald7cbcfe92014-09-22 22:01:51 +0000169 @Path("/index")
Nils Diewaldff6f7662014-09-21 15:08:52 +0000170 @Produces(MediaType.APPLICATION_JSON)
171 public String commit () {
172
173 // Get index
174 KorapIndex index = KorapNode.getIndex();
Nils Diewaldc471b182014-11-19 22:51:15 +0000175 KorapResponse kresp = new KorapResponse();
176 kresp.setNode(KorapNode.getName());
Nils Diewaldff6f7662014-09-21 15:08:52 +0000177
Nils Diewaldc471b182014-11-19 22:51:15 +0000178 if (index == null) {
179 kresp.addError(601, "Unable to find index");
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000180 return kresp.toJsonString();
Nils Diewaldc471b182014-11-19 22:51:15 +0000181 };
Nils Diewald979cc112014-09-23 19:53:54 +0000182
Nils Diewaldc471b182014-11-19 22:51:15 +0000183 kresp.setVersion(index.getVersion());
184 kresp.setName(index.getName());
Nils Diewaldff6f7662014-09-21 15:08:52 +0000185
186 // There are documents to commit
Nils Diewald7cbcfe92014-09-22 22:01:51 +0000187 try {
188 index.commit();
189 }
190 catch (IOException e) {
Nils Diewaldff6f7662014-09-21 15:08:52 +0000191 // Set HTTP to ???
Nils Diewaldc471b182014-11-19 22:51:15 +0000192 kresp.addError(603, "Unable to commit staged data to index");
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000193 return kresp.toJsonString();
Nils Diewaldff6f7662014-09-21 15:08:52 +0000194 };
195
196 // Set HTTP to ???
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000197 return kresp.toJsonString();
Nils Diewald7cbcfe92014-09-22 22:01:51 +0000198 };
199
Nils Diewald979cc112014-09-23 19:53:54 +0000200
201
Nils Diewald7cbcfe92014-09-22 22:01:51 +0000202 /**
Nils Diewald979cc112014-09-23 19:53:54 +0000203 * Find matches in the lucene index based on UIDs and return one match per doc.
Nils Diewald7cbcfe92014-09-22 22:01:51 +0000204 *
205 * @param text_id
206 */
207 @POST
208 @Produces(MediaType.APPLICATION_JSON)
209 @Consumes(MediaType.APPLICATION_JSON)
210 public String find (String json, @Context UriInfo uri) {
211
212 // Get index
213 KorapIndex index = KorapNode.getIndex();
214
215 // Search index
216 if (index != null) {
217 KorapSearch ks = new KorapSearch(json);
218
219 // Get query parameters
220 MultivaluedMap<String,String> qp = uri.getQueryParameters();
221
Nils Diewaldd723d812014-09-23 18:50:52 +0000222 if (qp.get("uid") != null) {
Nils Diewald7cbcfe92014-09-22 22:01:51 +0000223
Nils Diewaldd723d812014-09-23 18:50:52 +0000224 // Build Collection based on a list of uids
225 List<String> uids = qp.get("uid");
226 KorapCollection kc = new KorapCollection();
227 kc.filterUIDs(uids.toArray(new String[uids.size()]));
Nils Diewald7cbcfe92014-09-22 22:01:51 +0000228
Nils Diewaldd723d812014-09-23 18:50:52 +0000229 // TODO: RESTRICT COLLECTION TO ONLY RESPECT SELF DOCS (REPLICATION)
Nils Diewald7cbcfe92014-09-22 22:01:51 +0000230
Nils Diewaldd723d812014-09-23 18:50:52 +0000231 // Override old collection
232 ks.setCollection(kc);
Nils Diewald7cbcfe92014-09-22 22:01:51 +0000233
Nils Diewaldd723d812014-09-23 18:50:52 +0000234 // Only return the first match per text
235 ks.setItemsPerResource(1);
236
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000237 return ks.run(index).toJsonString();
Nils Diewaldd723d812014-09-23 18:50:52 +0000238 };
239 KorapResult kr = new KorapResult();
Nils Diewald979cc112014-09-23 19:53:54 +0000240 kr.setNode(KorapNode.getName());
Nils Diewaldc471b182014-11-19 22:51:15 +0000241 kr.addError(610, "Missing request parameters", "No unique IDs were given");
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000242 return kr.toJsonString();
Nils Diewald7cbcfe92014-09-22 22:01:51 +0000243 };
Nils Diewald979cc112014-09-23 19:53:54 +0000244
Nils Diewaldc471b182014-11-19 22:51:15 +0000245 KorapResponse kresp = new KorapResponse();
246 kresp.setNode(KorapNode.getName());
247 kresp.setName(index.getName());
248 kresp.setVersion(index.getVersion());
249
250 kresp.addError(601, "Unable to find index");
251
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000252 return kresp.toJsonString();
Nils Diewaldff6f7662014-09-21 15:08:52 +0000253 };
254
255
256 /**
Nils Diewald979cc112014-09-23 19:53:54 +0000257 * Collect matches and aggregate the UIDs plus matchcount in the database.
258 *
259 * @param text_id
260 */
Nils Diewaldf04e1002014-09-24 22:52:59 +0000261 @PUT
Nils Diewald979cc112014-09-23 19:53:54 +0000262 @Path("/collect/{resultID}")
263 @Produces(MediaType.APPLICATION_JSON)
264 @Consumes(MediaType.APPLICATION_JSON)
Nils Diewaldf04e1002014-09-24 22:52:59 +0000265 public String collect (String json,
266 @PathParam("resultID") String resultID,
267 @Context UriInfo uri) {
Nils Diewald979cc112014-09-23 19:53:54 +0000268
269 // Get index
270 KorapIndex index = KorapNode.getIndex();
271
272 // No index found
Nils Diewaldc471b182014-11-19 22:51:15 +0000273 if (index == null) {
274 KorapResponse kresp = new KorapResponse();
275 kresp.setNode(KorapNode.getName());
276 kresp.addError(601, "Unable to find index");
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000277 return kresp.toJsonString();
Nils Diewaldc471b182014-11-19 22:51:15 +0000278 };
Nils Diewald979cc112014-09-23 19:53:54 +0000279
280 // Get the database
Nils Diewaldf04e1002014-09-24 22:52:59 +0000281 try {
282 MatchCollectorDB mc = new MatchCollectorDB(1000, "Res_" + resultID);
Nils Diewald8d8641b2014-09-28 17:37:53 +0000283 Connection conn = KorapNode.getDBPool().getConnection();
284 mc.setDBPool("mysql", KorapNode.getDBPool(), conn);
Nils Diewald979cc112014-09-23 19:53:54 +0000285
Nils Diewaldf04e1002014-09-24 22:52:59 +0000286 // TODO: Only search in self documents (REPLICATION FTW!)
Nils Diewald979cc112014-09-23 19:53:54 +0000287
Nils Diewaldf04e1002014-09-24 22:52:59 +0000288 KorapSearch ks = new KorapSearch(json);
289 MatchCollector result = index.collect(ks, mc);
Nils Diewald979cc112014-09-23 19:53:54 +0000290
Nils Diewaldf04e1002014-09-24 22:52:59 +0000291 result.setNode(KorapNode.getName());
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000292 return result.toJsonString();
Nils Diewaldf04e1002014-09-24 22:52:59 +0000293 }
294 catch (SQLException e) {
295 log.error(e.getLocalizedMessage());
Nils Diewald979cc112014-09-23 19:53:54 +0000296 };
297
Nils Diewaldc471b182014-11-19 22:51:15 +0000298 KorapResponse kresp = new KorapResponse();
299 kresp.setNode(KorapNode.getName());
300 kresp.setName(index.getName());
301 kresp.setVersion(index.getVersion());
302
303 kresp.addError(604, "Unable to connect to database");
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000304 return kresp.toJsonString();
Nils Diewaldad3f3032014-09-24 01:42:47 +0000305 };
Nils Diewald979cc112014-09-23 19:53:54 +0000306
307
308
309
310
311 /* These routes are still wip: */
312
313
314
315
316 /**
Nils Diewaldf6b351c2014-09-04 21:34:05 +0000317 * Search the lucene index.
318 *
319 * @param json JSON-LD string with search and potential meta filters.
320 */
321 @POST
322 @Path("/search")
323 @Produces(MediaType.APPLICATION_JSON)
324 @Consumes(MediaType.APPLICATION_JSON)
325 public String search (String json) {
326
327 // Get index
328 KorapIndex index = KorapNode.getIndex();
329
330 // Search index
Nils Diewald979cc112014-09-23 19:53:54 +0000331 if (index != null) {
332 KorapResult kr = new KorapSearch(json).run(index);
333 kr.setNode(KorapNode.getName());
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000334 return kr.toJsonString();
Nils Diewald979cc112014-09-23 19:53:54 +0000335 };
Nils Diewaldf6b351c2014-09-04 21:34:05 +0000336
Nils Diewaldc471b182014-11-19 22:51:15 +0000337 KorapResponse kresp = new KorapResponse();
338 kresp.setNode(KorapNode.getName());
339 kresp.setName(index.getName());
340 kresp.setVersion(index.getVersion());
341
342 kresp.addError(601, "Unable to find index");
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000343 return kresp.toJsonString();
Nils Diewaldf6b351c2014-09-04 21:34:05 +0000344 };
345
346 @GET
347 @Path("/match/{matchID}")
348 @Produces(MediaType.APPLICATION_JSON)
349 public String match (@PathParam("matchID") String id,
350 @Context UriInfo uri) {
351
352 // Get index
353 KorapIndex index = KorapNode.getIndex();
354
355 // Search index
356 if (index != null) {
357
358 // Get query parameters
359 MultivaluedMap<String,String> qp = uri.getQueryParameters();
360
361 boolean includeSpans = false,
362 includeHighlights = true,
363 extendToSentence = false,
364 info = false;
365
366 // Optional query parameter "info" for more information on the match
367 if (!isNull(qp.getFirst("info")))
368 info = true;
369
370 // Optional query parameter "spans" for span information inclusion
371 if (!isNull(qp.getFirst("spans"))) {
372 includeSpans = true;
373 info = true;
374 };
375
376 // Optional query parameter "highlights" for highlight information inclusion
377 String highlights = qp.getFirst("highlights");
378 if (highlights != null && isNull(highlights))
379 includeHighlights = false;
380
381 // Optional query parameter "extended" for sentence expansion
382 if (!isNull(qp.getFirst("extended")))
383 extendToSentence = true;
384
385 List<String> foundries = qp.get("foundry");
386 List<String> layers = qp.get("layer");
387
388 try {
389 // Get match info
390 return index.getMatchInfo(
391 id,
392 "tokens",
393 info,
394 foundries,
395 layers,
396 includeSpans,
397 includeHighlights,
398 extendToSentence
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000399 ).toJsonString();
Nils Diewaldf6b351c2014-09-04 21:34:05 +0000400 }
401
402 // Nothing found
403 catch (QueryException qe) {
Nils Diewald979cc112014-09-23 19:53:54 +0000404 // Todo: Make KorapMatch rely on KorapResponse!
Nils Diewaldf6b351c2014-09-04 21:34:05 +0000405 KorapMatch km = new KorapMatch();
Nils Diewaldc471b182014-11-19 22:51:15 +0000406 km.addError(qe.getErrorCode(), qe.getMessage());
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000407 return km.toJsonString();
Nils Diewaldf6b351c2014-09-04 21:34:05 +0000408 }
409 };
410
411 // Response with error message
412 KorapMatch km = new KorapMatch();
Nils Diewaldc471b182014-11-19 22:51:15 +0000413 km.addError(601, "Unable to find index");
Nils Diewalde1ecd5e2014-11-27 02:17:24 +0000414 return km.toJsonString();
Nils Diewaldf6b351c2014-09-04 21:34:05 +0000415 };
416
Nils Diewaldff6f7662014-09-21 15:08:52 +0000417 /*
418 POST /collect/:result_id
419 POST /peek
420 POST /?text_id=...
421 POST /:text_id/
422
423 PUT /:text_id
424
425 DELETE /:text_id
426 DELETE /:corpus_sigle
427 DELETE /:corpus_sigle/:doc_sigle
428 DELETE /:corpus_sigle/:doc_sigle/:text_sigle
429 */
430
Nils Diewaldf6b351c2014-09-04 21:34:05 +0000431 @POST
432 @Path("/collection")
433 @Produces(MediaType.APPLICATION_JSON)
434 @Consumes(MediaType.APPLICATION_JSON)
435 public String collection (String json) {
436
437 // Get index
438 KorapIndex index = KorapNode.getIndex();
439
440 if (index == null)
441 return "{\"documents\" : -1, error\" : \"No index given\" }";
442
443 return "{}";
444 };
Nils Diewaldff6f7662014-09-21 15:08:52 +0000445
446
447
448 // Interceptor class
449 public class GZIPReaderInterceptor implements ReaderInterceptor {
450 @Override
451 public Object aroundReadFrom(ReaderInterceptorContext context)
452 throws IOException, WebApplicationException {
453 final InputStream originalInputStream = context.getInputStream();
454 context.setInputStream(new GZIPInputStream(originalInputStream));
455 return context.proceed();
456 };
457 };
458
459 public class GZIPWriterInterceptor implements WriterInterceptor {
460 @Override
461 public void aroundWriteTo(WriterInterceptorContext context)
462 throws IOException, WebApplicationException {
463 final OutputStream outputStream = context.getOutputStream();
464 context.setOutputStream(new GZIPOutputStream(outputStream));
465 context.proceed();
466 };
467 };
Nils Diewaldf6b351c2014-09-04 21:34:05 +0000468};