com.hp.autonomy.frontend.configuration.server.ServerConfig Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of configuration-idol Show documentation
Show all versions of configuration-idol Show documentation
IDOL implementation of the OpenText IDOL Configuration API
/*
* Copyright 2013-2015 Hewlett-Packard 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.configuration.server;
import com.autonomy.aci.client.services.AciService;
import com.autonomy.aci.client.services.Processor;
import com.autonomy.aci.client.transport.AciServerDetails;
import com.autonomy.aci.client.transport.EncryptionCodec;
import com.autonomy.aci.client.util.AciParameters;
import com.autonomy.nonaci.ServerDetails;
import com.autonomy.nonaci.indexing.IndexingException;
import com.autonomy.nonaci.indexing.IndexingService;
import com.autonomy.nonaci.indexing.impl.IndexCommandImpl;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
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.ConfigException;
import com.hp.autonomy.frontend.configuration.SimpleComponent;
import com.hp.autonomy.frontend.configuration.validation.OptionalConfigurationComponent;
import com.hp.autonomy.frontend.configuration.validation.ValidationResult;
import com.hp.autonomy.types.idol.marshalling.ProcessorFactory;
import com.hp.autonomy.types.idol.marshalling.processors.NoopProcessor;
import com.hp.autonomy.types.idol.responses.GetChildrenResponseData;
import com.hp.autonomy.types.idol.responses.GetStatusResponseData;
import com.hp.autonomy.types.idol.responses.GetVersionResponseData;
import com.hp.autonomy.types.requests.idol.actions.general.GeneralActions;
import com.hp.autonomy.types.requests.idol.actions.status.StatusActions;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;
/**
* Configuration for an ACI server, which can also include index and service ports.
*/
@SuppressWarnings({"JavaDoc", "WeakerAccess", "DefaultAnnotationParam"})
@Getter
@Builder(toBuilder = true)
@EqualsAndHashCode(callSuper = false)
@ToString
@JsonDeserialize(builder = ServerConfig.ServerConfigBuilder.class)
public class ServerConfig extends SimpleComponent implements OptionalConfigurationComponent {
private static final Logger LOGGER = LoggerFactory.getLogger(ServerConfig.class);
private static final int MAX_PORT = 65535;
private final AciServerDetails.TransportProtocol protocol;
private final String host;
private final Integer port;
private final ServerDetails.TransportProtocol indexProtocol;
private final Integer indexPort;
private final AciServerDetails.TransportProtocol serviceProtocol;
private final Integer servicePort;
/**
* @return The producttypecsv of the server, used for validation
*/
private final Set productType;
/**
* @return The error message to expect when testing the index port of the server. If not defined it is assumed that
* this server does not support indexing.
*/
private final String indexErrorMessage;
/**
* @return A Pattern used to match the product type. Useful for connectors.
*/
private final Pattern productTypeRegex;
/**
* @return An EncryptionCodec used for encryption. This is not serialized.
*/
@JsonIgnore
private final EncryptionCodec encryptionCodec;
/**
* Creates a new ServerConfig with the given ServerDetails for indexing
*
* @param serverDetails The ServerDetails to use
* @return A new ServerConfig with the supplied indexing details
*/
public ServerConfig withIndexServer(final ServerDetails serverDetails) {
return toBuilder()
.indexProtocol(serverDetails.getProtocol())
.indexPort(serverDetails.getPort())
.build();
}
/**
* Fetches the index and service ports from the component
*
* @param aciService The {@link AciService} used to discover the ports.
* @param indexingService The {@link IndexingService} used to test the index port. This can be null if no index port is specified.
* @param processorFactory Idol response parser generator
* @param serverProductTypes The product types associated with the server
* @return A new ServerConfig with its indexing and service details filled in.
*/
public ServerConfig fetchServerDetails(
final AciService aciService,
final IndexingService indexingService,
final ProcessorFactory processorFactory,
final Collection serverProductTypes
) {
final ServerConfigBuilder builder = toBuilder();
final Ports ports = determinePorts(aciService, processorFactory, serverProductTypes);
if (ports.indexPort != null) {
final ServerDetails indexDetails = new ServerDetails();
indexDetails.setHost(host);
indexDetails.setPort(ports.indexPort);
boolean isIndexPortValid = false;
for (final ServerDetails.TransportProtocol protocol : Arrays.asList(ServerDetails.TransportProtocol.HTTP, ServerDetails.TransportProtocol.HTTPS)) {
indexDetails.setProtocol(protocol);
if (testIndexingConnection(indexDetails, indexingService, indexErrorMessage)) {
// test http first. If the server is https, it will give an error (quickly),
// whereas the timeout when doing https to a http server takes a really long time
builder.indexProtocol(protocol);
builder.indexPort(ports.indexPort);
isIndexPortValid = true;
break;
}
}
if (!isIndexPortValid) {
throw new IllegalArgumentException("Server does not have a valid index port");
}
}
final int servicePort = ports.servicePort;
final AciServerDetails servicePortDetails = new AciServerDetails();
servicePortDetails.setHost(host);
servicePortDetails.setPort(servicePort);
for (final AciServerDetails.TransportProtocol protocol : Arrays.asList(AciServerDetails.TransportProtocol.HTTP, AciServerDetails.TransportProtocol.HTTPS)) {
servicePortDetails.setProtocol(protocol);
servicePortDetails.setPort(servicePort);
if (testServicePortConnection(servicePortDetails, aciService)) {
// test http first. If the server is https, it will give an error (quickly),
// whereas the timeout when doing https to a http server takes a really long time
builder.serviceProtocol(protocol);
builder.servicePort(servicePort);
//Both index and service ports are valid
return builder.build();
}
}
//Index port valid but service port invalid
throw new IllegalArgumentException("Server does not have a valid service port");
}
private Ports determinePorts(
final AciService aciService,
final ProcessorFactory processorFactory,
final Collection serverProductTypes
) {
try {
// getStatus doesn't always return ports, but does when an index port is used
// some versions of Distributed Connector don't return the service port from GetChildren
final boolean useGetStatusToDeterminePorts = indexErrorMessage != null || serverProductTypes.contains(ProductType.DISTRIBUTED_CONNECTOR.name());
if (useGetStatusToDeterminePorts) {
final Processor processor = processorFactory.getResponseDataProcessor(GetStatusResponseData.class);
final GetStatusResponseData getStatusResponseData = aciService.executeAction(toAciServerDetails(), new AciParameters(StatusActions.GetStatus.name()), processor);
return new Ports(getStatusResponseData.getAciport(), getStatusResponseData.getIndexport(), getStatusResponseData.getServiceport());
} else {
final Processor processor = processorFactory.getResponseDataProcessor(GetChildrenResponseData.class);
final GetChildrenResponseData responseData = aciService.executeAction(toAciServerDetails(), new AciParameters(GeneralActions.GetChildren.name()), processor);
return new Ports(responseData.getPort(), null, responseData.getServiceport());
}
} catch (final RuntimeException e) {
throw new IllegalArgumentException("Unable to connect to ACI server", e);
}
}
private boolean testServicePortConnection(final AciServerDetails serviceDetails, final AciService aciService) {
try {
aciService.executeAction(serviceDetails, new AciParameters("getstatus"), new NoopProcessor());
return true;
} catch (final RuntimeException ignored) {
return false;
}
}
private boolean testIndexingConnection(final ServerDetails indexDetails, final IndexingService indexingService, final CharSequence errorMessage) {
try {
indexingService.executeCommand(indexDetails, new IndexCommandImpl("test"));
} catch (final IndexingException e) {
// we got back a response from the index port
return e.getMessage().contains(errorMessage);
} catch (final RuntimeException ignored) {
// any other kind of exception is bad
}
return false;
}
/**
* @return A representation of this server as an {@link AciServerDetails}
*/
public AciServerDetails toAciServerDetails() {
final AciServerDetails details = new AciServerDetails(protocol, host, port);
if (this.encryptionCodec != null) {
details.setEncryptionCodec(this.encryptionCodec);
}
return details;
}
/**
* @return A representation of this server as an {@link ServerDetails}
*/
public ServerDetails toServerDetails() {
final ServerDetails serverDetails = new ServerDetails();
serverDetails.setHost(host);
serverDetails.setPort(indexPort);
serverDetails.setProtocol(indexProtocol);
return serverDetails;
}
/**
* Validates that the required settings are supplied and that the target server is responding
*
* @param aciService The {@link AciService} to use for validation
* @param indexingService The {@link IndexingService} to use for validation. If the server does not support indexing
* this may be null
* @param processorFactory The {@link ProcessorFactory}
* @return A {@link ValidationResult} which will be
*
* - Valid if the server config is valid
* - If it is not valid because the given server is not of the require type, the data will be a {@link IncorrectServerType},
* containing a list of valid server types
* - If it is invalid for any other reason, the data will be a {@link ServerConfig.Validation}
*
*/
public ValidationResult> validate(final AciService aciService, final IndexingService indexingService, final ProcessorFactory processorFactory) {
// if the host is blank further testing is futile
try {
// string doesn't matter here as we swallow the exception
basicValidate(null);
} catch (final ConfigException ignored) {
return new ValidationResult<>(false, Validation.REQUIRED_FIELD_MISSING);
}
final Collection serverProductTypes;
try {
serverProductTypes = getServerProductTypes(aciService, processorFactory);
} catch (final RuntimeException e) {
LOGGER.debug("Error validating server version for {}", productType);
LOGGER.debug("", e);
return new ValidationResult<>(false, Validation.CONNECTION_ERROR);
}
if (!testServerVersion(serverProductTypes)) {
if (productTypeRegex == null) {
final List friendlyNames = new ArrayList<>();
for (final ProductType productType : this.productType) {
friendlyNames.add(productType.getFriendlyName());
}
return new ValidationResult<>(false, new IncorrectServerType(friendlyNames));
} else {
// can't use friendly names for regex
return new ValidationResult