Java Code Examples for org.apache.nifi.components.ValidationResult#isValid()

The following examples show how to use org.apache.nifi.components.ValidationResult#isValid() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: StandardControllerServiceNode.java    From localization_nifi with Apache License 2.0 6 votes vote down vote up
@Override
public void verifyCanEnable(final Set<ControllerServiceNode> ignoredReferences) {
    if (getState() != ControllerServiceState.DISABLED) {
        throw new IllegalStateException(implementation.getIdentifier() + " cannot be enabled because it is not disabled");
    }

    final Set<String> ids = new HashSet<>();
    for (final ControllerServiceNode node : ignoredReferences) {
        ids.add(node.getIdentifier());
    }

    final Collection<ValidationResult> validationResults = getValidationErrors(ids);
    for (final ValidationResult result : validationResults) {
        if (!result.isValid()) {
            throw new IllegalStateException(implementation.getIdentifier() + " cannot be enabled because it is not valid: " + result);
        }
    }
}
 
Example 2
Source File: StandardProcessorTestRunner.java    From localization_nifi with Apache License 2.0 6 votes vote down vote up
@Override
public void assertNotValid(final ControllerService service) {
    final StateManager serviceStateManager = controllerServiceStateManagers.get(service.getIdentifier());
    if (serviceStateManager == null) {
        throw new IllegalStateException("Controller Service has not been added to this TestRunner via the #addControllerService method");
    }

    final ValidationContext validationContext = new MockValidationContext(context, serviceStateManager, variableRegistry).getControllerServiceValidationContext(service);
    final Collection<ValidationResult> results = context.getControllerService(service.getIdentifier()).validate(validationContext);

    for (final ValidationResult result : results) {
        if (!result.isValid()) {
            return;
        }
    }

    Assert.fail("Expected Controller Service " + service + " to be invalid but it is valid");
}
 
Example 3
Source File: StandardProcessorTestRunner.java    From nifi with Apache License 2.0 6 votes vote down vote up
@Override
public void assertValid(final ControllerService service) {
    final StateManager serviceStateManager = controllerServiceStateManagers.get(service.getIdentifier());
    if (serviceStateManager == null) {
        throw new IllegalStateException("Controller Service has not been added to this TestRunner via the #addControllerService method");
    }

    final ValidationContext validationContext = new MockValidationContext(context, serviceStateManager, variableRegistry).getControllerServiceValidationContext(service);
    final Collection<ValidationResult> results = context.getControllerService(service.getIdentifier()).validate(validationContext);

    for (final ValidationResult result : results) {
        if (!result.isValid()) {
            Assert.fail("Expected Controller Service to be valid but it is invalid due to: " + result.toString());
        }
    }
}
 
Example 4
Source File: ClientSideCEncryptionStrategy.java    From nifi with Apache License 2.0 6 votes vote down vote up
/**
 * Create an encryption client.
 *
 * @param credentialsProvider AWS credentials provider.
 * @param clientConfiguration Client configuration
 * @param kmsRegion not used by this encryption strategy
 * @param keyIdOrMaterial client master key, always base64 encoded
 * @return AWS S3 client
 */
@Override
public AmazonS3Client createEncryptionClient(AWSCredentialsProvider credentialsProvider, ClientConfiguration clientConfiguration, String kmsRegion, String keyIdOrMaterial) {
    ValidationResult keyValidationResult = validateKey(keyIdOrMaterial);
    if (!keyValidationResult.isValid()) {
        throw new IllegalArgumentException("Invalid client key; " + keyValidationResult.getExplanation());
    }

    byte[] keyMaterial = Base64.decodeBase64(keyIdOrMaterial);
    SecretKeySpec symmetricKey = new SecretKeySpec(keyMaterial, "AES");
    StaticEncryptionMaterialsProvider encryptionMaterialsProvider = new StaticEncryptionMaterialsProvider(new EncryptionMaterials(symmetricKey));

    AmazonS3EncryptionClient client = new AmazonS3EncryptionClient(credentialsProvider, encryptionMaterialsProvider);

    return client;
}
 
Example 5
Source File: StandardValidators.java    From nifi with Apache License 2.0 6 votes vote down vote up
public static Validator createDataSizeBoundsValidator(final long minBytesInclusive, final long maxBytesInclusive) {
    return new Validator() {

        @Override
        public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
            if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
                return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
            }

            final ValidationResult vr = DATA_SIZE_VALIDATOR.validate(subject, input, context);
            if (!vr.isValid()) {
                return vr;
            }
            final long dataSizeBytes = DataUnit.parseDataSize(input, DataUnit.B).longValue();
            if (dataSizeBytes < minBytesInclusive) {
                return new ValidationResult.Builder().subject(subject).input(input).valid(false).explanation("Cannot be smaller than " + minBytesInclusive + " bytes").build();
            }
            if (dataSizeBytes > maxBytesInclusive) {
                return new ValidationResult.Builder().subject(subject).input(input).valid(false).explanation("Cannot be larger than " + maxBytesInclusive + " bytes").build();
            }
            return new ValidationResult.Builder().subject(subject).input(input).valid(true).build();
        }
    };

}
 
Example 6
Source File: StandardValidators.java    From nifi with Apache License 2.0 5 votes vote down vote up
@Override
public ValidationResult validate(String subject, String input, ValidationContext context) {
    // expression language
    if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
        return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
    }
    // not empty
    ValidationResult nonEmptyValidatorResult = StandardValidators.NON_EMPTY_VALIDATOR.validate(subject, input, context);
    if (!nonEmptyValidatorResult.isValid()) {
        return nonEmptyValidatorResult;
    }
    // check format
    final List<String> hostnamePortList = Arrays.asList(input.split(","));
    for (String hostnamePort : hostnamePortList) {
        String[] addresses = hostnamePort.split(":");
        // Protect against invalid input like http://127.0.0.1:9300 (URL scheme should not be there)
        if (addresses.length != 2) {
            return new ValidationResult.Builder().subject(subject).input(input).explanation(
                    "Must be in hostname:port form (no scheme such as http://").valid(false).build();
        }

        // Validate the port
        String port = addresses[1].trim();
        ValidationResult portValidatorResult = StandardValidators.PORT_VALIDATOR.validate(subject, port, context);
        if (!portValidatorResult.isValid()) {
            return portValidatorResult;
        }
    }
    return new ValidationResult.Builder().subject(subject).input(input).explanation("Valid cluster definition").valid(true).build();
}
 
Example 7
Source File: StandardProcessorNode.java    From localization_nifi with Apache License 2.0 5 votes vote down vote up
@Override
public void verifyCanStart(final Set<ControllerServiceNode> ignoredReferences) {
    final ScheduledState currentState = getPhysicalScheduledState();
    if (currentState != ScheduledState.STOPPED && currentState != ScheduledState.DISABLED) {
        throw new IllegalStateException(this.getIdentifier() + " cannot be started because it is not stopped. Current state is " + currentState.name());
    }

    verifyNoActiveThreads();

    if (ignoredReferences != null) {
        final Set<String> ids = new HashSet<>();
        for (final ControllerServiceNode node : ignoredReferences) {
            ids.add(node.getIdentifier());
        }

        final Collection<ValidationResult> validationResults = getValidationErrors(ids);
        for (final ValidationResult result : validationResults) {
            if (!result.isValid()) {
                throw new IllegalStateException(this.getIdentifier() + " cannot be started because it is not valid: " + result);
            }
        }
    } else {
        if (!isValid()) {
            throw new IllegalStateException(this.getIdentifier() + " is not in a valid state");
        }
    }
}
 
Example 8
Source File: StandardProcessorTestRunner.java    From nifi with Apache License 2.0 5 votes vote down vote up
@Override
public void enableControllerService(final ControllerService service) {
    final ControllerServiceConfiguration configuration = context.getConfiguration(service.getIdentifier());
    if (configuration == null) {
        throw new IllegalArgumentException("Controller Service " + service + " is not known");
    }

    if (configuration.isEnabled()) {
        throw new IllegalStateException("Cannot enable Controller Service " + service + " because it is not disabled");
    }

    // ensure controller service is valid before enabling
    final ValidationContext validationContext = new MockValidationContext(context).getControllerServiceValidationContext(service);
    final Collection<ValidationResult> results = context.getControllerService(service.getIdentifier()).validate(validationContext);

    for (final ValidationResult result : results) {
        if (!result.isValid()) {
            throw new IllegalStateException("Cannot enable Controller Service " + service + " because it is in an invalid state: " + result.toString());
        }
    }

    try {
        final ConfigurationContext configContext = new MockConfigurationContext(service, configuration.getProperties(), context,variableRegistry);
        ReflectionUtils.invokeMethodsWithAnnotation(OnEnabled.class, service, configContext);
    } catch (final InvocationTargetException ite) {
        ite.getCause().printStackTrace();
        Assert.fail("Failed to enable Controller Service " + service + " due to " + ite.getCause());
    } catch (final Exception e) {
        e.printStackTrace();
        Assert.fail("Failed to enable Controller Service " + service + " due to " + e);
    }

    configuration.setEnabled(true);
}
 
Example 9
Source File: ConfigFilesValidator.java    From nifi with Apache License 2.0 5 votes vote down vote up
@Override
public ValidationResult validate(final String subject, final String value, final ValidationContext context) {
    final String[] filenames = value.split(",");
    for (final String filename : filenames) {
        final ValidationResult result = StandardValidators.FILE_EXISTS_VALIDATOR.validate(subject, filename.trim(), context);
        if (!result.isValid()) {
            return result;
        }
    }

    return new ValidationResult.Builder().subject(subject).input(value).valid(true).build();
}
 
Example 10
Source File: ExecuteStreamCommand.java    From nifi with Apache License 2.0 5 votes vote down vote up
@Override
public ValidationResult validate(String subject, String input, ValidationContext context) {
    ValidationResult result = new ValidationResult.Builder()
            .subject(subject).valid(true).input(input).build();
    List<String> args = ArgumentUtils.splitArgs(input, context.getProperty(ARG_DELIMITER).getValue().charAt(0));
    for (String arg : args) {
        ValidationResult valResult = ATTRIBUTE_EXPRESSION_LANGUAGE_VALIDATOR.validate(subject, arg, context);
        if (!valResult.isValid()) {
            result = valResult;
            break;
        }
    }
    return result;
}
 
Example 11
Source File: ConfigFilesValidator.java    From nifi with Apache License 2.0 5 votes vote down vote up
@Override
public ValidationResult validate(final String subject, final String value, final ValidationContext context) {
    final String[] filenames = value.split(",");
    for (final String filename : filenames) {
        final ValidationResult result = StandardValidators.FILE_EXISTS_VALIDATOR.validate(subject, filename.trim(), context);
        if (!result.isValid()) {
            return result;
        }
    }

    return new ValidationResult.Builder().subject(subject).input(value).valid(true).build();
}
 
Example 12
Source File: MockProcessContext.java    From localization_nifi with Apache License 2.0 5 votes vote down vote up
public boolean isValid() {
    for (final ValidationResult result : validate()) {
        if (!result.isValid()) {
            return false;
        }
    }

    return true;
}
 
Example 13
Source File: MockProcessContext.java    From nifi with Apache License 2.0 5 votes vote down vote up
public boolean isValid() {
    for (final ValidationResult result : validate()) {
        if (!result.isValid()) {
            return false;
        }
    }

    return true;
}
 
Example 14
Source File: StandardValidators.java    From localization_nifi with Apache License 2.0 5 votes vote down vote up
@Override
public ValidationResult validate(String subject, String input, ValidationContext context) {
    // expression language
    if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
        return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
    }
    // not empty
    ValidationResult nonEmptyValidatorResult = StandardValidators.NON_EMPTY_VALIDATOR.validate(subject, input, context);
    if (!nonEmptyValidatorResult.isValid()) {
        return nonEmptyValidatorResult;
    }
    // check format
    final List<String> hostnamePortList = Arrays.asList(input.split(","));
    for (String hostnamePort : hostnamePortList) {
        String[] addresses = hostnamePort.split(":");
        // Protect against invalid input like http://127.0.0.1:9300 (URL scheme should not be there)
        if (addresses.length != 2) {
            return new ValidationResult.Builder().subject(subject).input(input).explanation(
                    "Must be in hostname:port form (no scheme such as http://").valid(false).build();
        }

        // Validate the port
        String port = addresses[1].trim();
        ValidationResult portValidatorResult = StandardValidators.PORT_VALIDATOR.validate(subject, port, context);
        if (!portValidatorResult.isValid()) {
            return portValidatorResult;
        }
    }
    return new ValidationResult.Builder().subject(subject).input(input).explanation("Valid cluster definition").valid(true).build();
}
 
Example 15
Source File: StandardValidators.java    From nifi with Apache License 2.0 5 votes vote down vote up
public static Validator createListValidator(boolean trimEntries, boolean excludeEmptyEntries,
    Validator validator,
    boolean ensureElementValidation) {
    return (subject, input, context) -> {
        if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
            return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
        }
        try {
            if (input == null) {
                return new ValidationResult.Builder().subject(subject).input(null).explanation("List must have at least one non-empty element").valid(false).build();
            }

            final String[] list =  ensureElementValidation ? input.split(",",-1) : input.split(",");
            if (list.length == 0) {
                return new ValidationResult.Builder().subject(subject).input(null).explanation("List must have at least one non-empty element").valid(false).build();
            }

            for (String item : list) {
                String itemToValidate = trimEntries ? item.trim() : item;
                if (!isEmpty(itemToValidate) || !excludeEmptyEntries) {
                    ValidationResult result = validator.validate(subject, itemToValidate, context);
                    if (!result.isValid()) {
                        return result;
                    }
                }
            }
            return new ValidationResult.Builder().subject(subject).input(input).explanation("Valid List").valid(true).build();
        } catch (final Exception e) {
            return new ValidationResult.Builder().subject(subject).input(input).explanation("Not a valid list").valid(false).build();
        }
    };
}
 
Example 16
Source File: MockProcessContext.java    From localization_nifi with Apache License 2.0 5 votes vote down vote up
public void assertValid() {
    final StringBuilder sb = new StringBuilder();
    int failureCount = 0;

    for (final ValidationResult result : validate()) {
        if (!result.isValid()) {
            sb.append(result.toString()).append("\n");
            failureCount++;
        }
    }

    if (failureCount > 0) {
        Assert.fail("Processor has " + failureCount + " validation failures:\n" + sb.toString());
    }
}
 
Example 17
Source File: GetTCPUtils.java    From localization_nifi with Apache License 2.0 4 votes vote down vote up
@Override
public ValidationResult validate(final String subject, final String value, final ValidationContext context) {
    if (null == value || value.isEmpty()) {
        return new ValidationResult.Builder().subject(subject).input(value).valid(false)
                .explanation(subject + " cannot be empty").build();
    }
    // The format should be <host>:<port>{,<host>:<port>}
    // first split on ,
    final String[] hostPortPairs = value.split(",");
    boolean validHostPortPairs = true;
    String reason = "";
    String offendingSubject = subject;

    if (0 == hostPortPairs.length) {
        return new ValidationResult.Builder().subject(subject).input(value).valid(false)
                .explanation(offendingSubject + " cannot be empty").build();
    }

    for (int i = 0; i < hostPortPairs.length && validHostPortPairs; i++) {
        String[] parts = hostPortPairs[i].split(":");

        if (parts.length != 2) {
            validHostPortPairs = false;
            reason = " of malformed URL '" + hostPortPairs[i] + "'";
        } else {
            Matcher validHost = validHostnameRegex.matcher(parts[0]);
            Matcher validIp = validIpAddressRegex.matcher(parts[0]);
            Matcher looksLikeValidIp = looksLikeIpRegex.matcher(parts[0]);
            if (!validHost.find()) {
                validHostPortPairs = false;
                reason = " it contains invalid characters '" + parts[0] + "'";
            } else if (looksLikeValidIp.find() && !validIp.find()) {
                validHostPortPairs = false;
                reason = " it appears to be represented as an IP address which is out of legal range '" + parts[0] + "'";
            }
            ValidationResult result = StandardValidators.PORT_VALIDATOR.validate(parts[1], parts[1], context);
            if (!result.isValid()) {
                validHostPortPairs = false;
                reason = result.getExplanation();
            }
        }
    }

    return new ValidationResult.Builder().subject(offendingSubject).input(value).explanation(reason)
            .valid(validHostPortPairs).build();
}
 
Example 18
Source File: NotificationServiceManager.java    From nifi with Apache License 2.0 4 votes vote down vote up
/**
 * Loads the Notification Services from the given XML configuration file.
 *
 * File is expected to have the following format:
 *
 * <pre>
 * &lt;services&gt;
 *   &lt;service&gt;
 *     &lt;id&gt;service-identifier&lt;/id&gt;
 *     &lt;class&gt;org.apache.nifi.MyNotificationService&lt;/class&gt;
 *     &lt;property name="My First Property"&gt;Property Value&lt;/property&gt;
 *   &lt;/service&gt;
 *   &lt;service&gt;
 *     &lt;id&gt;other-service&lt;/id&gt;
 *     &lt;class&gt;org.apache.nifi.MyOtherNotificationService&lt;/class&gt;
 *     &lt;property name="Another Property"&gt;Property Value 2&lt;/property&gt;
 *   &lt;/service&gt;
 *   ...
 *   &lt;service&gt;
 *     &lt;id&gt;service-identifier-2&lt;/id&gt;
 *     &lt;class&gt;org.apache.nifi.FinalNotificationService&lt;/class&gt;
 *     &lt;property name="Yet Another Property"&gt;3rd Prop Value&lt;/property&gt;
 *   &lt;/service&gt;
 * &lt;/services&gt;
 * </pre>
 *
 * Note that as long as the file can be interpreted properly, a misconfigured service will result in a warning
 * or error being logged and the service will be unavailable but will not prevent the rest of the services from loading.
 *
 * @param servicesFile the XML file to load services from.
 * @throws IOException if unable to read from the given file
 * @throws ParserConfigurationException if unable to parse the given file as XML properly
 * @throws SAXException if unable to parse the given file properly
 */
public void loadNotificationServices(final File servicesFile) throws IOException, ParserConfigurationException, SAXException {
    final DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
    docBuilderFactory.setNamespaceAware(false);
    final DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();

    final Map<String, ConfiguredNotificationService> serviceMap = new HashMap<>();
    try (final InputStream fis = new FileInputStream(servicesFile);
        final InputStream in = new BufferedInputStream(fis)) {

        final Document doc = docBuilder.parse(new InputSource(in));
        final List<Element> serviceElements = getChildElementsByTagName(doc.getDocumentElement(), "service");
        logger.debug("Found {} service elements", serviceElements.size());

        for (final Element serviceElement : serviceElements) {
            final ConfiguredNotificationService config = createService(serviceElement);
            final NotificationService service = config.getService();

            if (service == null) {
                continue;  // reason will have already been logged, so just move on.
            }

            final String id = service.getIdentifier();
            if (serviceMap.containsKey(id)) {
                logger.error("Found two different Notification Services configured with the same ID: '{}'. Loaded the first service.", id);
                continue;
            }

            // Check if the service is valid; if not, warn now so that users know this before they fail to receive notifications
            final ValidationContext validationContext = new NotificationValidationContext(buildNotificationContext(config), variableRegistry);
            final Collection<ValidationResult> validationResults = service.validate(validationContext);
            final List<String> invalidReasons = new ArrayList<>();

            for (final ValidationResult result : validationResults) {
                if (!result.isValid()) {
                    invalidReasons.add(result.toString());
                }
            }

            if (!invalidReasons.isEmpty()) {
                logger.warn("Configured Notification Service {} is not valid for the following reasons: {}", service, invalidReasons);
            }

            serviceMap.put(id, config);
        }
    }

    logger.info("Successfully loaded the following {} services: {}", serviceMap.size(), serviceMap.keySet());

    servicesById.clear();
    servicesById.putAll(serviceMap);
}
 
Example 19
Source File: StandardProcessorNode.java    From localization_nifi with Apache License 2.0 4 votes vote down vote up
@Override
public boolean isValid() {
    try {
        final ValidationContext validationContext = this.getValidationContextFactory()
            .newValidationContext(getProperties(), getAnnotationData(), getProcessGroupIdentifier(), getIdentifier());

        final Collection<ValidationResult> validationResults;
        try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(getProcessor().getClass(), processor.getIdentifier())) {
            validationResults = getProcessor().validate(validationContext);
        }

        for (final ValidationResult result : validationResults) {
            if (!result.isValid()) {
                return false;
            }
        }

        for (final Relationship undef : getUndefinedRelationships()) {
            if (!isAutoTerminated(undef)) {
                return false;
            }
        }

        switch (getInputRequirement()) {
        case INPUT_ALLOWED:
            break;
        case INPUT_FORBIDDEN: {
            if (!getIncomingNonLoopConnections().isEmpty()) {
                return false;
            }
            break;
        }
        case INPUT_REQUIRED: {
            if (getIncomingNonLoopConnections().isEmpty()) {
                return false;
            }
            break;
        }
        }
    } catch (final Throwable t) {
        LOG.warn("Failed during validation", t);
        return false;
    }
    return true;
}
 
Example 20
Source File: NotificationServiceManager.java    From localization_nifi with Apache License 2.0 4 votes vote down vote up
/**
 * Loads the Notification Services from the given XML configuration file.
 *
 * File is expected to have the following format:
 *
 * <pre>
 * &lt;services&gt;
 *   &lt;service&gt;
 *     &lt;id&gt;service-identifier&lt;/id&gt;
 *     &lt;class&gt;org.apache.nifi.MyNotificationService&lt;/class&gt;
 *     &lt;property name="My First Property"&gt;Property Value&lt;/property&gt;
 *   &lt;/service&gt;
 *   &lt;service&gt;
 *     &lt;id&gt;other-service&lt;/id&gt;
 *     &lt;class&gt;org.apache.nifi.MyOtherNotificationService&lt;/class&gt;
 *     &lt;property name="Another Property"&gt;Property Value 2&lt;/property&gt;
 *   &lt;/service&gt;
 *   ...
 *   &lt;service&gt;
 *     &lt;id&gt;service-identifier-2&lt;/id&gt;
 *     &lt;class&gt;org.apache.nifi.FinalNotificationService&lt;/class&gt;
 *     &lt;property name="Yet Another Property"&gt;3rd Prop Value&lt;/property&gt;
 *   &lt;/service&gt;
 * &lt;/services&gt;
 * </pre>
 *
 * Note that as long as the file can be interpreted properly, a misconfigured service will result in a warning
 * or error being logged and the service will be unavailable but will not prevent the rest of the services from loading.
 *
 * @param servicesFile the XML file to load services from.
 * @throws IOException if unable to read from the given file
 * @throws ParserConfigurationException if unable to parse the given file as XML properly
 * @throws SAXException if unable to parse the given file properly
 */
public void loadNotificationServices(final File servicesFile) throws IOException, ParserConfigurationException, SAXException {
    final DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
    docBuilderFactory.setNamespaceAware(false);
    final DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();

    final Map<String, ConfiguredNotificationService> serviceMap = new HashMap<>();
    try (final InputStream fis = new FileInputStream(servicesFile);
        final InputStream in = new BufferedInputStream(fis)) {

        final Document doc = docBuilder.parse(new InputSource(in));
        final List<Element> serviceElements = getChildElementsByTagName(doc.getDocumentElement(), "service");
        logger.debug("Found {} service elements", serviceElements.size());

        for (final Element serviceElement : serviceElements) {
            final ConfiguredNotificationService config = createService(serviceElement);
            final NotificationService service = config.getService();

            if (service == null) {
                continue;  // reason will have already been logged, so just move on.
            }

            final String id = service.getIdentifier();
            if (serviceMap.containsKey(id)) {
                logger.error("Found two different Notification Services configured with the same ID: '{}'. Loaded the first service.", id);
                continue;
            }

            // Check if the service is valid; if not, warn now so that users know this before they fail to receive notifications
            final ValidationContext validationContext = new NotificationValidationContext(buildNotificationContext(config), variableRegistry);
            final Collection<ValidationResult> validationResults = service.validate(validationContext);
            final List<String> invalidReasons = new ArrayList<>();

            for (final ValidationResult result : validationResults) {
                if (!result.isValid()) {
                    invalidReasons.add(result.toString());
                }
            }

            if (!invalidReasons.isEmpty()) {
                logger.warn("Configured Notification Service {} is not valid for the following reasons: {}", service, invalidReasons);
            }

            serviceMap.put(id, config);
        }
    }

    logger.info("Successfully loaded the following {} services: {}", serviceMap.size(), serviceMap.keySet());

    servicesById.clear();
    servicesById.putAll(serviceMap);
}