com.indeed.proctor.common.JsonProctorLoaderFactory Maven / Gradle / Ivy
package com.indeed.proctor.common;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.indeed.util.varexport.ManagedVariable;
import com.indeed.util.varexport.VarExporter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.el.FunctionMapper;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class JsonProctorLoaderFactory {
// Lenient parser used by consumer apps to prevent deployment order dependencies
private static final Logger LOGGER = LogManager.getLogger(JsonProctorLoaderFactory.class);
private static final ObjectMapper OBJECT_MAPPER = Serializers.lenient();
// Prefix of export variable name for specifications.
private static final String EXPORT_NAME_PREFIX_FOR_SPECIFICATION = "specification";
protected static final VarExporter VAR_EXPORTER =
VarExporter.forNamespace(JsonProctorLoaderFactory.class.getSimpleName());
@Nullable protected String classResourcePath;
@Nullable protected String filePath;
@Nullable protected ProctorSpecification _specification;
protected FunctionMapper functionMapper = RuleEvaluator.FUNCTION_MAPPER;
protected List reporters = new ArrayList<>();
@SuppressWarnings("UnusedDeclaration")
public void setClassResourcePath(@Nullable final String classResourcePath) {
this.classResourcePath = classResourcePath;
}
public void setFilePath(@Nullable final String filePath) {
this.filePath = filePath;
}
public void setSpecificationResource(@Nonnull final Resource specificationResource) {
try (InputStream stream = specificationResource.getInputStream()) {
this._specification = OBJECT_MAPPER.readValue(stream, ProctorSpecification.class);
} catch (final IOException e) {
throw new IllegalArgumentException(
"Unable to read proctor specification from " + specificationResource, e);
}
exportJsonSpecification(
generateExportVariableNameFromResource(specificationResource), this._specification);
}
public void setSpecificationResource(@Nonnull final String specificationLocation) {
if (specificationLocation.startsWith("classpath:")) {
final String specificationClasspath = specificationLocation.replace("classpath:", "");
setSpecificationResource(
new ClassPathResource(specificationClasspath, this.getClass()));
} else {
setSpecificationResource(new FileSystemResource(specificationLocation));
}
}
/** setSpecificationResource() is likely more convenient to use instead of this method. */
public void setSpecification(@Nonnull final ProctorSpecification specification) {
this._specification =
Objects.requireNonNull(specification, "Null specifications are not supported");
exportJsonSpecification(generateExportVariableNameFromObject(specification), specification);
}
public void setFunctionMapper(@Nonnull final FunctionMapper functionMapper) {
this.functionMapper = functionMapper;
}
@Nonnull
public AbstractJsonProctorLoader getLoader() {
if ((classResourcePath == null) == (filePath == null)) {
throw new IllegalStateException(
"Must have exactly one of classResourcePath or filePath");
}
final ProctorSpecification specification =
Preconditions.checkNotNull(this._specification, "Missing specification");
if (classResourcePath != null) {
return new ClasspathProctorLoader(specification, classResourcePath, functionMapper);
}
final AbstractJsonProctorLoader loader =
new FileProctorLoader(specification, filePath, functionMapper);
loader.addLoadReporter(reporters);
return loader;
}
/**
* @param diffReporter a reporter to report changes of new proctor
* @deprecated use {@link JsonProctorLoaderFactory#setLoadReporters} instead
*/
@Deprecated
public void setDiffReporter(final AbstractProctorDiffReporter diffReporter) {
Preconditions.checkNotNull(
diffReporter,
"diff reporter can't be null use AbstractProctorDiffReporter for nop implementation");
setLoadReporters(ImmutableList.of(diffReporter));
}
public void setLoadReporters(final List reporters) {
this.reporters = reporters;
}
private void exportJsonSpecification(
final String variableName, final ProctorSpecification specification) {
try {
final ManagedVariable managedVariable =
ManagedVariable.builder()
.setName(variableName)
.setValue(OBJECT_MAPPER.writeValueAsString(specification))
.build();
VAR_EXPORTER.export(managedVariable);
} catch (final JsonProcessingException e) {
LOGGER.warn("Failed to expose json specification in VarExporter.", e);
}
}
private static String generateExportVariableNameFromResource(final Resource resource) {
return EXPORT_NAME_PREFIX_FOR_SPECIFICATION + "-" + resource.getFilename();
}
private static String generateExportVariableNameFromObject(
final ProctorSpecification specification) {
return EXPORT_NAME_PREFIX_FOR_SPECIFICATION
+ "-anonymous-"
+ Integer.toHexString(specification.hashCode());
}
}