Adding interfaces in documentation
[aai/sparky-be.git] / sparkybe-onap-service / src / main / java / org / onap / aai / sparky / search / UnifiedSearchProcessor.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.onap.aai.sparky.search;
26
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Map.Entry;
31 import java.util.Set;
32 import java.util.TreeMap;
33
34 import javax.servlet.http.HttpServletRequest;
35
36 import org.apache.camel.Exchange;
37 import org.onap.aai.cl.api.Logger;
38 import org.onap.aai.cl.eelf.LoggerFactory;
39 import org.onap.aai.sparky.logging.AaiUiMsgs;
40 import org.onap.aai.sparky.logging.util.ServletUtils;
41 import org.onap.aai.sparky.search.api.SearchProvider;
42 import org.onap.aai.sparky.search.entity.QuerySearchEntity;
43 import org.onap.aai.sparky.search.entity.SearchSuggestion;
44 import org.onap.aai.sparky.search.registry.SearchProviderRegistry;
45 import org.onap.aai.sparky.util.NodeUtils;
46
47 import com.fasterxml.jackson.databind.ObjectMapper;
48
49 public class UnifiedSearchProcessor {
50
51   protected static final String HASH_ID_KEY = "hashId";
52
53   private static final Logger LOG =
54       LoggerFactory.getInstance().getLogger(UnifiedSearchProcessor.class);
55
56   protected SearchProviderRegistry searchProviderRegistry;
57   protected ObjectMapper mapper;
58   protected boolean useOrderedSearchProviderKeys;
59
60   public UnifiedSearchProcessor() {
61     mapper = new ObjectMapper();
62     this.useOrderedSearchProviderKeys = false;
63   }
64
65   public boolean isUseOrderedSearchProviderKeys() {
66     return useOrderedSearchProviderKeys;
67   }
68
69   public void setUseOrderedSearchProviderKeys(boolean useOrderedSearchProviderKeys) {
70     this.useOrderedSearchProviderKeys = useOrderedSearchProviderKeys;
71   }
72
73   public void search(Exchange exchange) {
74     HttpServletRequest request = exchange.getIn().getBody(HttpServletRequest.class);
75     ServletUtils.setUpMdcContext(exchange, request);
76
77     SearchResponse searchResponse = new SearchResponse();
78     long processTime = System.currentTimeMillis();
79     int totalAdded = 0;
80
81     try {
82
83       String payload = exchange.getIn().getBody(String.class);
84
85       if (payload == null || payload.isEmpty()) {
86
87         LOG.error(AaiUiMsgs.SEARCH_SERVLET_ERROR, "Request Payload is empty");
88         exchange.getOut().setHeader(Exchange.HTTP_RESPONSE_CODE, 404);
89         return;
90
91       } else {
92
93         QuerySearchEntity searchRequest = mapper.readValue(payload, QuerySearchEntity.class);
94         int maxResultsPerSearch = Integer.valueOf(searchRequest.getMaxResults());
95
96         Map<String, List<SearchSuggestion>> searchProviderSuggestions =
97             new HashMap<String, List<SearchSuggestion>>();
98
99         int totalSuggestionsFromProviders = 0;
100         List<SearchSuggestion> suggestions = null;
101         for (SearchProvider searchProvider : searchProviderRegistry.getSearchProviders()) {
102           suggestions = searchProvider.search(searchRequest);
103           totalSuggestionsFromProviders += suggestions.size();
104           searchProviderSuggestions.put(searchProvider.getClass().getCanonicalName(), suggestions);
105         }
106
107         /*
108          * Using ordered search provider keys allows us to deterministically calculate how many
109          * results from each provider should be returned. At the moment, this behavior is primarily
110          * only beneficial to test classes. As there is a cost to sorted-collections in the call
111          * processing path, this behavior has been made optional.
112          */
113
114         if (useOrderedSearchProviderKeys) {
115           searchProviderSuggestions =
116               new TreeMap<String, List<SearchSuggestion>>(searchProviderSuggestions);
117         }
118
119         if (totalSuggestionsFromProviders > 0) {
120
121           int suggestionIndex = 0;
122
123           Set<Entry<String, List<SearchSuggestion>>> searchProviderResults =
124               searchProviderSuggestions.entrySet();
125
126           while (totalAdded < maxResultsPerSearch && (totalAdded < totalSuggestionsFromProviders)) {
127
128             for (Entry<String, List<SearchSuggestion>> searchProviderResultList : searchProviderResults) {
129
130               if ((suggestionIndex <= (searchProviderResultList.getValue().size() - 1))) {
131
132                 if (totalAdded < maxResultsPerSearch) {
133                   searchResponse
134                       .addSuggestion(searchProviderResultList.getValue().get(suggestionIndex));
135                   totalAdded++;
136                 }
137               }
138
139             }
140
141             suggestionIndex++;
142
143           }
144
145         }
146
147       }
148
149       searchResponse.addToTotalFound(totalAdded);
150
151       processTime = System.currentTimeMillis() - processTime;
152       searchResponse.setProcessingTimeInMs(processTime);
153       String searchResponseJson = NodeUtils.convertObjectToJson(searchResponse, true);
154       exchange.getOut().setHeader(Exchange.HTTP_RESPONSE_CODE, 200);
155       exchange.getOut().setBody(searchResponseJson);
156
157     } catch (Exception exc) {
158       LOG.error(AaiUiMsgs.ERROR_PROCESSING_REQUEST, exc);
159
160       exchange.getOut().setHeader(Exchange.HTTP_RESPONSE_CODE, 500);
161       exchange.getOut().setBody(
162           ServletUtils.generateJsonErrorResponse("Processing error = " + exc.getMessage()),
163           String.class);
164
165     } finally {
166       /*
167        * Restore the txnId + appId from the current thread local via the MdcContext
168        */
169
170       ServletUtils.getTxnHeaders().forEach((key, value) -> {
171         exchange.getOut().setHeader(key, value);
172       });
173
174       exchange.getOut().setHeader("RequestUrl", request.getRequestURI());
175       exchange.getOut().setHeader("RequestPort", request.getLocalPort());
176
177     }
178   }
179
180   public SearchProviderRegistry getSearchProviderRegistry() {
181     return searchProviderRegistry;
182   }
183
184   public void setSearchProviderRegistry(SearchProviderRegistry searchProviderRegistry) {
185     this.searchProviderRegistry = searchProviderRegistry;
186   }
187
188 }