source: SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/rest/RestService.java @ 7034

Last change on this file since 7034 was 7034, checked in by Leif-Jöran, 8 years ago

Added fully visible but unstyled ADV view. Very similar to the spreadsheet export. Updated some of the paragraphs on the help page. Preparing for new version number.

File size: 11.9 KB
Line 
1package eu.clarin.sru.fcs.aggregator.rest;
2
3import com.fasterxml.jackson.annotation.JsonProperty;
4import com.fasterxml.jackson.core.JsonProcessingException;
5import com.fasterxml.jackson.databind.ObjectMapper;
6import com.fasterxml.jackson.databind.ObjectWriter;
7import eu.clarin.sru.client.SRUVersion;
8import eu.clarin.sru.fcs.aggregator.app.Aggregator;
9import eu.clarin.sru.fcs.aggregator.app.AggregatorConfiguration;
10import eu.clarin.sru.fcs.aggregator.app.AggregatorConfiguration.Params.WeblichtConfig;
11import static eu.clarin.sru.fcs.aggregator.app.ErrorHandler.PARAM_AGGREGATION_CONTEXT;
12import static eu.clarin.sru.fcs.aggregator.app.ErrorHandler.PARAM_MODE;
13import static eu.clarin.sru.fcs.aggregator.app.ErrorHandler.PARAM_QUERY;
14import eu.clarin.sru.fcs.aggregator.scan.Corpora;
15import eu.clarin.sru.fcs.aggregator.scan.Corpus;
16import eu.clarin.sru.fcs.aggregator.scan.FCSProtocolVersion;
17import eu.clarin.sru.fcs.aggregator.scan.Statistics;
18import eu.clarin.sru.fcs.aggregator.search.Result;
19import eu.clarin.sru.fcs.aggregator.search.Search;
20import eu.clarin.sru.fcs.aggregator.util.LanguagesISO693;
21import eu.clarin.sru.fcs.aggregator.search.Exports;
22import java.io.IOException;
23import java.net.URI;
24import java.util.ArrayList;
25import java.util.HashMap;
26import java.util.HashSet;
27import java.util.List;
28import java.util.Map;
29import java.util.Set;
30import javax.servlet.ServletContext;
31import javax.servlet.http.HttpServletRequest;
32import javax.ws.rs.FormParam;
33import javax.ws.rs.GET;
34import javax.ws.rs.POST;
35import javax.ws.rs.Path;
36import javax.ws.rs.PathParam;
37import javax.ws.rs.Produces;
38import javax.ws.rs.QueryParam;
39import javax.ws.rs.core.Context;
40import javax.ws.rs.core.MediaType;
41import javax.ws.rs.core.Response;
42import org.slf4j.LoggerFactory;
43
44/**
45 *
46 * @author edima
47 * @author ljo
48 * The REST API of the Aggregator (actually, it's a HTTP API, not very restful).
49 *
50 */
51@Produces(MediaType.APPLICATION_JSON)
52@Path("/")
53public class RestService {
54
55        private static final String EXPORT_FILENAME_PREFIX = "ClarinFederatedContentSearch-";
56        private static final String TCF_MEDIA_TYPE = "text/tcf+xml";
57        private static final String EXCEL_MEDIA_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
58        private static final String SEARCH_RESULTS_ENCODING = "UTF-8";
59
60        private static final org.slf4j.Logger log = LoggerFactory.getLogger(RestService.class);
61
62        ObjectWriter ow = new ObjectMapper().writerWithDefaultPrettyPrinter();
63
64        @Context
65        HttpServletRequest request;
66        @Context
67        ServletContext servletContext;
68
69        private String toJson(Object o) throws JsonProcessingException {
70                return ow.writeValueAsString(o);
71        }
72
73        @GET
74        @Path("corpora")
75        public Response getCorpora() throws IOException {
76                List<Corpus> corpora = Aggregator.getInstance().getCorpora().getCorpora();
77                return Response.ok(toJson(corpora)).build();
78        }
79
80        @GET
81        @Path("languages")
82        public Response getLanguages() throws IOException {
83                Set<String> codes = Aggregator.getInstance().getCorpora().getLanguages();
84                log.info("get language codes", codes);
85                Map<String, String> languages = LanguagesISO693.getInstance().getLanguageMap(codes);
86                return Response.ok(toJson(languages)).build();
87        }
88
89        @GET
90        @Path("init")
91        public Response getInit(@Context final HttpServletRequest request) throws IOException {
92                log.info("get initial data");
93                final Corpora corpora = Aggregator.getInstance().getCorpora();
94                final Object query = request.getSession().getAttribute(PARAM_QUERY);
95                final Object mode = request.getSession().getAttribute(PARAM_MODE);
96                final Object contextString = request.getSession().getAttribute(PARAM_AGGREGATION_CONTEXT);
97                Object j = new HashMap<String, Object>() {
98                        {
99                                if (query != null) {
100                                        put(PARAM_QUERY, query);
101                                        request.getSession().setAttribute(PARAM_QUERY, null);
102                                }
103                                if (mode != null) {
104                                        put(PARAM_MODE, mode);
105                                        request.getSession().setAttribute(PARAM_MODE, null);
106                                }
107                                if (contextString instanceof String) {
108                                        Object context = new ObjectMapper().readValue((String) contextString, Object.class);
109                                        put(PARAM_AGGREGATION_CONTEXT, context); // preselected corpora
110                                        request.getSession().setAttribute(PARAM_AGGREGATION_CONTEXT, null);
111                                }
112                                put("corpora", corpora.getCorpora());
113                                put("languages", LanguagesISO693.getInstance().getLanguageMap(corpora.getLanguages()));
114                                put("weblichtLanguages", Aggregator.getInstance().getParams().getWeblichtConfig().getAcceptedTcfLanguages());
115                        }
116                };
117                return Response.ok(toJson(j)).build();
118        }
119
120        @POST
121        @Path("search")
122        public Response postSearch(
123                        @FormParam("query") String query,
124                        @FormParam("queryType") String queryType,
125                        @FormParam("firstResultIndex") Integer firstResultIndex,
126                        @FormParam("numberOfResults") Integer numberOfResults,
127                        @FormParam("language") String language,
128                        @FormParam("corporaIds[]") List<String> corporaIds) throws Exception {
129                if (query == null || query.isEmpty()) {
130                        return Response.status(400).entity("'query' parameter expected").build();
131                }
132//              log.info("POST /search corporaIds: " + corporaIds);
133                if (corporaIds == null || corporaIds.isEmpty()) {
134                        return Response.status(400).entity("'corporaIds' parameter expected").build();
135                }
136                List<Corpus> corpora = Aggregator.getInstance().getCorpora().getCorporaByIds(new HashSet<String>(corporaIds));
137                if ("fcs".equals(queryType)) {
138                    List<Corpus> tmp = new ArrayList<Corpus>();
139                    for (Corpus corpus : corpora) {
140                        if (corpus.getEndpoint().getProtocol().equals(FCSProtocolVersion.VERSION_2)) {
141                            tmp.add(corpus);
142                        }
143                    }
144                    corpora = tmp;
145                }
146                if (corpora == null || corpora.isEmpty()) {
147                        return Response.status(503).entity("No corpora, please wait for the server to finish scanning").build();
148                }
149
150                if (firstResultIndex == null || firstResultIndex < 1) {
151                        firstResultIndex = 1;
152                }
153                if (firstResultIndex > 250) {
154                        firstResultIndex = 250;
155                }
156
157                if (numberOfResults == null || numberOfResults < 10) {
158                        numberOfResults = 10;
159                }
160                if (numberOfResults > 250) {
161                        numberOfResults = 250;
162                }
163                Search search = Aggregator.getInstance().startSearch("fcs".equals(queryType) ? SRUVersion.VERSION_2_0 : SRUVersion.VERSION_1_2,
164                                corpora, queryType, query, language, firstResultIndex, numberOfResults);
165                if (search == null) {
166                        return Response.status(500).entity("Initiating search failed").build();
167                }
168                URI uri = URI.create("" + search.getId());
169                return Response.created(uri).entity(uri).build();
170        }
171
172        public static class JsonSearch {
173
174                @JsonProperty
175                int inProgress = 0;
176                @JsonProperty
177                List<Result> results;
178
179                public JsonSearch(List<Result> results) {
180                        this.results = results;
181                }
182        }
183
184        @GET
185        @Path("search/{id}")
186        public Response getSearch(@PathParam("id") Long searchId,
187                        @QueryParam("corpusId") String corpusId) throws Exception {
188                Search search = Aggregator.getInstance().getSearchById(searchId);
189                if (search == null) {
190                        return Response.status(Response.Status.NOT_FOUND).entity("Search job not found").build();
191                }
192
193                JsonSearch js = new JsonSearch(search.getResults(corpusId));
194                for (Result r : js.results) {
195                        if (r.getInProgress()) {
196                                js.inProgress++;
197                        }
198                }
199                return Response.ok(js).build();
200        }
201
202        @POST
203        @Path("search/{id}")
204        public Response postSearchNextResults(@PathParam("id") Long searchId,
205                        @FormParam("corpusId") String corpusId,
206                        @FormParam("numberOfResults") Integer numberOfResults) throws Exception {
207                log.info("POST /search/{id}, corpusId: " + corpusId);
208                if (corpusId == null || corpusId.isEmpty()) {
209                        return Response.status(400).entity("'corpusId' parameter expected").build();
210                }
211                Search search = Aggregator.getInstance().getSearchById(searchId);
212                if (search == null) {
213                        return Response.status(Response.Status.NOT_FOUND).entity("Search job not found").build();
214                }
215                if (numberOfResults == null || numberOfResults < 10) {
216                        numberOfResults = 10;
217                }
218                if (numberOfResults > 250) {
219                        numberOfResults = 250;
220                }
221
222                boolean ret = search.searchForNextResults(corpusId, numberOfResults);
223                if (ret == false) {
224                        return Response.status(500).entity("Initiating subSearch failed").build();
225                }
226                URI uri = URI.create("" + search.getId());
227                return Response.created(uri).entity(uri).build();
228        }
229
230        @GET
231        @Path("search/{id}/download")
232        public Response downloadSearchResults(@PathParam("id") Long searchId,
233                        @QueryParam("corpusId") String corpusId,
234                        @QueryParam("filterLanguage") String filterLanguage,
235                        @QueryParam("format") String format) throws Exception {
236                Search search = Aggregator.getInstance().getSearchById(searchId);
237                if (search == null) {
238                        return Response.status(Response.Status.NOT_FOUND).entity("Search job not found").build();
239                }
240                if (filterLanguage == null || filterLanguage.isEmpty()) {
241                        filterLanguage = null;
242                }
243
244                if (format == null || format.trim().isEmpty() || format.trim().equals("text")) {
245                        String text = Exports.getExportText(search.getResults(corpusId), filterLanguage);
246                        return download(text, MediaType.TEXT_PLAIN, search.getQuery() + ".txt");
247                } else if (format.equals("tcf")) {
248                        byte[] bytes = Exports.getExportTCF(
249                                        search.getResults(corpusId), search.getSearchLanguage(), filterLanguage);
250                        return download(bytes, TCF_MEDIA_TYPE, search.getQuery() + ".xml");
251                } else if (format.equals("excel")) {
252                        byte[] bytes = Exports.getExportExcel(search.getResults(corpusId), filterLanguage);
253                        return download(bytes, EXCEL_MEDIA_TYPE, search.getQuery() + ".xls");
254                } else if (format.equals("csv")) {
255                        String csv = Exports.getExportCSV(search.getResults(corpusId), filterLanguage, ";");
256                        return download(csv, MediaType.TEXT_PLAIN, search.getQuery() + ".csv");
257                }
258
259                return Response.status(Response.Status.BAD_REQUEST)
260                                .entity("format parameter must be one of: text, tcf, excel, csv")
261                                .build();
262        }
263
264        Response download(Object entity, String mediaType, String filesuffix) {
265                if (entity == null) {
266                        return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
267                                        .entity("error while converting to the export format").build();
268                }
269                String filename = EXPORT_FILENAME_PREFIX + filesuffix;
270                return Response.ok(entity, mediaType)
271                                .header("Content-Disposition", "attachment; filename=\"" + filename + "\"")
272                                .build();
273        }
274
275        @GET
276        @Path("search/{id}/toWeblicht")
277        public Response sendSearchResultsToWeblicht(@PathParam("id") Long searchId,
278                        @QueryParam("filterLanguage") String filterLanguage,
279                        @QueryParam("corpusId") String corpusId) throws Exception {
280                Search search = Aggregator.getInstance().getSearchById(searchId);
281                if (search == null) {
282                        return Response.status(Response.Status.NOT_FOUND).entity("Search job not found").build();
283                }
284                if (filterLanguage == null || filterLanguage.isEmpty()) {
285                        filterLanguage = null;
286                }
287
288                String url = null;
289                byte[] bytes = Exports.getExportTCF(
290                                search.getResults(corpusId), search.getSearchLanguage(), filterLanguage);
291                if (bytes != null) {
292                        url = DataTransfer.uploadToDropOff(bytes, "text/tcf+xml", ".tcf");
293                }
294
295                WeblichtConfig weblicht = Aggregator.getInstance().getParams().getWeblichtConfig();
296                URI weblichtUri = new URI(weblicht.getUrl() + url);
297                return url == null
298                                ? Response.status(503).entity("error while exporting to weblicht").build()
299                                : Response.seeOther(weblichtUri).entity(weblichtUri).build();
300        }
301
302        @GET
303        @Path("statistics")
304        public Response getStatistics() throws IOException {
305                final Statistics scan = Aggregator.getInstance().getScanStatistics();
306                final Statistics search = Aggregator.getInstance().getSearchStatistics();
307                final AggregatorConfiguration.Params params = Aggregator.getInstance().getParams();
308
309                Object j = new HashMap<String, Object>() {
310                        {
311                                put("Last Scan", new HashMap<String, Object>() {
312                                        {
313                                                put("timeout", params.getENDPOINTS_SCAN_TIMEOUT_MS() / 1000.);
314                                                put("isScan", true);
315                                                put("institutions", scan.getInstitutions());
316                                                put("date", scan.getDate());
317                                        }
318                                });
319                                put("Recent Searches", new HashMap<String, Object>() {
320                                        {
321                                                put("timeout", params.getENDPOINTS_SEARCH_TIMEOUT_MS() / 1000.);
322                                                put("isScan", false);
323                                                put("institutions", search.getInstitutions());
324                                                put("date", scan.getDate());
325                                        }
326                                });
327                        }
328                };
329                return Response.ok(toJson(j)).build();
330        }
331
332}
Note: See TracBrowser for help on using the repository browser.