2 * ============LICENSE_START=======================================================
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.aai.sparky.search;
23 import java.util.HashMap;
24 import java.util.List;
26 import java.util.Map.Entry;
28 import java.util.TreeMap;
30 import javax.servlet.http.HttpServletRequest;
32 import org.apache.camel.Exchange;
33 import org.onap.aai.cl.api.Logger;
34 import org.onap.aai.cl.eelf.LoggerFactory;
35 import org.onap.aai.sparky.logging.AaiUiMsgs;
36 import org.onap.aai.sparky.logging.util.ServletUtils;
37 import org.onap.aai.sparky.search.api.SearchProvider;
38 import org.onap.aai.sparky.search.entity.QuerySearchEntity;
39 import org.onap.aai.sparky.search.entity.SearchSuggestion;
40 import org.onap.aai.sparky.search.registry.SearchProviderRegistry;
41 import org.onap.aai.sparky.util.NodeUtils;
43 import com.fasterxml.jackson.databind.ObjectMapper;
45 public class UnifiedSearchProcessor {
47 protected static final String HASH_ID_KEY = "hashId";
49 private static final Logger LOG =
50 LoggerFactory.getInstance().getLogger(UnifiedSearchProcessor.class);
52 protected SearchProviderRegistry searchProviderRegistry;
53 protected ObjectMapper mapper;
54 protected boolean useOrderedSearchProviderKeys;
56 public UnifiedSearchProcessor() {
57 mapper = new ObjectMapper();
58 this.useOrderedSearchProviderKeys = false;
61 public boolean isUseOrderedSearchProviderKeys() {
62 return useOrderedSearchProviderKeys;
65 public void setUseOrderedSearchProviderKeys(boolean useOrderedSearchProviderKeys) {
66 this.useOrderedSearchProviderKeys = useOrderedSearchProviderKeys;
69 public void search(Exchange exchange) {
70 HttpServletRequest request = exchange.getIn().getBody(HttpServletRequest.class);
71 ServletUtils.setUpMdcContext(exchange, request);
73 SearchResponse searchResponse = new SearchResponse();
74 long processTime = System.currentTimeMillis();
79 String payload = exchange.getIn().getBody(String.class);
81 if (payload == null || payload.isEmpty()) {
83 LOG.error(AaiUiMsgs.SEARCH_SERVLET_ERROR, "Request Payload is empty");
84 exchange.getOut().setHeader(Exchange.HTTP_RESPONSE_CODE, 404);
89 QuerySearchEntity searchRequest = mapper.readValue(payload, QuerySearchEntity.class);
90 int maxResultsPerSearch = Integer.valueOf(searchRequest.getMaxResults());
92 Map<String, List<SearchSuggestion>> searchProviderSuggestions =
93 new HashMap<String, List<SearchSuggestion>>();
95 int totalSuggestionsFromProviders = 0;
96 List<SearchSuggestion> suggestions = null;
97 for (SearchProvider searchProvider : searchProviderRegistry.getSearchProviders()) {
98 if (searchProvider.isPassThrough()){
99 suggestions = searchProvider.searchPassThrough(payload);
101 suggestions = searchProvider.search(searchRequest);
104 totalSuggestionsFromProviders += suggestions.size();
105 searchProviderSuggestions.put(searchProvider.getClass().getCanonicalName(), suggestions);
109 * Using ordered search provider keys allows us to deterministically calculate how many
110 * results from each provider should be returned. At the moment, this behavior is primarily
111 * only beneficial to test classes. As there is a cost to sorted-collections in the call
112 * processing path, this behavior has been made optional.
115 if (useOrderedSearchProviderKeys) {
116 searchProviderSuggestions =
117 new TreeMap<String, List<SearchSuggestion>>(searchProviderSuggestions);
120 if (totalSuggestionsFromProviders > 0) {
122 int suggestionIndex = 0;
124 Set<Entry<String, List<SearchSuggestion>>> searchProviderResults =
125 searchProviderSuggestions.entrySet();
127 while (totalAdded < maxResultsPerSearch && (totalAdded < totalSuggestionsFromProviders)) {
129 for (Entry<String, List<SearchSuggestion>> searchProviderResultList : searchProviderResults) {
131 if ((suggestionIndex <= (searchProviderResultList.getValue().size() - 1))) {
133 if (totalAdded < maxResultsPerSearch) {
135 .addSuggestion(searchProviderResultList.getValue().get(suggestionIndex));
150 searchResponse.addToTotalFound(totalAdded);
152 processTime = System.currentTimeMillis() - processTime;
153 searchResponse.setProcessingTimeInMs(processTime);
154 String searchResponseJson = NodeUtils.convertObjectToJson(searchResponse, true);
155 exchange.getOut().setHeader(Exchange.HTTP_RESPONSE_CODE, 200);
156 exchange.getOut().setBody(searchResponseJson);
158 } catch (Exception exc) {
159 LOG.error(AaiUiMsgs.ERROR_PROCESSING_REQUEST, exc);
161 exchange.getOut().setHeader(Exchange.HTTP_RESPONSE_CODE, 500);
162 exchange.getOut().setBody(
163 ServletUtils.generateJsonErrorResponse("Processing error = " + exc.getMessage()),
168 * Restore the txnId + appId from the current thread local via the MdcContext
171 ServletUtils.getTxnHeaders().forEach((key, value) -> {
172 exchange.getOut().setHeader(key, value);
175 exchange.getOut().setHeader("RequestUrl", request.getRequestURI());
176 exchange.getOut().setHeader("RequestPort", request.getLocalPort());
181 public SearchProviderRegistry getSearchProviderRegistry() {
182 return searchProviderRegistry;
185 public void setSearchProviderRegistry(SearchProviderRegistry searchProviderRegistry) {
186 this.searchProviderRegistry = searchProviderRegistry;