/* * Copyright 2015-2017 Hewlett Packard Enterprise Development Company, L.P. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. */ package com.hp.autonomy.frontend.find.idol.configuration; import com.autonomy.aci.client.transport.AciServerDetails; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import com.hp.autonomy.frontend.configuration.AbstractConfig; import com.hp.autonomy.frontend.configuration.ConfigException; import com.hp.autonomy.frontend.configuration.authentication.Authentication; import com.hp.autonomy.frontend.configuration.authentication.CommunityAuthentication; import com.hp.autonomy.frontend.configuration.server.ProductType; import com.hp.autonomy.frontend.configuration.server.ServerConfig; import com.hp.autonomy.frontend.configuration.validation.OptionalConfigurationComponent; import com.hp.autonomy.frontend.find.core.configuration.FindConfig; import com.hp.autonomy.frontend.find.core.configuration.FindConfigBuilder; import com.hp.autonomy.frontend.find.core.configuration.MapConfiguration; import com.hp.autonomy.frontend.find.core.configuration.SavedSearchConfig; import com.hp.autonomy.frontend.find.core.configuration.TrendingConfiguration; import com.hp.autonomy.frontend.find.core.configuration.UiCustomization; import com.hp.autonomy.frontend.find.core.configuration.export.ExportConfig; import com.hp.autonomy.frontend.find.idol.configuration.IdolFindConfig.IdolFindConfigBuilder; import com.hp.autonomy.searchcomponents.core.config.FieldsInfo; import com.hp.autonomy.searchcomponents.idol.answer.configuration.AnswerServerConfig; import com.hp.autonomy.searchcomponents.idol.configuration.IdolSearchCapable; import com.hp.autonomy.searchcomponents.idol.configuration.QueryManipulation; import com.hp.autonomy.searchcomponents.idol.view.configuration.ViewConfig; import com.hp.autonomy.user.UserServiceConfig; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.lang3.BooleanUtils; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; @SuppressWarnings({"InstanceVariableOfConcreteClass", "DefaultAnnotationParam"}) @Data @EqualsAndHashCode(callSuper = false) @Builder(toBuilder = true) @JsonDeserialize(builder = IdolFindConfigBuilder.class) public class IdolFindConfig extends AbstractConfig<IdolFindConfig> implements UserServiceConfig, IdolSearchCapable, FindConfig<IdolFindConfig, IdolFindConfigBuilder> { private static final String SECTION = "Find Config Root"; private final CommunityAuthentication login; private final ServerConfig content; private final QueryManipulation queryManipulation; private final ViewConfig view; private final AnswerServerConfig answerServer; @JsonProperty("savedSearches") private final SavedSearchConfig savedSearchConfig; private final MMAP mmap; private final UiCustomization uiCustomization; private final FieldsInfo fieldsInfo; private final MapConfiguration map; private final TrendingConfiguration trending; private final Integer minScore; private final StatsServerConfig statsServer; private final Integer topicMapMaxResults; private final ExportConfig export; @JsonIgnore private volatile Map<String, Map<Integer, String>> productMap; @Override public IdolFindConfig merge(final IdolFindConfig maybeOther) { return Optional.ofNullable(maybeOther) .map(other -> builder() .content(content == null ? other.content : content.merge(other.content)) .login(login == null ? other.login : login.merge(other.login)) .queryManipulation(queryManipulation == null ? other.queryManipulation : queryManipulation.merge(other.queryManipulation)) .view(view == null ? other.view : view.merge(other.view)) .answerServer(answerServer == null ? other.answerServer : answerServer.merge(other.answerServer)) .savedSearchConfig(savedSearchConfig == null ? other.savedSearchConfig : savedSearchConfig.merge(other.savedSearchConfig)) .mmap(mmap == null ? other.mmap : mmap.merge(other.mmap)) .uiCustomization(uiCustomization == null ? other.uiCustomization : uiCustomization.merge(other.uiCustomization)) .fieldsInfo(fieldsInfo == null ? other.fieldsInfo : fieldsInfo.merge(other.fieldsInfo)) .map(map == null ? other.map : map.merge(other.map)) .trending(trending == null ? other.trending : trending.merge(other.trending)) .minScore(minScore == null ? other.minScore : minScore) .statsServer(statsServer == null ? other.statsServer : statsServer.merge(other.statsServer)) .topicMapMaxResults(topicMapMaxResults == null ? other.topicMapMaxResults : topicMapMaxResults) .export(Optional.ofNullable(export).map(exportConfig -> exportConfig.merge(maybeOther.export)).orElse(maybeOther.export)) .build()) .orElse(this); } // somewhat messy workaround for the fact that default method does not handle @JsonProperty annotations @Override public Map<String, OptionalConfigurationComponent<?>> getValidationMap() { final Map<String, OptionalConfigurationComponent<?>> validationMap = super.getValidationMap(); if (validationMap.containsKey("savedSearchConfig")) { validationMap.put("savedSearches", validationMap.remove("savedSearchConfig")); } return validationMap; } @JsonIgnore @Override public AciServerDetails getCommunityDetails() { return login.getCommunity().toAciServerDetails(); } @JsonIgnore @Override public Authentication<?> getAuthentication() { return login; } @Override public IdolFindConfig withoutDefaultLogin() { return toBuilder() .login(login.withoutDefaultLogin()) .build(); } @Override public IdolFindConfig generateDefaultLogin() { return toBuilder() .login(login.generateDefaultLogin()) .build(); } @Override public IdolFindConfig withHashedPasswords() { // no work to do yet return this; } @Override public void basicValidate(final String section) throws ConfigException { login.basicValidate(SECTION); content.basicValidate("content"); trending.basicValidate("trending"); savedSearchConfig.basicValidate(SECTION); if(map != null) { map.basicValidate("map"); } if(export != null) { export.basicValidate(SECTION); } if(queryManipulation != null) { queryManipulation.basicValidate(SECTION); } if(answerServer != null) { answerServer.basicValidate("AnswerServer"); } } @JsonIgnore @Override public AciServerDetails getContentAciServerDetails() { return content.toAciServerDetails(); } @Override @JsonIgnore public ViewConfig getViewConfig() { return view; } @Override public String lookupComponentNameByHostAndPort(final String hostName, final int port) { if(productMap == null) { final Map<String, Map<Integer, String>> tempProductMap = new HashMap<>(); addEntriesToProductMap(tempProductMap, ProductType.AXE.getFriendlyName(), content.getHost(), content.getPort(), content.getServicePort()); addEntriesToProductMap(tempProductMap, ProductType.VIEW.getFriendlyName(), view.getHost(), view.getPort(), view.getServicePort()); if(!"default".equals(login.getMethod())) { addEntriesToProductMap(tempProductMap, ProductType.UASERVER.getFriendlyName(), login.getCommunity().getHost(), login.getCommunity().getPort(), login.getCommunity().getServicePort()); } if(isOptionalComponentEnabled(queryManipulation)) { addEntriesToProductMap(tempProductMap, ProductType.QMS.getFriendlyName(), queryManipulation.getServer().getHost(), queryManipulation.getServer().getPort(), queryManipulation.getServer().getServicePort()); } if(isOptionalComponentEnabled(answerServer)) { addEntriesToProductMap(tempProductMap, ProductType.ANSWERSERVER.getFriendlyName(), answerServer.getServer().getHost(), answerServer.getServer().getPort(), answerServer.getServer().getServicePort()); } if(isOptionalComponentEnabled(statsServer)) { addEntriesToProductMap(tempProductMap, statsServer.getServer().getHost(), ProductType.STATS.getFriendlyName(), statsServer.getServer().getPort(), statsServer.getServer().getServicePort()); } productMap = tempProductMap; } return productMap.getOrDefault(hostName, Collections.emptyMap()).get(port); } private <T extends OptionalConfigurationComponent<T>> Boolean isOptionalComponentEnabled(final OptionalConfigurationComponent<T> maybeComponent) { return Optional.ofNullable(maybeComponent) .map(component -> BooleanUtils.isTrue(component.getEnabled())) .orElse(false); } private void addEntriesToProductMap(final Map<String, Map<Integer, String>> productMap, final String productName, final String hostName, final Integer... ports) { productMap.compute(hostName, (key, maybeMap) -> { final Map<Integer, String> map = Optional.ofNullable(maybeMap).orElse(new HashMap<>()); Arrays.stream(ports) .filter(Objects::nonNull) .forEach(port -> map.put(port, productName)); return map; }); } @SuppressWarnings("WeakerAccess") @JsonPOJOBuilder(withPrefix = "") public static class IdolFindConfigBuilder implements FindConfigBuilder<IdolFindConfig, IdolFindConfigBuilder> { @SuppressWarnings("unused") @JsonProperty("savedSearches") private SavedSearchConfig savedSearchConfig; } }