ddf.catalog.metacard.validation.MetacardValidityMarkerPlugin.java Source code

Java tutorial

Introduction

Here is the source code for ddf.catalog.metacard.validation.MetacardValidityMarkerPlugin.java

Source

/**
 * Copyright (c) Codice Foundation
 *
 * <p>This is free software: you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation, either version 3 of
 * the License, or any later version.
 *
 * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details. A copy of the GNU Lesser General Public
 * License is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package ddf.catalog.metacard.validation;

import ddf.catalog.Constants;
import ddf.catalog.data.Attribute;
import ddf.catalog.data.Metacard;
import ddf.catalog.data.impl.AttributeImpl;
import ddf.catalog.data.types.Validation;
import ddf.catalog.operation.CreateRequest;
import ddf.catalog.operation.DeleteRequest;
import ddf.catalog.operation.UpdateRequest;
import ddf.catalog.operation.impl.CreateRequestImpl;
import ddf.catalog.operation.impl.UpdateRequestImpl;
import ddf.catalog.plugin.PluginExecutionException;
import ddf.catalog.plugin.PreIngestPlugin;
import ddf.catalog.plugin.StopProcessingException;
import ddf.catalog.util.Describable;
import ddf.catalog.validation.MetacardValidator;
import ddf.catalog.validation.ValidationException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MetacardValidityMarkerPlugin implements PreIngestPlugin {
    private static final Logger LOGGER = LoggerFactory.getLogger(MetacardValidityMarkerPlugin.class);

    private static final Logger INGEST_LOGGER = LoggerFactory.getLogger(Constants.INGEST_LOGGER_NAME);

    private final Predicate<Object> didNotFailEnforcedValidator = Objects::nonNull;

    private static final String INVALID_TAG = "INVALID";

    private static final String VALID_TAG = "VALID";

    private List<String> enforcedMetacardValidators;

    private List<MetacardValidator> metacardValidators;

    private boolean enforceErrors = true;

    private boolean enforceWarnings = true;

    @Override
    public CreateRequest process(CreateRequest input) throws PluginExecutionException, StopProcessingException {
        List<Metacard> validatedMetacards = validateList(input.getMetacards(), Function.identity());
        return new CreateRequestImpl(validatedMetacards, input.getProperties(), input.getStoreIds());
    }

    @Override
    public UpdateRequest process(UpdateRequest input) throws PluginExecutionException, StopProcessingException {
        List<Map.Entry<Serializable, Metacard>> validatedUpdates = validateList(input.getUpdates(),
                Map.Entry::getValue);
        return new UpdateRequestImpl(validatedUpdates, input.getAttributeName(), input.getProperties(),
                input.getStoreIds());
    }

    private <T> List<T> validateList(List<T> requestItems, Function<T, Metacard> itemToMetacard) {
        Map<String, Integer> counter = new HashMap<>();

        List<T> validated = requestItems.stream().map(item -> validate(item, itemToMetacard, counter))
                .filter(didNotFailEnforcedValidator).collect(Collectors.toList());

        INGEST_LOGGER.info("Validation results: {} had warnings and {} had errors.",
                counter.getOrDefault(Validation.VALIDATION_WARNINGS, 0),
                counter.getOrDefault(Validation.VALIDATION_ERRORS, 0));

        return validated;
    }

    private <T> T validate(T item, Function<T, Metacard> itemToMetacard, Map<String, Integer> counter) {
        Set<Serializable> newErrors = new HashSet<>();
        Set<Serializable> newWarnings = new HashSet<>();
        Set<Serializable> errorValidators = new HashSet<>();
        Set<Serializable> warningValidators = new HashSet<>();

        Metacard metacard = itemToMetacard.apply(item);
        Set<String> tags = metacard.getTags();
        tags.remove(VALID_TAG);
        tags.remove(INVALID_TAG);

        String valid = VALID_TAG;

        for (MetacardValidator validator : metacardValidators) {
            try {
                validator.validate(metacard);
            } catch (ValidationException e) {
                String validatorName = getValidatorName(validator);
                boolean validationErrorsExist = CollectionUtils.isNotEmpty(e.getErrors());
                boolean validationWarningsExist = CollectionUtils.isNotEmpty(e.getWarnings());

                if ((isValidatorEnforced(validatorName) && validationErrorsExist && enforceErrors)
                        || isValidatorEnforced(validatorName) && validationWarningsExist && enforceWarnings) {
                    INGEST_LOGGER.debug(
                            "The metacard with id={} is being removed from the operation because it failed the enforced validator [{}].",
                            metacard.getId(), validatorName);
                    return null;
                } else {
                    getValidationProblems(validatorName, e, newErrors, newWarnings, errorValidators,
                            warningValidators, counter);
                }
            }
        }

        Attribute existingErrors = metacard.getAttribute(Validation.VALIDATION_ERRORS);
        Attribute existingWarnings = metacard.getAttribute(Validation.VALIDATION_WARNINGS);

        if (existingErrors != null) {
            newErrors.addAll(existingErrors.getValues());
        }

        if (existingWarnings != null) {
            newWarnings.addAll(existingWarnings.getValues());
        }

        if (!newErrors.isEmpty() || !newWarnings.isEmpty()) {
            valid = INVALID_TAG;
        }

        tags.add(valid);
        metacard.setAttribute(new AttributeImpl(Metacard.TAGS, new ArrayList<String>(tags)));

        metacard.setAttribute(
                new AttributeImpl(Validation.VALIDATION_ERRORS, (List<Serializable>) new ArrayList<>(newErrors)));
        metacard.setAttribute(new AttributeImpl(Validation.VALIDATION_WARNINGS,
                (List<Serializable>) new ArrayList<>(newWarnings)));
        metacard.setAttribute(new AttributeImpl(Validation.FAILED_VALIDATORS_WARNINGS,
                (List<Serializable>) new ArrayList<>(warningValidators)));
        metacard.setAttribute(new AttributeImpl(Validation.FAILED_VALIDATORS_ERRORS,
                (List<Serializable>) new ArrayList<>(errorValidators)));

        return item;
    }

    private void getValidationProblems(String validatorName, ValidationException e, Set<Serializable> errors,
            Set<Serializable> warnings, Set<Serializable> errorValidators, Set<Serializable> warningValidators,
            Map<String, Integer> counter) {
        boolean validationErrorsExist = CollectionUtils.isNotEmpty(e.getErrors());
        boolean validationWarningsExist = CollectionUtils.isNotEmpty(e.getWarnings());
        if (validationErrorsExist || validationWarningsExist) {
            if (validationErrorsExist) {
                errors.addAll(e.getErrors());
                errorValidators.add(validatorName);
                counter.merge(Validation.VALIDATION_ERRORS, 1, Integer::sum);
            }
            if (validationWarningsExist) {
                warnings.addAll(e.getWarnings());
                warningValidators.add(validatorName);
                counter.merge(Validation.VALIDATION_WARNINGS, 1, Integer::sum);
            }
        } else {
            LOGGER.debug(
                    "Metacard validator {} did not have any warnings or errors but it threw a validation exception."
                            + " There is likely something wrong with your implementation. This will result in the metacard not"
                            + " being properly marked as invalid.",
                    validatorName);
        }
    }

    @Override
    public DeleteRequest process(DeleteRequest input) throws PluginExecutionException, StopProcessingException {
        return input;
    }

    public List<String> getEnforcedMetacardValidators() {
        return enforcedMetacardValidators;
    }

    public void setEnforcedMetacardValidators(List<String> enforcedMetacardValidators) {
        this.enforcedMetacardValidators = enforcedMetacardValidators;
    }

    public List<MetacardValidator> getMetacardValidators() {
        return metacardValidators;
    }

    public void setMetacardValidators(List<MetacardValidator> metacardValidators) {
        this.metacardValidators = metacardValidators;

        List<String> validatorsNoDescribable = metacardValidators.stream()
                .filter(validator -> !(validator instanceof Describable)).map(this::getValidatorName)
                .collect(Collectors.toList());

        if (!validatorsNoDescribable.isEmpty()) {
            LOGGER.debug("Metacard validators SHOULD implement Describable. Validators in error: {}",
                    validatorsNoDescribable);
        }
    }

    private boolean isValidatorEnforced(String validatorName) {
        return enforcedMetacardValidators != null && enforcedMetacardValidators.contains(validatorName);
    }

    protected String getValidatorName(MetacardValidator metacardValidator) {
        if (metacardValidator instanceof Describable) {
            return ((Describable) metacardValidator).getId();
        } else {
            return metacardValidator.getClass().getCanonicalName();
        }
    }

    public void setEnforceErrors(boolean enforceErrors) {
        this.enforceErrors = enforceErrors;
    }

    public boolean getEnforceErrors() {
        return enforceErrors;
    }

    public void setEnforceWarnings(boolean enforceWarnings) {
        this.enforceWarnings = enforceWarnings;
    }

    public boolean getEnforceWarnings() {
        return enforceWarnings;
    }
}