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 org.apache.camel.Exchange;
31 import org.apache.camel.component.restlet.RestletConstants;
32 import org.onap.aai.cl.api.Logger;
33 import org.onap.aai.cl.eelf.LoggerFactory;
34 import org.onap.aai.cl.mdc.MdcContext;
35 import org.onap.aai.sparky.logging.AaiUiMsgs;
36 import org.onap.aai.sparky.search.api.SearchProvider;
37 import org.onap.aai.sparky.search.entity.QuerySearchEntity;
38 import org.onap.aai.sparky.search.entity.SearchSuggestion;
39 import org.onap.aai.sparky.search.registry.SearchProviderRegistry;
40 import org.onap.aai.sparky.util.NodeUtils;
41 import org.restlet.Request;
42 import org.restlet.Response;
43 import org.restlet.data.ClientInfo;
44 import org.restlet.data.MediaType;
45 import org.restlet.data.Status;
47 import com.fasterxml.jackson.databind.ObjectMapper;
49 public class UnifiedSearchProcessor {
51 protected static final String HASH_ID_KEY = "hashId";
53 private static final Logger LOG =
54 LoggerFactory.getInstance().getLogger(UnifiedSearchProcessor.class);
56 protected SearchProviderRegistry searchProviderRegistry;
57 protected ObjectMapper mapper;
58 protected boolean useOrderedSearchProviderKeys;
60 public UnifiedSearchProcessor() {
61 mapper = new ObjectMapper();
62 this.useOrderedSearchProviderKeys = false;
65 public boolean isUseOrderedSearchProviderKeys() {
66 return useOrderedSearchProviderKeys;
69 public void setUseOrderedSearchProviderKeys(boolean useOrderedSearchProviderKeys) {
70 this.useOrderedSearchProviderKeys = useOrderedSearchProviderKeys;
73 public void search(Exchange exchange) {
75 Object xTransactionId = exchange.getIn().getHeader("X-TransactionId");
76 if (xTransactionId == null) {
77 xTransactionId = NodeUtils.getRandomTxnId();
80 Object partnerName = exchange.getIn().getHeader("X-FromAppId");
81 if (partnerName == null) {
82 partnerName = "Browser";
85 Request request = exchange.getIn().getHeader(RestletConstants.RESTLET_REQUEST, Request.class);
87 /* Disables automatic Apache Camel Restlet component logging which prints out an undesirable log entry
88 which includes client (e.g. browser) information */
89 request.setLoggable(false);
91 ClientInfo clientInfo = request.getClientInfo();
92 MdcContext.initialize((String) xTransactionId, "AAI-UI", "", (String) partnerName,
93 clientInfo.getAddress() + ":" + clientInfo.getPort());
95 SearchResponse searchResponse = new SearchResponse();
96 long processTime = System.currentTimeMillis();
100 String payload = exchange.getIn().getBody(String.class);
102 if (payload == null || payload.isEmpty()) {
104 LOG.error(AaiUiMsgs.SEARCH_SERVLET_ERROR, "Request Payload is empty");
107 * Don't throw back an error, just return an empty set
112 QuerySearchEntity searchRequest = mapper.readValue(payload, QuerySearchEntity.class);
113 int maxResultsPerSearch = Integer.valueOf(searchRequest.getMaxResults());
115 Map<String, List<SearchSuggestion>> searchProviderSuggestions =
116 new HashMap<String, List<SearchSuggestion>>();
118 int totalSuggestionsFromProviders = 0;
119 List<SearchSuggestion> suggestions = null;
120 for (SearchProvider searchProvider : searchProviderRegistry.getSearchProviders()) {
121 suggestions = searchProvider.search(searchRequest);
122 totalSuggestionsFromProviders += suggestions.size();
123 searchProviderSuggestions.put(searchProvider.getClass().getCanonicalName(), suggestions);
127 * Using ordered search provider keys allows us to deterministically calculate how many results
128 * from each provider should be returned. At the moment, this behavior is primarily only beneficial
129 * to test classes. As there is a cost to sorted-collections in the call processing path, this behavior
130 * has been made optional.
133 if (useOrderedSearchProviderKeys) {
134 searchProviderSuggestions =
135 new TreeMap<String, List<SearchSuggestion>>(searchProviderSuggestions);
138 if (totalSuggestionsFromProviders > 0) {
140 int suggestionIndex = 0;
142 Set<Entry<String, List<SearchSuggestion>>> searchProviderResults =
143 searchProviderSuggestions.entrySet();
145 while (totalAdded < maxResultsPerSearch
146 && (totalAdded < totalSuggestionsFromProviders)) {
148 for (Entry<String, List<SearchSuggestion>> searchProviderResultList : searchProviderResults) {
150 if ((suggestionIndex <= (searchProviderResultList.getValue().size() - 1))) {
152 if (totalAdded < maxResultsPerSearch) {
154 .addSuggestion(searchProviderResultList.getValue().get(suggestionIndex));
169 searchResponse.addToTotalFound(totalAdded);
170 String searchResponseJson = NodeUtils.convertObjectToJson(searchResponse, true);
172 processTime = System.currentTimeMillis() - processTime;
173 searchResponse.setProcessingTimeInMs(processTime);
175 Response response = exchange.getIn().getHeader(RestletConstants.RESTLET_RESPONSE, Response.class);
176 response.setStatus(Status.SUCCESS_OK);
177 response.setEntity(searchResponseJson, MediaType.APPLICATION_JSON);
178 exchange.getOut().setBody(response);
180 } catch (Exception exc) {
181 LOG.error(AaiUiMsgs.SEARCH_SERVLET_ERROR,
182 "Query search failed with error = " + exc.getMessage());
183 exchange.getOut().setBody(
184 generateJsonErrorResponse("Error while building response. Error = " + exc.getMessage()),
189 public SearchProviderRegistry getSearchProviderRegistry() {
190 return searchProviderRegistry;
193 public void setSearchProviderRegistry(SearchProviderRegistry searchProviderRegistry) {
194 this.searchProviderRegistry = searchProviderRegistry;
199 * This is the manual approach, however we could also create an object container for the error
200 * then use the Jackson ObjectWrite to dump the object to json instead. If it gets any more
201 * complicated we could do that approach so we don't have to manually trip over the JSON
204 protected String generateJsonErrorResponse(String message) {
205 return String.format("{ \"errorMessage\" : %s }", message);