Performance Improvements for Gizmo bulk API
[aai/champ.git] / champ-service / src / main / java / org / onap / champ / ChampRESTAPI.java
1 /**
2  * ============LICENSE_START==========================================
3  * org.onap.aai
4  * ===================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017-2018 Amdocs
7  * ===================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END============================================
20  */
21 package org.onap.champ;
22
23 import java.io.IOException;
24 import java.security.NoSuchAlgorithmException;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Optional;
30 import java.util.Timer;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33 import javax.servlet.http.HttpServletRequest;
34 import javax.ws.rs.Consumes;
35 import javax.ws.rs.DELETE;
36 import javax.ws.rs.GET;
37 import javax.ws.rs.POST;
38 import javax.ws.rs.PUT;
39 import javax.ws.rs.Path;
40 import javax.ws.rs.PathParam;
41 import javax.ws.rs.Produces;
42 import javax.ws.rs.QueryParam;
43 import javax.ws.rs.core.Context;
44 import javax.ws.rs.core.EntityTag;
45 import javax.ws.rs.core.HttpHeaders;
46 import javax.ws.rs.core.MediaType;
47 import javax.ws.rs.core.Response;
48 import javax.ws.rs.core.Response.Status;
49 import javax.ws.rs.core.UriInfo;
50 import org.json.JSONException;
51 import org.json.JSONObject;
52 import org.onap.aai.champcore.ChampTransaction;
53 import org.onap.aai.champcore.exceptions.ChampObjectNotExistsException;
54 import org.onap.aai.champcore.exceptions.ChampRelationshipNotExistsException;
55 import org.onap.aai.champcore.exceptions.ChampTransactionException;
56 import org.onap.aai.champcore.exceptions.ChampUnmarshallingException;
57 import org.onap.aai.champcore.model.ChampObject;
58 import org.onap.aai.champcore.model.ChampRelationship;
59 import org.onap.aai.cl.api.Logger;
60 import org.onap.aai.cl.eelf.LoggerFactory;
61 import org.onap.champ.async.ChampAsyncRequestProcessor;
62 import org.onap.champ.entity.ChampBulkPayload;
63 import org.onap.champ.entity.ChampBulkResponse;
64 import org.onap.champ.entity.ChampObjectDeserializer;
65 import org.onap.champ.entity.ChampObjectSerializer;
66 import org.onap.champ.entity.ChampRelationshipDeserializer;
67 import org.onap.champ.entity.ChampRelationshipSerializer;
68 import org.onap.champ.exception.ChampServiceException;
69 import org.onap.champ.service.ChampDataService;
70 import org.onap.champ.service.logging.ChampMsgs;
71 import org.onap.champ.service.logging.LoggingUtil;
72 import org.onap.champ.util.ChampProperties;
73 import org.onap.champ.util.ChampServiceConstants;
74 import org.onap.champ.util.HttpHeadersValidator;
75 import org.onap.champ.util.etag.EtagGenerator;
76 import com.fasterxml.jackson.core.JsonProcessingException;
77 import com.fasterxml.jackson.databind.ObjectMapper;
78 import com.fasterxml.jackson.databind.module.SimpleModule;
79
80 @Path(value = "/services/champ-service/v1/")
81 public class ChampRESTAPI {
82
83   private ObjectMapper mapper;
84
85   private ChampDataService champDataService;
86   private EtagGenerator etagGenerator;
87   private HttpHeadersValidator httpHeadersValidator;
88   private String TRANSACTION_METHOD = "method";
89   private Timer timer;
90
91   private Logger logger = LoggerFactory.getInstance().getLogger(ChampRESTAPI.class);
92   Logger auditLogger = LoggerFactory.getInstance().getAuditLogger(ChampRESTAPI.class.getName());
93   private static Logger metricsLogger = LoggerFactory.getInstance().getMetricsLogger(ChampRESTAPI.class.getName());
94   private static final Pattern QUERY_OBJECT_ID_URL_MATCH = Pattern.compile("_reserved_(.*)");
95
96   public ChampRESTAPI(ChampDataService champDataService, ChampAsyncRequestProcessor champAsyncRequestProcessor) throws NoSuchAlgorithmException {
97     this.champDataService = champDataService;
98
99     // Async request handling is optional.
100     if (champAsyncRequestProcessor != null) {
101       timer = new Timer("ChampAsyncRequestProcessor-1");
102       timer.schedule(champAsyncRequestProcessor, champAsyncRequestProcessor.getRequestPollingTimeSeconds(),
103           champAsyncRequestProcessor.getRequestPollingTimeSeconds());
104     }
105
106     mapper = new ObjectMapper();
107     SimpleModule module = new SimpleModule();
108     module.addSerializer(ChampObject.class, new ChampObjectSerializer());
109     module.addDeserializer(ChampObject.class, new ChampObjectDeserializer());
110     module.addSerializer(ChampRelationship.class, new ChampRelationshipSerializer());
111     module.addDeserializer(ChampRelationship.class, new ChampRelationshipDeserializer());
112     mapper.registerModule(module);
113
114     etagGenerator = new EtagGenerator();
115     httpHeadersValidator = new HttpHeadersValidator();
116   }
117
118   @GET
119   @Path("echo")
120   @Produces(MediaType.TEXT_PLAIN)
121   public Response echo() {
122     return Response.ok().entity("alive").build();
123   }
124
125   @GET
126   @Path("objects/{objectId}")
127   @Produces(MediaType.APPLICATION_JSON)
128   public Response getObject(@PathParam("objectId") String objectId, @QueryParam("transactionId") String tId,
129       @Context HttpHeaders headers, @Context UriInfo uriInfo, @Context HttpServletRequest req) {
130     LoggingUtil.initMdcContext(req, headers);
131     long startTimeInMs = System.currentTimeMillis();
132     logger.info(ChampMsgs.INCOMING_REQUEST, tId, objectId);
133
134     Response response = null;
135     ChampObject retrieved;
136
137     try {
138       httpHeadersValidator.validateRequestHeaders(headers);
139       ChampTransaction transaction = champDataService.getTransaction(tId);
140
141       if (tId != null && transaction == null) {
142         throw new ChampServiceException("transactionId not found", Status.BAD_REQUEST);
143       }
144       retrieved = champDataService.getObject(objectId, Optional.ofNullable(transaction));
145       if (retrieved == null) {
146         response = Response.status(Status.NOT_FOUND).entity(objectId + " not found").build();
147       } else {
148         EntityTag etag = new EntityTag(etagGenerator.computeHashForChampObject(retrieved));
149         response = Response.status(Status.OK).entity(mapper.writeValueAsString(retrieved)).tag(etag).build();
150       }
151
152     } catch (JsonProcessingException e) {
153       response = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
154     } catch (ChampServiceException ce) {
155       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
156     } catch (Exception e) {
157         response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
158         LoggingUtil.logInternalError(logger, e);
159     } finally {
160       if (response != null) {
161         logger.debug(response.getEntity().toString());
162       }
163       LoggingUtil.logRestRequest(logger, auditLogger, req, response);
164       metricsLogger.info(ChampMsgs.PROCESSED_REQUEST, "GET", Long.toString(System.currentTimeMillis() - startTimeInMs));
165     }
166
167     return response;
168   }
169
170   @DELETE
171   @Path("objects/{objectId}")
172   public Response deleteObject(@PathParam("objectId") String objectId, @QueryParam("transactionId") String tId,
173       @Context HttpHeaders headers, @Context UriInfo uriInfo, @Context HttpServletRequest req) {
174     LoggingUtil.initMdcContext(req, headers);
175     long startTimeInMs = System.currentTimeMillis();
176     logger.info(ChampMsgs.INCOMING_REQUEST, tId, objectId);
177     Response response = null;
178     try {
179       httpHeadersValidator.validateRequestHeaders(headers);
180       ChampTransaction transaction = champDataService.getTransaction(tId);
181
182       if (tId != null && transaction == null) {
183         throw new ChampServiceException("transactionId not found", Status.BAD_REQUEST);
184       }
185       champDataService.deleteObject(objectId, Optional.ofNullable(transaction));
186
187       response = Response.status(Status.OK).build();
188     } catch (ChampObjectNotExistsException e) {
189       response = Response.status(Status.NOT_FOUND).entity(objectId + " not found").build();
190     } catch (ChampServiceException ce) {
191       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
192     } catch (ChampTransactionException | ChampUnmarshallingException e) {
193       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
194     } finally {
195       LoggingUtil.logRestRequest(logger, auditLogger, req, response);
196       metricsLogger.info(ChampMsgs.PROCESSED_REQUEST, "DELETE",
197           Long.toString(System.currentTimeMillis() - startTimeInMs));
198     }
199     return response;
200   }
201
202   @POST
203   @Path("objects")
204   @Consumes(MediaType.APPLICATION_JSON)
205   @Produces(MediaType.APPLICATION_JSON)
206   public Response postObject(String champObj, @QueryParam("transactionId") String tId, @Context HttpHeaders headers,
207       @Context UriInfo uriInfo, @Context HttpServletRequest req) {
208     LoggingUtil.initMdcContext(req, headers);
209     long startTimeInMs = System.currentTimeMillis();
210     logger.info(ChampMsgs.INCOMING_REQUEST, tId, champObj);
211     Response response = null;
212     try {
213       httpHeadersValidator.validateRequestHeaders(headers);
214       ChampTransaction transaction = champDataService.getTransaction(tId);
215       if (tId != null && transaction == null) {
216         throw new ChampServiceException("transactionId not found", Status.BAD_REQUEST);
217       }
218       ChampObject champObject = mapper.readValue(champObj, ChampObject.class);
219
220       ChampObject created = champDataService.storeObject(champObject, Optional.ofNullable(transaction));
221       EntityTag eTag = new EntityTag(etagGenerator.computeHashForChampObject(created));
222       response = Response.status(Status.CREATED).entity(mapper.writeValueAsString(created)).tag(eTag).build();
223     } catch (IOException e) {
224       response = Response.status(Status.BAD_REQUEST).entity("Unable to parse the payload").build();
225     } catch (ChampServiceException ce) {
226       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
227     } catch (IllegalArgumentException e) {
228       response = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
229     } catch (Exception e) {
230       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
231       LoggingUtil.logInternalError(logger, e);
232     } finally {
233       LoggingUtil.logRestRequest(logger, auditLogger, req, response);
234       metricsLogger.info(ChampMsgs.PROCESSED_REQUEST, "POST",
235           Long.toString(System.currentTimeMillis() - startTimeInMs));
236     }
237     return response;
238   }
239
240   @PUT
241   @Path("objects/{objectId}")
242   @Consumes(MediaType.APPLICATION_JSON)
243   @Produces(MediaType.APPLICATION_JSON)
244   public Response putObject(@PathParam("objectId") String objectId, String champObj,
245       @QueryParam("transactionId") String tId, @Context HttpHeaders headers, @Context UriInfo uriInfo,
246       @Context HttpServletRequest req) {
247     LoggingUtil.initMdcContext(req, headers);
248     long startTimeInMs = System.currentTimeMillis();
249     logger.info(ChampMsgs.INCOMING_REQUEST, tId, objectId + " " + champObj);
250
251     Response response = null;
252     try {
253       httpHeadersValidator.validateRequestHeaders(headers);
254       ChampTransaction transaction = champDataService.getTransaction(tId);
255       if (tId != null && transaction == null) {
256         throw new ChampServiceException("transactionId not found", Status.BAD_REQUEST);
257       }
258
259       ChampObject co = mapper.readValue(champObj, ChampObject.class);
260       // check if key is present or if it equals the key that is in the URI
261       ChampObject updated = champDataService.replaceObject(co, objectId, Optional.ofNullable(transaction));
262       EntityTag eTag = new EntityTag(etagGenerator.computeHashForChampObject(updated));
263       response = Response.status(Status.OK).entity(mapper.writeValueAsString(updated)).tag(eTag).build();
264     } catch (IOException e) {
265       response = Response.status(Status.BAD_REQUEST).entity("Unable to parse the payload").build();
266     } catch (ChampServiceException ce) {
267       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
268     } catch (IllegalArgumentException e) {
269       response = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
270     } catch (Exception e) {
271       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
272       LoggingUtil.logInternalError(logger, e);
273     } finally {
274       LoggingUtil.logRestRequest(logger, auditLogger, req, response);
275       metricsLogger.info(ChampMsgs.PROCESSED_REQUEST, "PUT", Long.toString(System.currentTimeMillis() - startTimeInMs));
276     }
277     return response;
278   }
279
280   @GET
281   @Path("objects/relationships/{oId}")
282   @Produces(MediaType.APPLICATION_JSON)
283   public Response getEdges(@PathParam("oId") String oId, @QueryParam("transactionId") String tId,
284       @Context HttpHeaders headers, @Context UriInfo uriInfo, @Context HttpServletRequest req) {
285     LoggingUtil.initMdcContext(req, headers);
286     long startTimeInMs = System.currentTimeMillis();
287     List<ChampRelationship> retrieved;
288     Response response = null;
289     logger.info(ChampMsgs.INCOMING_REQUEST, tId, oId);
290     try {
291       httpHeadersValidator.validateRequestHeaders(headers);
292       ChampTransaction transaction = tId == null ? null : champDataService.getTransaction(tId);
293
294       if (tId != null && transaction == null) {
295         throw new ChampServiceException("No transaction found for transaction ID: " + tId, Status.BAD_REQUEST);
296       }
297       retrieved = champDataService.getRelationshipsByObject(oId, Optional.ofNullable(transaction));
298       if (retrieved == null) {
299         response = Response.status(Status.NOT_FOUND).entity(oId + " not found").build();
300         return response;
301       }
302       EntityTag eTag = new EntityTag(etagGenerator.computeHashForChampRelationships(retrieved));
303       response = Response.status(Status.OK).entity(mapper.writeValueAsString(retrieved)).tag(eTag).build();
304     } catch (JsonProcessingException e) {
305       response = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
306     } catch (ChampServiceException ce) {
307       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
308     } catch (Exception e) {
309       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
310       LoggingUtil.logInternalError(logger, e);
311     } finally {
312       LoggingUtil.logRestRequest(logger, auditLogger, req, response);
313       metricsLogger.info(ChampMsgs.PROCESSED_REQUEST, "GET", Long.toString(System.currentTimeMillis() - startTimeInMs));
314     }
315     return response;
316   }
317
318   @GET
319   @Path("objects/filter/")
320   @Produces(MediaType.APPLICATION_JSON)
321   public Response filterObject(@Context HttpHeaders headers, @Context UriInfo uriInfo,
322       @Context HttpServletRequest req) {
323     LoggingUtil.initMdcContext(req, headers);
324     long startTimeInMs = System.currentTimeMillis();
325     String propertiesKey = ChampProperties.get(ChampServiceConstants.CHAMP_COLLECTION_PROPERTIES_KEY);
326     List<ChampObject> champObjects;
327     Map<String, Object> filter = new HashMap<>();
328
329     for (Map.Entry<String, List<String>> e : uriInfo.getQueryParameters().entrySet()) {
330       if ((!e.getKey().equals(propertiesKey)) && !reservedKeyMatcher ( QUERY_OBJECT_ID_URL_MATCH, e.getKey () )) {
331         filter.put(e.getKey(), e.getValue().get(0));
332       }
333     }
334
335     HashSet<String> properties;
336     if (uriInfo.getQueryParameters().containsKey(propertiesKey)) {
337       properties = new HashSet<>(uriInfo.getQueryParameters().get(propertiesKey));
338     } else {
339       properties = new HashSet<>();
340     }
341
342     Response response = null;
343     try {
344       httpHeadersValidator.validateRequestHeaders(headers);
345       champObjects = champDataService.queryObjects(filter, properties);
346       EntityTag eTag = new EntityTag(etagGenerator.computeHashForChampObjects(champObjects));
347       response = Response.status(Status.OK).type(MediaType.APPLICATION_JSON).tag(eTag).entity(mapper.writeValueAsString(champObjects))
348           .build();
349     } catch (JsonProcessingException e) {
350       response = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
351     } catch (ChampServiceException e1) {
352       response = Response.status(e1.getHttpStatus()).entity(e1.getMessage()).build();
353     } catch (Exception e) {
354       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
355       LoggingUtil.logInternalError(logger, e);
356     } finally {
357       LoggingUtil.logRestRequest(logger, auditLogger, req, response);
358       metricsLogger.info(ChampMsgs.PROCESSED_REQUEST, "GET", Long.toString(System.currentTimeMillis() - startTimeInMs));
359     }
360     return response;
361   }
362
363   @GET
364   @Path("relationships/{rId}")
365   @Produces(MediaType.APPLICATION_JSON)
366   public Response getRelationship(@PathParam("rId") String rId, @QueryParam("transactionId") String tId,
367       @Context HttpHeaders headers, @Context UriInfo uriInfo, @Context HttpServletRequest req) {
368     LoggingUtil.initMdcContext(req, headers);
369     long startTimeInMs = System.currentTimeMillis();
370     logger.info(ChampMsgs.INCOMING_REQUEST, tId, rId);
371     ChampRelationship retrieved;
372     Response response = null;
373     try {
374       httpHeadersValidator.validateRequestHeaders(headers);
375       ChampTransaction transaction = champDataService.getTransaction(tId);
376
377       if (tId != null && transaction == null) {
378         throw new ChampServiceException("transactionId not found", Status.BAD_REQUEST);
379       }
380       retrieved = champDataService.getRelationship(rId, Optional.ofNullable(transaction));
381       if (retrieved == null) {
382         response = Response.status(Status.NOT_FOUND).entity(rId + " not found").build();
383         return response;
384       }
385       EntityTag eTag = new EntityTag(etagGenerator.computeHashForChampRelationship(retrieved));
386       response = Response.status(Status.OK).entity(mapper.writeValueAsString(retrieved)).tag(eTag).build();
387
388     } catch (IOException e) {
389       response = Response.status(Status.BAD_REQUEST).entity("Unable to parse the payload").build();
390     } catch (ChampServiceException ce) {
391       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
392     } catch (Exception e) {
393       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
394       LoggingUtil.logInternalError(logger, e);
395     } finally {
396       LoggingUtil.logRestRequest(logger, auditLogger, req, response);
397       metricsLogger.info(ChampMsgs.PROCESSED_REQUEST, "GET", Long.toString(System.currentTimeMillis() - startTimeInMs));
398     }
399     return response;
400   }
401
402   @POST
403   @Path("relationships")
404   @Consumes(MediaType.APPLICATION_JSON)
405   @Produces(MediaType.APPLICATION_JSON)
406   public Response postRelationships(String relationship, @QueryParam("transactionId") String tId,
407       @Context HttpHeaders headers, @Context UriInfo uriInfo, @Context HttpServletRequest req) {
408     LoggingUtil.initMdcContext(req, headers);
409     long startTimeInMs = System.currentTimeMillis();
410     logger.info(ChampMsgs.INCOMING_REQUEST, tId, relationship);
411     Response response = null;
412     try {
413       httpHeadersValidator.validateRequestHeaders(headers);
414       ChampTransaction transaction = champDataService.getTransaction(tId);
415       if (tId != null && transaction == null) {
416         throw new ChampServiceException("transactionId not found", Status.BAD_REQUEST);
417       }
418       ChampRelationship r = mapper.readValue(relationship, ChampRelationship.class);
419
420       ChampRelationship created = champDataService.storeRelationship(r, Optional.ofNullable(transaction));
421       EntityTag eTag = new EntityTag(etagGenerator.computeHashForChampRelationship(created));
422       response = Response.status(Status.CREATED).entity(mapper.writeValueAsString(created)).tag(eTag).build();
423     } catch (IOException e) {
424       response = Response.status(Status.BAD_REQUEST).entity("Unable to parse the payload").build();
425     } catch (ChampServiceException ce) {
426       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
427     } catch (IllegalArgumentException e) {
428       response = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
429     } catch (Exception e) {
430       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
431       LoggingUtil.logInternalError(logger, e);
432     } finally {
433       LoggingUtil.logRestRequest(logger, auditLogger, req, response);
434       metricsLogger.info(ChampMsgs.PROCESSED_REQUEST, "POST",
435           Long.toString(System.currentTimeMillis() - startTimeInMs));
436     }
437     return response;
438   }
439
440   @PUT
441   @Path("relationships/{rId}")
442   @Consumes(MediaType.APPLICATION_JSON)
443   @Produces(MediaType.APPLICATION_JSON)
444   public Response updateRelationship(@PathParam("rId") String rId, String relationship,
445       @QueryParam("transactionId") String tId, @Context HttpHeaders headers, @Context UriInfo uriInfo,
446       @Context HttpServletRequest req) {
447     LoggingUtil.initMdcContext(req, headers);
448     long startTimeInMs = System.currentTimeMillis();
449     logger.info(ChampMsgs.INCOMING_REQUEST, tId, relationship);
450
451     Response response = null;
452     try {
453       httpHeadersValidator.validateRequestHeaders(headers);
454       ChampTransaction transaction = champDataService.getTransaction(tId);
455       if (tId != null && transaction == null) {
456         throw new ChampServiceException("transactionId not found", Status.BAD_REQUEST);
457       }
458       ChampRelationship r = mapper.readValue(relationship, ChampRelationship.class);
459       ChampRelationship updated = champDataService.updateRelationship(r, rId, Optional.ofNullable(transaction));
460       EntityTag eTag = new EntityTag(etagGenerator.computeHashForChampRelationship(updated));
461       response = Response.status(Status.OK).entity(mapper.writeValueAsString(updated)).tag(eTag).build();
462     } catch (IOException e) {
463       response = Response.status(Status.BAD_REQUEST).entity("Unable to parse the payload").build();
464     } catch (ChampServiceException ce) {
465       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
466     } catch (IllegalArgumentException e) {
467       response = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
468     } catch (Exception e) {
469       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
470       LoggingUtil.logInternalError(logger, e);
471     } finally {
472       LoggingUtil.logRestRequest(logger, auditLogger, req, response);
473       metricsLogger.info(ChampMsgs.PROCESSED_REQUEST, "PUT", Long.toString(System.currentTimeMillis() - startTimeInMs));
474     }
475     return response;
476   }
477
478   @DELETE
479   @Path("relationships/{relationshipId}")
480   public Response deleteRelationship(@PathParam("relationshipId") String relationshipId,
481       @QueryParam("transactionId") String tId, @Context HttpHeaders headers, @Context UriInfo uriInfo,
482       @Context HttpServletRequest req) {
483     LoggingUtil.initMdcContext(req, headers);
484     long startTimeInMs = System.currentTimeMillis();
485     logger.info(ChampMsgs.INCOMING_REQUEST, tId, relationshipId);
486
487     Response response = null;
488     try {
489       httpHeadersValidator.validateRequestHeaders(headers);
490       ChampTransaction transaction = champDataService.getTransaction(tId);
491       if (tId != null && transaction == null) {
492         throw new ChampServiceException("transactionId not found", Status.BAD_REQUEST);
493       }
494       champDataService.deleteRelationship(relationshipId, Optional.ofNullable(transaction));
495       response = Response.status(Status.OK).build();
496
497     } catch (ChampRelationshipNotExistsException e) {
498       response = Response.status(Status.NOT_FOUND).entity(relationshipId + " not found").build();
499     } catch (ChampServiceException ce) {
500       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
501     } catch (ChampTransactionException | ChampUnmarshallingException e) {
502       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
503     } finally {
504       LoggingUtil.logRestRequest(logger, auditLogger, req, response);
505       metricsLogger.info(ChampMsgs.PROCESSED_REQUEST, "DELETE",
506           Long.toString(System.currentTimeMillis() - startTimeInMs));
507     }
508     return response;
509   }
510
511   @GET
512   @Path("relationships/filter/")
513   @Produces(MediaType.APPLICATION_JSON)
514   public Response filterMethod(@Context HttpHeaders headers, @Context UriInfo uriInfo,
515       @Context HttpServletRequest req) {
516     LoggingUtil.initMdcContext(req, headers);
517     long startTimeInMs = System.currentTimeMillis();
518     List<ChampRelationship> champRelationshipList;
519     Map<String, Object> filter = new HashMap<>();
520     for (Map.Entry<String, List<String>> e : uriInfo.getQueryParameters().entrySet()) {
521       if (!reservedKeyMatcher ( QUERY_OBJECT_ID_URL_MATCH, e.getKey () )) {
522         filter.put ( e.getKey (), e.getValue ().get ( 0 ) );
523       }
524     }
525     Response response = null;
526     try {
527       httpHeadersValidator.validateRequestHeaders(headers);
528       champRelationshipList = champDataService.queryRelationships(filter);
529       EntityTag eTag = new EntityTag(etagGenerator.computeHashForChampRelationships(champRelationshipList));
530       response = Response.status(Status.OK).type(MediaType.APPLICATION_JSON).tag(eTag).entity(mapper.writeValueAsString(champRelationshipList))
531           .build();
532     } catch (JsonProcessingException e) {
533       response = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
534     } catch (ChampServiceException e1) {
535       response = Response.status(e1.getHttpStatus()).entity(e1.getMessage()).build();
536     } catch (Exception e) {
537       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
538       LoggingUtil.logInternalError(logger, e);
539     } finally {
540       LoggingUtil.logRestRequest(logger, auditLogger, req, response);
541       metricsLogger.info(ChampMsgs.PROCESSED_REQUEST, "GET", Long.toString(System.currentTimeMillis() - startTimeInMs));
542     }
543     return response;
544   }
545
546   @POST
547   @Path("transaction")
548   @Produces(MediaType.TEXT_PLAIN)
549   public Response openTransaction(@Context HttpHeaders headers, @Context UriInfo uriInfo,
550       @Context HttpServletRequest req) {
551     LoggingUtil.initMdcContext(req, headers);
552     long startTimeInMs = System.currentTimeMillis();
553     Response response = null;
554     try {
555       httpHeadersValidator.validateRequestHeaders(headers);
556       String transaction = champDataService.openTransaction();
557       Status s = Status.OK;
558       response = Response.status(s).entity(transaction).build();
559       logger.info(ChampMsgs.PROCESS_EVENT, "Opened Transaction with ID: " + transaction, s.toString());
560     } catch (ChampServiceException e) {
561       response = Response.status(e.getHttpStatus()).entity(e.getMessage()).build();
562     } finally {
563       LoggingUtil.logRestRequest(logger, auditLogger, req, response);
564       metricsLogger.info(ChampMsgs.PROCESSED_REQUEST, "POST", Long.toString(System.currentTimeMillis() - startTimeInMs));
565     }
566     return response;
567   }
568
569   @GET
570   @Path("transaction/{tId}")
571   public Response getSpecificTransaction(@PathParam("tId") String tId, @Context HttpHeaders headers,
572       @Context UriInfo uriInfo, @Context HttpServletRequest req) {
573     LoggingUtil.initMdcContext(req, headers);
574     long startTimeInMs = System.currentTimeMillis();
575
576     Response response = null;
577     ChampTransaction transaction = champDataService.getTransaction(tId);
578     if (transaction == null) {
579       response = Response.status(Status.NOT_FOUND).entity("transaction " + tId + " not found").build();
580       return response;
581     }
582
583     try {
584       httpHeadersValidator.validateRequestHeaders(headers);
585       response = Response.status(Status.OK).entity(mapper.writeValueAsString(tId + " is OPEN")).build();
586     } catch (JsonProcessingException e) {
587       response = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
588     } catch (ChampServiceException e) {
589         response = Response.status(e.getHttpStatus()).entity(e.getMessage()).build();
590     } catch (Exception e) {
591       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
592       LoggingUtil.logInternalError(logger, e);
593     } finally {
594       LoggingUtil.logRestRequest(logger, auditLogger, req, response);
595       metricsLogger.info(ChampMsgs.PROCESSED_REQUEST, "GET", Long.toString(System.currentTimeMillis() - startTimeInMs));
596     }
597     return response;
598   }
599
600   @PUT
601   @Path("transaction/{tId}")
602   @Produces(MediaType.TEXT_PLAIN)
603   @Consumes(MediaType.APPLICATION_JSON)
604   public Response updateTransaction(String t, @PathParam("tId") String tId, @Context HttpHeaders headers,
605       @Context UriInfo uriInfo, @Context HttpServletRequest req) {
606     LoggingUtil.initMdcContext(req, headers);
607     long startTimeInMs = System.currentTimeMillis();
608     logger.info(ChampMsgs.INCOMING_REQUEST, tId, "COMMIT/ROLLBACK");
609
610     Response response = null;
611     try {
612       httpHeadersValidator.validateRequestHeaders(headers);
613       JSONObject jsonObj = new JSONObject(t);
614       String method = jsonObj.getString(this.TRANSACTION_METHOD);
615
616       if (method.equals("commit")) {
617         champDataService.commitTransaction(tId);
618         response = Response.status(Status.OK).entity("COMMITTED").build();
619
620       } else if (method.equals("rollback")) {
621         champDataService.rollbackTransaction(tId);
622         response = Response.status(Status.OK).entity("ROLLED BACK").build();
623       } else {
624         response = Response.status(Status.BAD_REQUEST).entity("Invalid Method: " + method).build();
625         return response;
626       }
627
628     } catch (ChampTransactionException e) {
629       response = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
630     } catch (JSONException e) {
631       response = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
632     } catch (ChampServiceException e) {
633       response = Response.status(e.getHttpStatus()).entity(e.getMessage()).build();
634     } catch (Exception e) {
635       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
636       LoggingUtil.logInternalError(logger, e);
637     } finally {
638       LoggingUtil.logRestRequest(logger, auditLogger, req, response);
639       metricsLogger.info(ChampMsgs.PROCESSED_REQUEST, "PUT", Long.toString(System.currentTimeMillis() - startTimeInMs));
640     }
641     return response;
642   }
643
644   @POST
645   @Path("bulk")
646   @Consumes(MediaType.APPLICATION_JSON)
647   @Produces(MediaType.APPLICATION_JSON)
648   public Response postBulk(String bulkPayload, @Context HttpHeaders headers,
649       @Context UriInfo uriInfo, @Context HttpServletRequest req) {
650     LoggingUtil.initMdcContext(req, headers);
651     long startTimeInMs = System.currentTimeMillis();
652     logger.info(ChampMsgs.INCOMING_REQUEST, "null", bulkPayload);
653     Response response = null;
654     try {
655       httpHeadersValidator.validateRequestHeaders(headers);
656       ChampBulkPayload bulkRequest = ChampBulkPayload.fromJson(bulkPayload);
657       ChampBulkResponse bulkResponse = champDataService.processBulkRequest(bulkRequest);
658       
659       response = Response.status(Status.OK).entity(bulkResponse.toJson()).build();
660     } catch (ChampServiceException ce) {
661       response = Response.status(ce.getHttpStatus()).entity(ce.getMessage()).build();
662     } catch (IllegalArgumentException e) {
663       response = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
664     } catch (Exception e) {
665       response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
666       LoggingUtil.logInternalError(logger, e);
667     } finally {
668       LoggingUtil.logRestRequest(logger, auditLogger, req, response);
669       metricsLogger.info(ChampMsgs.PROCESSED_REQUEST, "POST",
670           Long.toString(System.currentTimeMillis() - startTimeInMs));
671     }
672     
673     return response;
674   }
675   
676   private boolean reservedKeyMatcher(Pattern p, String key) {
677     Matcher m = p.matcher ( key );
678     if (m.matches()) {
679       return true;
680     } else {
681       return false;
682     }
683   }
684
685 }