Changing the license and trademark
[aai/sparky-be.git] / src / main / java / org / openecomp / sparky / viewandinspect / services / SearchServiceWrapper.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017 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  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  */
23 package org.openecomp.sparky.viewandinspect.services;
24
25 import java.io.BufferedReader;
26 import java.io.IOException;
27 import java.io.PrintWriter;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.Map;
35
36 import javax.servlet.ServletException;
37 import javax.servlet.http.HttpServletRequest;
38 import javax.servlet.http.HttpServletResponse;
39
40 import org.json.JSONException;
41 import org.json.JSONObject;
42 import org.openecomp.cl.api.Logger;
43 import org.openecomp.cl.eelf.LoggerFactory;
44 import org.openecomp.sparky.config.oxm.OxmEntityDescriptor;
45 import org.openecomp.sparky.config.oxm.OxmModelLoader;
46 import org.openecomp.sparky.dal.elasticsearch.HashQueryResponse;
47 import org.openecomp.sparky.dal.elasticsearch.SearchAdapter;
48 import org.openecomp.sparky.dal.rest.OperationResult;
49 import org.openecomp.sparky.dal.sas.config.SearchServiceConfig;
50 import org.openecomp.sparky.logging.AaiUiMsgs;
51 import org.openecomp.sparky.search.VnfSearchService;
52 import org.openecomp.sparky.search.config.SuggestionConfig;
53 import org.openecomp.sparky.suggestivesearch.SuggestionEntity;
54 import org.openecomp.sparky.util.NodeUtils;
55 import org.openecomp.sparky.viewandinspect.entity.QuerySearchEntity;
56 import org.openecomp.sparky.viewandinspect.entity.SearchResponse;
57
58 import com.fasterxml.jackson.databind.JsonNode;
59 import com.fasterxml.jackson.databind.ObjectMapper;
60 import com.fasterxml.jackson.databind.node.ArrayNode;
61
62 /**
63  * The Class SearchServlet.
64  */
65
66 public class SearchServiceWrapper {
67
68   private static final long serialVersionUID = 1L;
69
70   private static final Logger LOG = LoggerFactory.getInstance().getLogger(SearchServiceWrapper.class);
71
72   private SearchServiceConfig sasConfig = null;
73   private SuggestionConfig suggestionConfig = null;
74   private SearchAdapter search = null;
75   private ObjectMapper mapper;
76   private OxmModelLoader oxmModelLoader;
77   private VnfSearchService vnfSearch = null;
78
79   private static final String SEARCH_STRING = "search";
80   private static final String COUNT_STRING = "count";
81   private static final String QUERY_SEARCH = SEARCH_STRING + "/querysearch";
82   private static final String SUMMARY_BY_ENTITY_TYPE_API = SEARCH_STRING + "/summarybyentitytype";
83   private static final String SUMMARY_BY_ENTITY_TYPE_COUNT_API =
84       SUMMARY_BY_ENTITY_TYPE_API + "/" + COUNT_STRING;
85
86   private static final String VALUE_ANYKEY = "anyKey";
87   private static final String VALUE_QUERY = "query";
88   
89   private static final String KEY_HASH_ID = "hashId";
90   private static final String KEY_GROUP_BY = "groupby";  
91   private static final String KEY_SEARCH_RESULT = "searchResult";
92   private static final String KEY_HITS = "hits";
93   private static final String KEY_PAYLOAD = "payload";
94   private static final String KEY_DOCUMENT = "document";
95   private static final String KEY_CONTENT = "content";
96   private static final String KEY_SEARCH_TAG_IDS = "searchTagIDs";
97   private static final String KEY_SEARCH_TAGS = "searchTags";
98   private static final String KEY_LINK = "link";
99   private static final String KEY_ENTITY_TYPE = "entityType";
100   
101   private static final String VI_SUGGESTION_ROUTE = "viewInspect"; // TODO -> Read route from
102                                                                    // suggestive-search.properties
103                                                                    // instead of hard coding
104   
105   private static final String VIUI_SEARCH_TEMPLATE =
106       "{ " + "\"results-start\": 0," + "\"results-size\": %d," + "\"queries\": [{" + "\"must\": {"
107           + "\"match\": {" + "\"field\": \"entityType searchTags crossEntityReferenceValues\","
108           + "\"value\": \"%s\"," + "\"operator\": \"and\", "
109           + "\"analyzer\": \"whitespace_analyzer\"" + "}" + "}" + "}]" + "}";  
110   
111   /**
112    * Instantiates a new search service wrapper
113    */
114   public SearchServiceWrapper() {
115     this.mapper = new ObjectMapper();
116     vnfSearch = new VnfSearchService();
117     
118     try {
119       if (sasConfig == null) {
120         sasConfig = SearchServiceConfig.getConfig();
121       }
122
123       if (suggestionConfig == null) {
124         suggestionConfig = SuggestionConfig.getConfig();
125       }
126
127       if (search == null) {
128         search = new SearchAdapter();
129       }
130
131       if (oxmModelLoader == null) {
132         oxmModelLoader = OxmModelLoader.getInstance();
133
134         if (OxmModelLoader.getInstance().getSearchableEntityDescriptors().isEmpty()) {
135           LOG.error(AaiUiMsgs.ENTITY_NOT_FOUND_IN_OXM, "searchable entity");
136         }
137       } 
138      } catch (Exception exc) {
139       new ServletException(
140           "Caught an exception while getting an instance of servlet configuration from SearchServlet.", exc);
141     }
142   }
143
144   public void doGet(HttpServletRequest request, HttpServletResponse response)
145       throws ServletException, IOException {
146     doPost(request, response);
147   }
148
149   public void setSasConfig(SearchServiceConfig sasConfig) {
150     this.sasConfig = sasConfig;
151   }
152
153   public SearchServiceConfig getSasConfig() {
154     return sasConfig;
155   }
156
157   public void setSuggestionConfig(SuggestionConfig suggestionConfig) {
158     this.suggestionConfig = suggestionConfig;
159   }
160
161   public void setSearch(SearchAdapter search) {
162     this.search = search;
163   }
164
165   public SuggestionConfig getSuggestionConfig() {
166     return suggestionConfig;
167   }
168
169   public SearchAdapter getSearch() {
170     return search;
171   }
172
173   public void setOxmModelLoader(OxmModelLoader oxmModelLoader) {
174     this.oxmModelLoader = oxmModelLoader;
175   }
176
177   public OxmModelLoader getOxmModelLoader() {
178     return oxmModelLoader;
179   }
180
181   public VnfSearchService getVnfSearch() {
182     return vnfSearch;
183   }
184
185   public void setVnfSearch(VnfSearchService vnfSearch) {
186     this.vnfSearch = vnfSearch;
187   }
188
189   /**
190    * Get Full URL for search
191    *
192    * @param api the api
193    * @param indexName
194    * @return the full url
195    */
196   private String getSasFullUrl(String indexName, String type, String ipAddress, String port,
197       String version) {
198
199     return String.format("https://%s:%s/services/search-data-service/%s/search/indexes/%s/%s",
200         ipAddress, port, version, indexName, type);
201   }
202
203   /**
204    * Handle search service do query.
205    *
206    * @param app the app
207    * @param request the request
208    * @param response the response
209    * @throws Exception the exception
210    */
211
212   protected JSONObject getRequestParamsFromHeader(HttpServletRequest request) {
213     StringBuffer br = new StringBuffer();
214     String line = null;
215     try {
216       BufferedReader reader = request.getReader();
217       while ((line = reader.readLine()) != null) {
218         br.append(line);
219       }
220     } catch (Exception exc) {
221       LOG.error(AaiUiMsgs.ERROR_READING_HTTP_REQ_PARAMS);
222     }
223
224     String output = br.toString();
225
226     return new JSONObject(output);
227   }
228
229   protected void handleSummaryByEntityTypeCount(HttpServletRequest request,
230       HttpServletResponse response) throws Exception {
231     JSONObject parameters = getRequestParamsFromHeader(request);
232     String hashId = null;
233     if (parameters.has(KEY_HASH_ID)){
234       hashId = parameters.get(KEY_HASH_ID).toString();
235     } else {
236       vnfSearch.setZeroCountResponse(response);
237       LOG.error(AaiUiMsgs.ERROR_HASH_NOT_FOUND);
238       return;
239     }
240     HashQueryResponse hashQueryResponse = getResponseForQueryByHash(hashId, response);
241     Map<String, String> hashQueryResponsePayloadParams = new HashMap<String, String>();
242     if (hashQueryResponse.getJsonPayload() != null) {
243       hashQueryResponsePayloadParams = getPayloadParams(hashQueryResponse.getJsonPayload());
244       vnfSearch.getEntityCountResults(response, hashQueryResponsePayloadParams);
245     } else {
246       vnfSearch.setZeroCountResponse(response);
247       LOG.error(AaiUiMsgs.ERROR_INVALID_HASH, hashId);
248     }    
249   }
250   
251   protected Map<String, String> getPayloadParams(String parameters) {
252     Map<String, String> payloadParams = new HashMap<String, String>();
253     try {
254       JSONObject json = new JSONObject(parameters);
255       JSONObject payload = json.getJSONObject(KEY_PAYLOAD);
256       if (payload.length() > 0) {
257         for (String key : JSONObject.getNames(payload)) {
258           payloadParams.put(key, payload.getString(key));
259         }
260       }
261     } catch (JSONException exc) {
262       LOG.error(AaiUiMsgs.ERROR_PARSING_PARAMS, exc);
263     }
264     return payloadParams;
265   }
266
267   protected HashQueryResponse getResponseForQueryByHash(String hashId, HttpServletResponse response){
268     return vnfSearch.getJSONPayloadFromHash(hashId);
269   }
270
271   protected void handleSummaryByEntityType(HttpServletRequest request, HttpServletResponse response)
272       throws Exception {
273     JSONObject parameters = getRequestParamsFromHeader(request);
274     String hashId = null;
275     if (parameters.has(KEY_HASH_ID)){
276       hashId = parameters.get(KEY_HASH_ID).toString();
277     } else {
278       vnfSearch.setZeroCountResponse(response);
279       LOG.error(AaiUiMsgs.ERROR_HASH_NOT_FOUND);
280       return;
281     }
282     HashQueryResponse hashQueryResponse = getResponseForQueryByHash(hashId, response);
283     Map<String, String> hashQueryResponsePayloadParams = new HashMap<String, String>();
284     if (hashQueryResponse.getJsonPayload() != null) {
285       hashQueryResponsePayloadParams = getPayloadParams(hashQueryResponse.getJsonPayload());
286       if (parameters.has(KEY_GROUP_BY)){
287         String groupByKey = parameters.getString(KEY_GROUP_BY);
288         vnfSearch.getSummaryByEntityType(response, hashQueryResponsePayloadParams, groupByKey);
289       }
290     } else {
291       LOG.error(AaiUiMsgs.ERROR_INVALID_HASH, hashId);
292       vnfSearch.setEmptyAggResponse(response);
293     }  
294   }
295
296   /**
297    * Gets the value from node.
298    *
299    * @param node the node
300    * @param fieldName the field name
301    * @return the value from node
302    */
303   private String getValueFromNode(JsonNode node, String fieldName) {
304
305     if (node == null || fieldName == null) {
306       return null;
307     }
308
309     JsonNode valueNode = node.get(fieldName);
310
311     if (valueNode != null) {
312       return valueNode.asText();
313     }
314
315     return null;
316
317   }
318
319   /**
320    * Builds the search response.
321    *
322    * @param operationResult the operation result
323    * @param queryStr the query str
324    * @return TODO
325    * @return the search response
326    */
327   private List<SuggestionEntity> generateSuggestionsForSearchResponse(String operationResult,
328       String queryStr) {
329
330
331     if (operationResult == null || operationResult.length() == 0) {
332       return null;
333     }
334
335     ObjectMapper mapper = new ObjectMapper();
336     JsonNode rootNode = null;
337     List<SuggestionEntity> suggestionEntityList = new ArrayList<SuggestionEntity>();
338     try {
339       rootNode = mapper.readTree(operationResult);
340
341       JsonNode hitsNode = rootNode.get(KEY_SEARCH_RESULT);
342
343
344       // Check if there are hits that are coming back
345       if (hitsNode.has(KEY_HITS)) {
346         ArrayNode hitsArray = (ArrayNode) hitsNode.get(KEY_HITS);
347
348         /*
349          * next we iterate over the values in the hit array elements
350          */
351
352         Iterator<JsonNode> nodeIterator = hitsArray.elements();
353         JsonNode entityNode = null;
354         SuggestionEntity suggestionEntity = null;
355         JsonNode sourceNode = null;
356         while (nodeIterator.hasNext()) {
357           entityNode = nodeIterator.next();
358           sourceNode = entityNode.get(KEY_DOCUMENT).get(KEY_CONTENT);
359
360           // do the point transformation as we build the response?
361           suggestionEntity = new SuggestionEntity();
362           suggestionEntity.setRoute(VI_SUGGESTION_ROUTE);
363
364           /*
365            * This is where we probably want to annotate the search tags because we also have access
366            * to the seachTagIds
367            */
368
369           String searchTagIds = getValueFromNode(sourceNode, KEY_SEARCH_TAG_IDS);
370           String searchTags = getValueFromNode(sourceNode, KEY_SEARCH_TAGS);
371           String link = getValueFromNode(sourceNode, KEY_LINK);
372           String entityType = getValueFromNode(sourceNode, KEY_ENTITY_TYPE);
373           if (link != null) {
374             suggestionEntity.setHashId(NodeUtils.generateUniqueShaDigest(link));
375           }
376
377           try {
378             suggestionEntity
379                 .setText(annotateSearchTags(searchTags, searchTagIds, entityType, queryStr));
380           } catch (Exception exc) {
381             LOG.error(AaiUiMsgs.SEARCH_TAG_ANNOTATION_ERROR, searchTags.toString(),
382                 exc.getLocalizedMessage());
383             // at least send back the un-annotated search tags
384             suggestionEntity.setText(searchTags);
385           }
386
387           if (searchTags != null) {
388             suggestionEntityList.add(suggestionEntity);
389           }
390
391         }
392       }
393     } catch (IOException exc) {
394       LOG.warn(AaiUiMsgs.SEARCH_RESPONSE_BUILDING_EXCEPTION, exc.getLocalizedMessage());
395     }
396     return suggestionEntityList;
397   }
398
399   /*
400    */
401
402   /**
403    * Query terms match search tag.
404    *
405    * @param queryTerms the query terms
406    * @param searchTag the search tag
407    * @return true, if successful @return.
408    */
409   private boolean queryTermsMatchSearchTag(String[] queryTerms, String searchTag) {
410
411     if (queryTerms == null || queryTerms.length == 0 || searchTag == null) {
412       return false;
413     }
414
415     for (String queryTerm : queryTerms) {
416       if (searchTag.toLowerCase().contains(queryTerm.toLowerCase())) {
417         return true;
418       }
419     }
420
421     return false;
422
423   }
424
425   /**
426    * The current format of an UI-dropdown-item is like: "search-terms  entityType  att1=attr1_val".
427    * Example, for pserver: search-terms pserver hostname=djmAG-72060,
428    * pserver-name2=example-pserver-name2-val-17254, pserver-id=example-pserver-id-val-17254,
429    * ipv4-oam-address=example-ipv4-oam-address-val-17254 SearchController.js parses the above
430    * format. So if you are modifying the parsing below, please update SearchController.js as well.
431    *
432    * @param searchTags the search tags
433    * @param searchTagIds the search tag ids
434    * @param entityType the entity type
435    * @param queryStr the query str
436    * @return the string
437    */
438
439   private String annotateSearchTags(String searchTags, String searchTagIds, String entityType,
440       String queryStr) {
441
442     if (searchTags == null || searchTagIds == null) {
443       String valueOfSearchTags = String.valueOf(searchTags);
444       String valueOfSearchTagIds = String.valueOf(searchTagIds);
445
446       LOG.error(AaiUiMsgs.SEARCH_TAG_ANNOTATION_ERROR, "See error",
447           "Search tags = " + valueOfSearchTags + " and Seach tag IDs = " + valueOfSearchTagIds);
448       return searchTags;
449     }
450
451     if (entityType == null) {
452       LOG.error(AaiUiMsgs.SEARCH_TAG_ANNOTATION_ERROR, searchTags.toString(), "EntityType is null");
453       return searchTags;
454     }
455
456     if (queryStr == null) {
457       LOG.error(AaiUiMsgs.SEARCH_TAG_ANNOTATION_ERROR, searchTags.toString(),
458           "Query string is null");
459       return searchTags;
460     }
461
462     /*
463      * The ElasticSearch analyzer has already applied the lowercase filter, so we don't have to
464      * covert them again
465      */
466     String[] searchTagsArray = searchTags.split(";");
467     String[] searchTagIdsArray = searchTagIds.split(";");
468
469     // specifically apply lower case to the the query terms to make matching
470     // simpler
471     String[] queryTerms = queryStr.toLowerCase().split(" ");
472
473     OxmEntityDescriptor desc = oxmModelLoader.getSearchableEntityDescriptors().get(entityType);
474
475     if (desc == null) {
476       LOG.error(AaiUiMsgs.ENTITY_NOT_FOUND_IN_OXM, entityType.toString());
477       return searchTags;
478     }
479
480     String primaryKeyName = NodeUtils.concatArray(desc.getPrimaryKeyAttributeName(), "/");
481     String primaryKeyValue = null;
482
483     /*
484      * For each used attribute, get the fieldName for the attribute index and transform the search
485      * tag into t1,t2,t3 => h1=t1, h2=t2, h3=t3;
486      */
487     StringBuilder searchTagsBuilder = new StringBuilder(128);
488     searchTagsBuilder.append(entityType);
489
490     String primaryKeyConjunctionValue = null;
491     boolean queryTermsMatchedSearchTags = false;
492
493     if (searchTagsArray.length == searchTagIdsArray.length) {
494       for (int i = 0; i < searchTagsArray.length; i++) {
495         String searchTagAttributeId = searchTagIdsArray[i];
496         String searchTagAttributeValue = searchTagsArray[i];
497
498         // Find the concat conjunction
499         Map<String, String> pairConjunctionList = suggestionConfig.getPairingList();
500
501         String suggConjunction = null;
502         if (pairConjunctionList.get(searchTagAttributeId) != null) {
503           suggConjunction = pairConjunctionList.get(searchTagAttributeId);
504         } else {
505           suggConjunction = suggestionConfig.getDefaultPairingValue();
506         }
507
508         if (primaryKeyName.equals(searchTagAttributeId)) {
509           primaryKeyValue = searchTagAttributeValue;
510           primaryKeyConjunctionValue = suggConjunction;
511         }
512
513         if (queryTermsMatchSearchTag(queryTerms, searchTagAttributeValue)) {
514           searchTagsBuilder.append(" " + suggConjunction + " " + searchTagAttributeValue);
515           queryTermsMatchedSearchTags = true;
516         }
517       }
518     } else {
519       String errorMessage = "Search tags length did not match search tag ID length for entity type " + entityType;
520       LOG.error(AaiUiMsgs.ENTITY_SYNC_SEARCH_TAG_ANNOTATION_FAILED, errorMessage);
521     }
522
523     /*
524      * if none of the user query terms matched the index entity search tags then we should still tag
525      * the matched entity with a conjunction set to at least it's entity primary key value to
526      * discriminate between the entities of the same type in the search results displayed in the UI
527      * search bar results
528      */
529
530     if (!queryTermsMatchedSearchTags) {
531
532       if (primaryKeyValue != null && primaryKeyConjunctionValue != null) {
533         searchTagsBuilder.append(" " + primaryKeyConjunctionValue + " " + primaryKeyValue);
534       } else {
535         LOG.error(AaiUiMsgs.SEARCH_TAG_ANNOTATION_ERROR, "See error",
536             "Could not annotate user query terms " + queryStr
537                 + " from available entity search tags = " + searchTags);
538         return searchTags;
539       }
540
541     }
542
543     return searchTagsBuilder.toString();
544
545   }
546
547
548   /**
549    * @param queryStr - space separate query search terms
550    * @return - query string with stop-words removed
551    */
552   private String stripStopWordsFromQuery(String queryStr) {
553
554     if (queryStr == null) {
555       return queryStr;
556     }
557
558     Collection<String> stopWords = suggestionConfig.getStopWords();
559     ArrayList<String> queryTerms = new ArrayList<String>(Arrays.asList(queryStr.toLowerCase().split(" ")));
560
561     queryTerms.removeAll(stopWords);
562
563     return String.join(" ", queryTerms);
564   }
565
566   /*
567    * Expected query:
568    * 
569    * POST /search/viuiSearch/
570    * 
571    * { "maxResults" : "10", "searchStr" : "<search bar text>" }
572    */
573
574   /**
575    * Handle view and inspect search.
576    *
577    * @param request the request
578    * @param maxResults Max number of results to return
579    * @param response the response
580    * @return
581    * @throws IOException Signals that an I/O exception has occurred.
582    */
583   protected List<SuggestionEntity> performViewAndInspectQuerySearch(
584       QuerySearchEntity querySearchEntity, int maxResults) throws IOException {
585     List<SuggestionEntity> suggestionEntityList = new ArrayList<SuggestionEntity>();
586
587     /*
588      * Based on the configured stop words, we need to strip any matched stop-words ( case
589      * insensitively ) from the query string, before hitting elastic to prevent the words from being
590      * used against the elastic view-and-inspect index. Another alternative to this approach would
591      * be to define stop words on the elastic search index configuration for the
592      * entity-search-index, but but that may be more complicated / more risky than just a simple bug
593      * fix, but it's something we should think about for the future.
594      */
595
596     try {
597       final String queryStringWithoutStopWords =
598           stripStopWordsFromQuery(querySearchEntity.getQueryStr());
599
600       final String fullUrlStr = getSasFullUrl(sasConfig.getIndexName(), VALUE_QUERY,
601           sasConfig.getIpAddress(), sasConfig.getHttpPort(), sasConfig.getVersion());
602
603       String postBody =
604           String.format(VIUI_SEARCH_TEMPLATE, maxResults, queryStringWithoutStopWords);
605
606       OperationResult opResult = search.doPost(fullUrlStr, postBody, "application/json");
607       if (opResult.getResultCode() == 200) {
608         suggestionEntityList = generateSuggestionsForSearchResponse(opResult.getResult(),
609             querySearchEntity.getQueryStr());
610       }
611     } catch (Exception exc) {
612       LOG.error(AaiUiMsgs.SEARCH_SERVLET_ERROR,
613           "View and inspect query failed with error = " + exc.getMessage());
614     }
615     return suggestionEntityList;
616   }
617
618   protected List<SuggestionEntity> performVnfQuerySearch(QuerySearchEntity querySearchEntity,
619       int resultCountLimit) throws Exception {
620     return vnfSearch.getSuggestionsResults(querySearchEntity, resultCountLimit);
621   }
622
623   protected void handleQuerySearch(HttpServletRequest request, HttpServletResponse response)
624       throws IOException {
625     String payload = NodeUtils.getBody(request);
626     if (payload == null || payload.isEmpty()) {
627       handleSearchServletErrors("Unable to parse payload", null, response);
628     } else {
629       QuerySearchEntity querySearchEntity = mapper.readValue(payload, QuerySearchEntity.class);
630       int maxResultsPerSearch = Integer.valueOf(querySearchEntity.getMaxResults());
631       try {
632         SearchResponse searchResponse = new SearchResponse();
633         List<SuggestionEntity> viewAndInspectsuggestionEntityList =
634             new ArrayList<SuggestionEntity>();
635         List<SuggestionEntity> vnfSuggestionEntityList = new ArrayList<SuggestionEntity>();
636         long processTime = System.currentTimeMillis();
637         for (String searchService : suggestionConfig.getSearchIndexToSearchService().values()) {
638           if (searchService.equals(SearchServiceWrapper.class.getSimpleName())) {
639             viewAndInspectsuggestionEntityList =
640                 performViewAndInspectQuerySearch(querySearchEntity, maxResultsPerSearch);
641           } else if (searchService.equals(VnfSearchService.class.getSimpleName())) {
642             vnfSuggestionEntityList = performVnfQuerySearch(querySearchEntity, maxResultsPerSearch);
643           }
644         }
645
646         int totalAdded = 0;
647         for (int i = 0; i < maxResultsPerSearch; i++) {
648           if (i < viewAndInspectsuggestionEntityList.size() && totalAdded < maxResultsPerSearch) {
649             searchResponse.addSuggestion(viewAndInspectsuggestionEntityList.get(i));
650             totalAdded++;
651           }
652           if (i < vnfSuggestionEntityList.size() && totalAdded < maxResultsPerSearch) {
653             searchResponse.addSuggestion(vnfSuggestionEntityList.get(i));
654             totalAdded++;
655           }
656           if (totalAdded >= maxResultsPerSearch) {
657             break;
658           }
659         }
660         searchResponse.addToTotalFound(totalAdded);
661         String searchResponseJson = NodeUtils.convertObjectToJson(searchResponse, true);
662
663         processTime = System.currentTimeMillis() - processTime;
664         searchResponse.setProcessingTimeInMs(processTime);
665         setServletResponse(response, searchResponseJson);
666       } catch (Exception exc) {
667         LOG.error(AaiUiMsgs.SEARCH_SERVLET_ERROR,
668             "Query search failed with error = " + exc.getMessage());
669       }
670     }
671   }
672
673   public void doPost(HttpServletRequest request, HttpServletResponse response)
674       throws ServletException, IOException {
675
676     String api = null;
677     try {
678       
679       // set default response
680       response.setStatus(200);
681
682       if (request.getRequestURI().contains(QUERY_SEARCH)) {
683         api = QUERY_SEARCH; 
684         handleQuerySearch(request, response);
685         return;
686       } else if (request.getRequestURI().contains(SUMMARY_BY_ENTITY_TYPE_COUNT_API)) {
687         api = SUMMARY_BY_ENTITY_TYPE_COUNT_API;
688         handleSummaryByEntityTypeCount(request, response);
689         return;
690       } else if (request.getRequestURI().contains(SUMMARY_BY_ENTITY_TYPE_API)) {
691         api = SUMMARY_BY_ENTITY_TYPE_API;
692         handleSummaryByEntityType(request, response);
693         return;
694       } else {
695
696         final String errorMessage = "Ignored request-uri = " + request.getRequestURI();
697         LOG.error(AaiUiMsgs.SEARCH_SERVLET_ERROR, errorMessage);
698         response.setStatus(404);
699         response.setContentType("application/json");
700         PrintWriter out = response.getWriter();
701         out.println(generateJsonErrorResponse(errorMessage));
702         out.close();
703         
704         
705       }
706     } catch (JSONException je){
707       handleSearchServletErrors("Caught an exception while parsing json in processing for " + api, je,
708           response);
709     } catch (Exception e1) {
710       handleSearchServletErrors("Caught an exception while communicating with elasticsearch", e1,
711           response);
712     }
713   }
714
715   /**
716    * Generate json error response.
717    *
718    * @param message the message
719    * @return the string
720    */
721   /*
722    * This is the manual approach, however we could also create an object container for the error
723    * then use the Jackson ObjectWrite to dump the object to json instead. If it gets any more
724    * complicated we could do that approach so we don't have to manually trip over the JSON
725    * formatting.
726    */
727   protected String generateJsonErrorResponse(String message) {
728     return String.format("{ \"errorMessage\" : %s }", message);
729   }
730
731   /**
732    * Handle search servlet errors.
733    *
734    * @param errorMsg the error msg
735    * @param exc the exc
736    * @param response the response
737    * @throws IOException Signals that an I/O exception has occurred.
738    */
739   public void handleSearchServletErrors(String errorMsg, Exception exc,
740       HttpServletResponse response) throws IOException {
741
742     String errorLogMsg =
743         (exc == null ? errorMsg : errorMsg + ". Error:" + exc.getLocalizedMessage());
744
745     LOG.error(AaiUiMsgs.SEARCH_SERVLET_ERROR, errorLogMsg);
746
747     response.setContentType("application/json");
748     PrintWriter out = response.getWriter();
749     out.println(generateJsonErrorResponse(errorMsg));
750     out.close();
751   }
752
753
754   /**
755    * Execute query.
756    *
757    * @param response the response
758    * @param requestUrl the request url
759    * @param requestJsonPayload the request json payload
760    * @throws Exception the exception
761    */
762   public void executeQuery(HttpServletResponse response, String requestUrl,
763       String requestJsonPayload) throws Exception {
764
765     OperationResult opResult = search.doPost(requestUrl, requestJsonPayload, "application/json");
766
767     if (opResult != null) {
768
769       response.setStatus(opResult.getResultCode());
770       String finalOutput = opResult.getResult();
771
772       // example: failed to populate drop-down items from formatOutputJson()
773       if (finalOutput != null) {
774         response.setContentType("application/json");
775         PrintWriter out = response.getWriter();
776         out.println(finalOutput);
777         out.close();
778       }
779
780     } else {
781       response.setStatus(500);
782     }
783
784   }
785
786   /**
787    * Sets the servlet response.
788    * 
789    * @param response the response
790    * @param postPayload the post payload
791    *
792    * @throws IOException Signals that an I/O exception has occurred.
793    */
794   private void setServletResponse(HttpServletResponse response, String postPayload)
795       throws IOException {
796
797     if (postPayload != null) {
798       response.setContentType("application/json");
799       PrintWriter out = response.getWriter();
800       out.println(postPayload);
801       out.close();
802     }
803   }
804   
805
806   
807 }