1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.exoplatform.commons.search.rest;
18
19 import io.swagger.annotations.*;
20 import org.apache.commons.lang.StringUtils;
21 import org.exoplatform.commons.search.domain.IndexingOperation;
22 import org.exoplatform.commons.search.index.IndexingOperationProcessor;
23 import org.exoplatform.commons.search.index.IndexingService;
24 import org.exoplatform.commons.search.index.IndexingServiceConnector;
25 import org.exoplatform.commons.search.index.impl.QueueIndexingService;
26 import org.exoplatform.commons.search.rest.resource.CollectionResource;
27 import org.exoplatform.commons.search.rest.resource.CollectionSizeResource;
28 import org.exoplatform.commons.search.rest.resource.ConnectorResource;
29 import org.exoplatform.commons.search.rest.resource.OperationResource;
30 import org.exoplatform.common.http.HTTPStatus;
31 import org.exoplatform.services.log.ExoLogger;
32 import org.exoplatform.services.log.Log;
33 import org.exoplatform.services.rest.resource.ResourceContainer;
34 import org.exoplatform.ws.frameworks.json.impl.JsonException;
35 import org.exoplatform.ws.frameworks.json.impl.JsonGeneratorImpl;
36 import org.exoplatform.ws.frameworks.json.value.JsonValue;
37
38 import javax.annotation.security.RolesAllowed;
39 import javax.ws.rs.*;
40 import javax.ws.rs.core.MediaType;
41 import javax.ws.rs.core.Response;
42 import java.io.Serializable;
43 import java.util.ArrayList;
44 import java.util.Calendar;
45 import java.util.List;
46
47
48
49
50
51
52
53 @Path(IndexingManagementRestServiceV1.BASE_VERSION_URI+ IndexingManagementRestServiceV1.INDEXING_MANAGEMENT_URI)
54 @RolesAllowed("administrators")
55 @Api(
56 value = IndexingManagementRestServiceV1.BASE_VERSION_URI+ IndexingManagementRestServiceV1.INDEXING_MANAGEMENT_URI,
57 description = "Entry point for Indexing Management resources",
58 basePath = IndexingManagementRestServiceV1.BASE_VERSION_URI+ IndexingManagementRestServiceV1.INDEXING_MANAGEMENT_URI
59 )
60 public class IndexingManagementRestServiceV1 implements ResourceContainer {
61
62 public final static String BASE_VERSION_URI = "/v1";
63 public final static String INDEXING_MANAGEMENT_URI = "/indexingManagement";
64 public final static String CONNECTORS_URI = "/connectors";
65 public final static String OPERATIONS_URI = "/operations";
66 public final static String ERRORS_URI = "/errors";
67
68 private final static Log LOG = ExoLogger.getLogger(IndexingManagementRestServiceV1.class);
69
70 private QueueIndexingService indexingService;
71 private IndexingOperationProcessor indexingOperationProcessor;
72
73 public IndexingManagementRestServiceV1(IndexingService indexingService, IndexingOperationProcessor indexingOperationProcessor) {
74 this.indexingService = (QueueIndexingService) indexingService;
75 this.indexingOperationProcessor = indexingOperationProcessor;
76 }
77
78
79
80 @GET
81 @Path(IndexingManagementRestServiceV1.CONNECTORS_URI)
82 @Produces(MediaType.APPLICATION_JSON)
83 @RolesAllowed("administrators")
84 @ApiOperation(value = "Return all Indexing Connectors")
85 @ApiResponses(value = {
86 @ApiResponse(code = 200, message = "Successful retrieval of all Indexing Connectors"),
87 @ApiResponse(code = 500, message = "Can't generate JSON file") })
88 public Response getConnectors(
89 @ApiParam(
90 value = "The name of a JavaScript function to be used as the JSONP callback",
91 required = false)
92 @QueryParam("jsonp")
93 String jsonp,
94 @ApiParam(
95 value = "Tell the service if it must return the size of the collection in the store",
96 required = false)
97 @QueryParam("returnSize")
98 boolean returnSize
99 ) {
100
101
102 List<IndexingServiceConnector> connectors = new ArrayList<>(indexingOperationProcessor.getConnectors().values());
103
104 CollectionResource<IndexingServiceConnector> connectorData;
105
106
107 if (returnSize) {
108 int connectorNb = indexingOperationProcessor.getConnectors().size();
109 connectorData = new CollectionSizeResource<>(connectors, connectorNb);
110 }
111 else {
112 connectorData = new CollectionResource<>(connectors);
113 }
114
115 Response.ResponseBuilder response;
116
117
118 if (StringUtils.isNotBlank(jsonp)) {
119 try {
120 response = buildJsonCallBack(connectorData, jsonp);
121 } catch (JsonException e) {
122 LOG.error(e);
123 response = Response.status(HTTPStatus.INTERNAL_ERROR);
124 }
125 }
126 else {
127 response = Response.ok(connectorData, MediaType.APPLICATION_JSON);
128 }
129
130 return response.build();
131 }
132
133 @GET
134 @Path(IndexingManagementRestServiceV1.CONNECTORS_URI+"/{connectorType}")
135 @Produces(MediaType.APPLICATION_JSON)
136 @RolesAllowed("administrators")
137 @ApiOperation(value = "Return the Indexing Connectors with the specified Connector Type")
138 @ApiResponses(value = {
139 @ApiResponse(code = 200, message = "Successful retrieval of the Indexing Connector"),
140 @ApiResponse(code = 404, message = "Indexing Connector with specified type Not Found"),
141 @ApiResponse(code = 500, message = "Can't generate JSON file") })
142 public Response getConnector(
143 @ApiParam(
144 value = "Type of the Indexing Connector to retrieve",
145 required = true)
146 @PathParam("connectorType")
147 String connectorType,
148 @ApiParam(
149 value = "The name of a JavaScript function to be used as the JSONP callback",
150 required = false)
151 @QueryParam("jsonp")
152 String jsonp
153 ) {
154
155 IndexingServiceConnector connector = indexingOperationProcessor.getConnectors().get(connectorType);
156
157 if (connector == null) return Response.status(HTTPStatus.NOT_FOUND).build();
158
159 Response.ResponseBuilder response;
160
161
162 if (StringUtils.isNotBlank(jsonp)) {
163 try {
164 response = buildJsonCallBack(connector, jsonp);
165 } catch (JsonException e) {
166 LOG.error(e);
167 response = Response.status(HTTPStatus.INTERNAL_ERROR);
168 }
169 }
170 else {
171 response = Response.ok(connector, MediaType.APPLICATION_JSON);
172 }
173
174 return response.build();
175 }
176
177 @PUT
178 @Path(IndexingManagementRestServiceV1.CONNECTORS_URI+"/{connectorType}")
179 @Consumes(MediaType.APPLICATION_JSON)
180 @RolesAllowed("administrators")
181 @ApiOperation(value = "Update an Indexing Connector to enable / disable it")
182 @ApiResponses(value = {
183 @ApiResponse(code = 200, message = "Successful update of the Indexing Connector"),
184 @ApiResponse(code = 404, message = "Indexing Connector with specified type Not Found") })
185 public Response updateConnector(
186 @ApiParam(
187 value = "Type of the Indexing Connector to update",
188 required = true)
189 @PathParam("connectorType")
190 String connectorType,
191 @ApiParam(
192 value = "An Indexing Connector Resource",
193 required = true)
194 ConnectorResource connectorResource
195 ) {
196
197 if (indexingOperationProcessor.getConnectors().get(connectorType) == null) {
198 return Response.status(HTTPStatus.NOT_FOUND).build();
199 }
200
201 indexingOperationProcessor.getConnectors().get(connectorType).setEnable(connectorResource.isEnable());
202
203 return Response.ok().build();
204 }
205
206
207
208 @GET
209 @Path(IndexingManagementRestServiceV1.OPERATIONS_URI)
210 @Produces(MediaType.APPLICATION_JSON)
211 @RolesAllowed("administrators")
212 @ApiOperation(value = "Return all Indexing Operations")
213 @ApiResponses(value = {
214 @ApiResponse(code = 200, message = "Successful retrieval of all Indexing Operations"),
215 @ApiResponse(code = 500, message = "Can't generate JSON file") })
216 public Response getOperations(
217 @ApiParam(
218 value = "The name of a JavaScript function to be used as the JSONP callback",
219 required = false)
220 @QueryParam("jsonp")
221 String jsonp,
222 @ApiParam(value = "The starting point when paging through a list of entities",
223 required = false)
224 @QueryParam("offset")
225 int offset,
226 @ApiParam(value = "The maximum number of results when paging through a list of entities. " +
227 "If not specified or exceed the *query_limit* configuration of Indexing Management rest service, " +
228 "it will use the *query_limit*",
229 required = false)
230 @QueryParam("limit")
231 int limit,
232 @ApiParam(
233 value = "Tell the service if it must return the size of the collection in the store",
234 required = false)
235 @QueryParam("returnSize")
236 boolean returnSize
237 ) {
238
239 offset = parseOffset(offset);
240 limit = parseLimit(limit);
241
242
243 List<IndexingOperation> operations = new ArrayList<>(indexingService.getOperations(offset, limit));
244
245 CollectionResource<IndexingOperation> operationData;
246
247
248 if (returnSize) {
249 int operationNb = indexingService.getNumberOperations().intValue();
250 operationData = new CollectionSizeResource<>(operations, operationNb);
251 }
252 else {
253 operationData = new CollectionResource<>(operations);
254 }
255 operationData.setLimit(limit);
256 operationData.setOffset(offset);
257
258 Response.ResponseBuilder response;
259
260
261 if (StringUtils.isNotBlank(jsonp)) {
262 try {
263 response = buildJsonCallBack(operationData, jsonp);
264 } catch (JsonException e) {
265 LOG.error(e);
266 response = Response.status(HTTPStatus.INTERNAL_ERROR);
267 }
268 }
269 else {
270 response = Response.ok(operationData, MediaType.APPLICATION_JSON);
271 }
272
273 return response.build();
274 }
275
276 @POST
277 @Path(IndexingManagementRestServiceV1.OPERATIONS_URI)
278 @Consumes(MediaType.APPLICATION_JSON)
279 @RolesAllowed("administrators")
280 @ApiOperation(value = "Add an Indexing Operation to the queue")
281 @ApiResponses(value = {
282 @ApiResponse(code = 201, message = "Indexing Operation successfully added"),
283 @ApiResponse(code = 400, message = "The specified Indexing Operation is unknown")})
284 public Response addOperation(
285 @ApiParam(
286 value = "An Indexing Operation Resource",
287 required = true)
288 OperationResource operationResource
289 ) {
290
291 switch (operationResource.getOperation()) {
292
293 case "init": indexingService.init(operationResource.getEntityType());
294 break;
295 case "index": indexingService.index(operationResource.getEntityType(), operationResource.getEntityId());
296 break;
297 case "reindex": indexingService.reindex(operationResource.getEntityType(), operationResource.getEntityId());
298 break;
299 case "unindex": indexingService.unindex(operationResource.getEntityType(), operationResource.getEntityId());
300 break;
301 case "reindexAll": indexingService.reindexAll(operationResource.getEntityType());
302 break;
303 case "unindexAll": indexingService.unindexAll(operationResource.getEntityType());
304 break;
305 default: return getBadRequestResponse().build();
306
307 }
308
309 return Response.status(HTTPStatus.CREATED).build();
310 }
311
312 @DELETE
313 @Path(IndexingManagementRestServiceV1.OPERATIONS_URI)
314 @RolesAllowed("administrators")
315 @ApiOperation(value = "Delete all Indexing Operation")
316 @ApiResponses(value = {
317 @ApiResponse(code = 200, message = "Successful deletion of all Indexing Operations") })
318 public Response deleteOperations() {
319
320 indexingService.deleteAllOperations();
321
322 return Response.ok().build();
323
324 }
325
326 @GET
327 @Path(IndexingManagementRestServiceV1.OPERATIONS_URI+"/{operationId}")
328 @Produces(MediaType.APPLICATION_JSON)
329 @RolesAllowed("administrators")
330 @ApiOperation(value = "Return the Indexing Operation with the specified Opertion Id")
331 @ApiResponses(value = {
332 @ApiResponse(code = 200, message = "Successful retrieval of the Indexing Operation"),
333 @ApiResponse(code = 404, message = "Indexing Operation with specified Id Not Found"),
334 @ApiResponse(code = 500, message = "Can't generate JSON file") })
335 public Response getOperation(
336 @ApiParam(
337 value = "Id of the Indexing Operation to retrieve",
338 required = true)
339 @PathParam("operationId")
340 String operationId,
341 @ApiParam(
342 value = "The name of a JavaScript function to be used as the JSONP callback",
343 required = false)
344 @QueryParam("jsonp")
345 String jsonp
346 ) {
347
348 IndexingOperation operation = indexingService.getOperation(operationId);
349
350 if (operation == null) return Response.status(HTTPStatus.NOT_FOUND).build();
351
352 Response.ResponseBuilder response;
353
354
355 if (StringUtils.isNotBlank(jsonp)) {
356 try {
357 response = buildJsonCallBack(operation, jsonp);
358 } catch (JsonException e) {
359 LOG.error(e);
360 response = Response.status(HTTPStatus.INTERNAL_ERROR);
361 }
362 }
363 else {
364 response = Response.ok(operation, MediaType.APPLICATION_JSON);
365 }
366
367 return response.build();
368
369 }
370
371 @DELETE
372 @Path(IndexingManagementRestServiceV1.OPERATIONS_URI+"/{operationId}")
373 @RolesAllowed("administrators")
374 @ApiOperation(value = "Delete a specified Indexing Operation")
375 @ApiResponses(value = {
376 @ApiResponse(code = 200, message = "Successful deletion of the Indexing Operations"),
377 @ApiResponse(code = 404, message = "Indexing Operation with specified Id Not Found") })
378 public Response DeleteOperation(
379 @ApiParam(
380 value = "Id of the Indexing Operation to delete",
381 required = true)
382 @PathParam("operationId")
383 String operationId
384 ) {
385
386 IndexingOperation operation = indexingService.getOperation(operationId);
387
388 if (operation == null) return Response.status(HTTPStatus.NOT_FOUND).build();
389
390 indexingService.deleteOperation(operation);
391
392 return Response.ok().build();
393
394 }
395
396
397
398
399
400
401
402
403
404 private Response.ResponseBuilder buildJsonCallBack(Serializable resource, String jsonp) throws JsonException {
405 JsonValue value = new JsonGeneratorImpl().createJsonObject(resource);
406 StringBuilder sb = new StringBuilder(jsonp);
407 sb.append("(").append(value).append(");");
408 return Response.ok(sb.toString(), new MediaType("text", "javascript"));
409 }
410
411
412
413
414 private int parseLimit(int limit) {
415 return (limit <=0 || limit > CollectionResource.QUERY_LIMIT) ? CollectionResource.QUERY_LIMIT : limit;
416 }
417
418
419
420
421 private int parseOffset(int offset) {
422 return (offset <=0) ? 0 : offset;
423 }
424
425 private Response.ResponseBuilder getBadRequestResponse() {
426 Calendar today = Calendar.getInstance();
427 if (today.get(Calendar.DAY_OF_MONTH) == 1 && today.get(Calendar.MONTH) == Calendar.APRIL) {
428 return Response.status(418);
429 }
430 return Response.status(HTTPStatus.BAD_REQUEST);
431 }
432
433 }
434