2 * ============LICENSE_START=======================================================
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
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 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
23 package org.onap.aai.sparky.search;
25 import java.util.HashMap;
26 import java.util.List;
28 import java.util.Map.Entry;
30 import java.util.TreeMap;
32 import org.apache.camel.Exchange;
33 import org.apache.camel.component.restlet.RestletConstants;
34 import org.onap.aai.cl.api.Logger;
35 import org.onap.aai.cl.eelf.LoggerFactory;
36 import org.onap.aai.cl.mdc.MdcContext;
37 import org.onap.aai.sparky.logging.AaiUiMsgs;
38 import org.onap.aai.sparky.search.api.SearchProvider;
39 import org.onap.aai.sparky.search.entity.QuerySearchEntity;
40 import org.onap.aai.sparky.search.entity.SearchSuggestion;
41 import org.onap.aai.sparky.search.registry.SearchProviderRegistry;
42 import org.onap.aai.sparky.util.NodeUtils;
43 import org.restlet.Request;
44 import org.restlet.Response;
45 import org.restlet.data.ClientInfo;
46 import org.restlet.data.MediaType;
47 import org.restlet.data.Status;
49 import com.fasterxml.jackson.databind.ObjectMapper;
51 public class UnifiedSearchProcessor {
53 protected static final String HASH_ID_KEY = "hashId";
55 private static final Logger LOG =
56 LoggerFactory.getInstance().getLogger(UnifiedSearchProcessor.class);
58 protected SearchProviderRegistry searchProviderRegistry;
59 protected ObjectMapper mapper;
60 protected boolean useOrderedSearchProviderKeys;
62 public UnifiedSearchProcessor() {
63 mapper = new ObjectMapper();
64 this.useOrderedSearchProviderKeys = false;
67 public boolean isUseOrderedSearchProviderKeys() {
68 return useOrderedSearchProviderKeys;
71 public void setUseOrderedSearchProviderKeys(boolean useOrderedSearchProviderKeys) {
72 this.useOrderedSearchProviderKeys = useOrderedSearchProviderKeys;
75 public void search(Exchange exchange) {
77 Object xTransactionId = exchange.getIn().getHeader("X-TransactionId");
78 if (xTransactionId == null) {
79 xTransactionId = NodeUtils.getRandomTxnId();
82 Object partnerName = exchange.getIn().getHeader("X-FromAppId");
83 if (partnerName == null) {
84 partnerName = "Browser";
87 Request request = exchange.getIn().getHeader(RestletConstants.RESTLET_REQUEST, Request.class);
90 * Disables automatic Apache Camel Restlet component logging which prints out an undesirable log
91 * entry which includes client (e.g. browser) information
93 request.setLoggable(false);
95 ClientInfo clientInfo = request.getClientInfo();
96 MdcContext.initialize((String) xTransactionId, "AAI-UI", "", (String) partnerName,
97 clientInfo.getAddress() + ":" + clientInfo.getPort());
99 SearchResponse searchResponse = new SearchResponse();
100 long processTime = System.currentTimeMillis();
104 String payload = exchange.getIn().getBody(String.class);
106 if (payload == null || payload.isEmpty()) {
108 LOG.error(AaiUiMsgs.SEARCH_SERVLET_ERROR, "Request Payload is empty");
111 * Don't throw back an error, just return an empty set
116 QuerySearchEntity searchRequest = mapper.readValue(payload, QuerySearchEntity.class);
117 int maxResultsPerSearch = Integer.valueOf(searchRequest.getMaxResults());
119 Map<String, List<SearchSuggestion>> searchProviderSuggestions =
120 new HashMap<String, List<SearchSuggestion>>();
122 int totalSuggestionsFromProviders = 0;
123 List<SearchSuggestion> suggestions = null;
124 for (SearchProvider searchProvider : searchProviderRegistry.getSearchProviders()) {
125 suggestions = searchProvider.search(searchRequest);
126 totalSuggestionsFromProviders += suggestions.size();
127 searchProviderSuggestions.put(searchProvider.getClass().getCanonicalName(), suggestions);
131 * Using ordered search provider keys allows us to deterministically calculate how many
132 * results from each provider should be returned. At the moment, this behavior is primarily
133 * only beneficial to test classes. As there is a cost to sorted-collections in the call
134 * processing path, this behavior has been made optional.
137 if (useOrderedSearchProviderKeys) {
138 searchProviderSuggestions =
139 new TreeMap<String, List<SearchSuggestion>>(searchProviderSuggestions);
142 if (totalSuggestionsFromProviders > 0) {
144 int suggestionIndex = 0;
146 Set<Entry<String, List<SearchSuggestion>>> searchProviderResults =
147 searchProviderSuggestions.entrySet();
149 while (totalAdded < maxResultsPerSearch && (totalAdded < totalSuggestionsFromProviders)) {
151 for (Entry<String, List<SearchSuggestion>> searchProviderResultList : searchProviderResults) {
153 if ((suggestionIndex <= (searchProviderResultList.getValue().size() - 1))) {
155 if (totalAdded < maxResultsPerSearch) {
157 .addSuggestion(searchProviderResultList.getValue().get(suggestionIndex));
172 searchResponse.addToTotalFound(totalAdded);
173 String searchResponseJson = NodeUtils.convertObjectToJson(searchResponse, true);
175 processTime = System.currentTimeMillis() - processTime;
176 searchResponse.setProcessingTimeInMs(processTime);
179 exchange.getIn().getHeader(RestletConstants.RESTLET_RESPONSE, Response.class);
180 response.setStatus(Status.SUCCESS_OK);
181 response.setEntity(searchResponseJson, MediaType.APPLICATION_JSON);
182 exchange.getOut().setBody(response);
184 } catch (Exception exc) {
185 LOG.error(AaiUiMsgs.SEARCH_SERVLET_ERROR,
186 "Query search failed with error = " + exc.getMessage());
187 exchange.getOut().setBody(
188 generateJsonErrorResponse("Error while building response. Error = " + exc.getMessage()),
193 public SearchProviderRegistry getSearchProviderRegistry() {
194 return searchProviderRegistry;
197 public void setSearchProviderRegistry(SearchProviderRegistry searchProviderRegistry) {
198 this.searchProviderRegistry = searchProviderRegistry;
203 * This is the manual approach, however we could also create an object container for the error
204 * then use the Jackson ObjectWrite to dump the object to json instead. If it gets any more
205 * complicated we could do that approach so we don't have to manually trip over the JSON
208 protected String generateJsonErrorResponse(String message) {
209 return String.format("{ \"errorMessage\" : %s }", message);