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