You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by tk...@apache.org on 2015/11/19 07:21:06 UTC

[23/24] nifi git commit: NIFI-1054: Fixing Line endings of source code

http://git-wip-us.apache.org/repos/asf/nifi/blob/e2d3d1b7/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServiceManager.java
----------------------------------------------------------------------
diff --git a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServiceManager.java b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServiceManager.java
index 6aebe62..2fda022 100644
--- a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServiceManager.java
+++ b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServiceManager.java
@@ -1,430 +1,430 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.bootstrap;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
-import org.apache.nifi.bootstrap.notification.NotificationContext;
-import org.apache.nifi.bootstrap.notification.NotificationInitializationContext;
-import org.apache.nifi.bootstrap.notification.NotificationService;
-import org.apache.nifi.bootstrap.notification.NotificationType;
-import org.apache.nifi.bootstrap.notification.NotificationValidationContext;
-import org.apache.nifi.components.PropertyDescriptor;
-import org.apache.nifi.components.PropertyValue;
-import org.apache.nifi.components.ValidationContext;
-import org.apache.nifi.components.ValidationResult;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-
-public class NotificationServiceManager {
-    private static final Logger logger = LoggerFactory.getLogger(NotificationServiceManager.class);
-    private final Map<String, ConfiguredNotificationService> servicesById = new HashMap<>();
-    private final Map<NotificationType, List<ConfiguredNotificationService>> servicesByNotificationType = new HashMap<>();
-
-    private final ScheduledExecutorService notificationExecutor;
-    private int maxAttempts = 5;
-
-    public NotificationServiceManager() {
-        notificationExecutor = Executors.newScheduledThreadPool(1, new ThreadFactory() {
-            @Override
-            public Thread newThread(final Runnable r) {
-                final Thread t = Executors.defaultThreadFactory().newThread(r);
-                t.setName("Notification Service Dispatcher");
-                t.setDaemon(true);
-                return t;
-            }
-        });
-    }
-
-    public void setMaxNotificationAttempts(final int maxAttempts) {
-        this.maxAttempts = maxAttempts;
-    }
-
-    /**
-     * 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));
-                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);
-    }
-
-    public void notify(final NotificationType type, final String subject, final String message) {
-        final List<ConfiguredNotificationService> configs = servicesByNotificationType.get(type);
-        if (configs == null || configs.isEmpty()) {
-            return;
-        }
-
-        for (final ConfiguredNotificationService config : configs) {
-            final NotificationService service = config.getService();
-            final AtomicInteger attemptCount = new AtomicInteger(0);
-
-            notificationExecutor.submit(new Runnable() {
-                @Override
-                public void run() {
-                    // 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));
-                    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 the service is valid, attempt to send the notification
-                    boolean failure = false;
-                    if (invalidReasons.isEmpty()) {
-                        final NotificationContext context = buildNotificationContext(config);
-                        try {
-                            service.notify(context, subject, message);
-                            logger.info("Successfully sent notification of type {} to {}", type, service);
-                        } catch (final Throwable t) {   // keep running even if a Throwable is caught because we need to ensure that we are able to restart NiFi
-                            logger.error("Failed to send notification of type {} to {} with Subject {} due to {}. Will ",
-                                type, service == null ? "Unknown Notification Service" : service.toString(), subject, t.toString());
-                            logger.error("", t);
-                            failure = true;
-                        }
-                    } else {
-                        logger.warn("Notification Service {} is not valid for the following reasons: {}", service, invalidReasons);
-                        failure = true;
-                    }
-
-                    final int attempts = attemptCount.incrementAndGet();
-                    if (failure) {
-                        if (attempts < maxAttempts) {
-                            logger.info("After failing to send notification to {} {} times, will attempt again in 1 minute", service, attempts);
-                            notificationExecutor.schedule(this, 1, TimeUnit.MINUTES);
-                        } else {
-                            logger.info("After failing to send notification of type {} to {} {} times, will no longer attempt to send notification", type, service, attempts);
-                        }
-                    }
-                }
-            });
-
-            if (NotificationType.NIFI_STOPPED.equals(type)) {
-                // If we are stopping NiFi, we want to block until we've tried to send the notification at least once before
-                // we return. We do this because the executor used to run the task marks the threads as daemon, and on shutdown
-                // we don't want to return before the notifier has had a chance to perform its task.
-                while (attemptCount.get() == 0) {
-                    try {
-                        Thread.sleep(1000L);
-                    } catch (final InterruptedException ie) {
-                    }
-                }
-            }
-        }
-    }
-
-    private NotificationContext buildNotificationContext(final ConfiguredNotificationService config) {
-        return new NotificationContext() {
-            @Override
-            public PropertyValue getProperty(final PropertyDescriptor descriptor) {
-                final PropertyDescriptor fullPropDescriptor = config.getService().getPropertyDescriptor(descriptor.getName());
-                if (fullPropDescriptor == null) {
-                    return null;
-                }
-
-                String configuredValue = config.getProperties().get(fullPropDescriptor.getName());
-                if (configuredValue == null) {
-                    configuredValue = fullPropDescriptor.getDefaultValue();
-                }
-
-                return new NotificationServicePropertyValue(configuredValue);
-            }
-
-            @Override
-            public Map<PropertyDescriptor, String> getProperties() {
-                final Map<PropertyDescriptor, String> props = new HashMap<>();
-                final Map<String, String> configuredProps = config.getProperties();
-
-                final NotificationService service = config.getService();
-                for (final PropertyDescriptor descriptor : service.getPropertyDescriptors()) {
-                    final String configuredValue = configuredProps.get(descriptor.getName());
-                    if (configuredValue == null) {
-                        props.put(descriptor, descriptor.getDefaultValue());
-                    } else {
-                        props.put(descriptor, configuredValue);
-                    }
-                }
-
-                return props;
-            }
-
-        };
-    }
-
-    /**
-     * Registers the service that has the given identifier to respond to notifications of the given type
-     *
-     * @param type the type of notification to register the service for
-     * @param serviceId the identifier of the service
-     */
-    public void registerNotificationService(final NotificationType type, final String serviceId) {
-        final ConfiguredNotificationService service = servicesById.get(serviceId);
-        if (service == null) {
-            throw new IllegalArgumentException("No Notification Service exists with ID " + serviceId);
-        }
-
-        List<ConfiguredNotificationService> services = servicesByNotificationType.get(type);
-        if (services == null) {
-            services = new ArrayList<>();
-            servicesByNotificationType.put(type, services);
-        }
-
-        services.add(service);
-    }
-
-    /**
-     * Creates a Notification Service and initializes it. Then returns the service and its configured properties
-     *
-     * @param serviceElement the XML element from which to build the Notification Service
-     * @return a Tuple with the NotificationService as the key and the configured properties as the value, or <code>null</code> if
-     *         unable to create the service
-     */
-    private ConfiguredNotificationService createService(final Element serviceElement) {
-        final Element idElement = getChild(serviceElement, "id");
-        if (idElement == null) {
-            logger.error("Found configuration for Notification Service with no 'id' element; this service cannot be referenced so it will not be loaded");
-            return null;
-        }
-
-        final String serviceId = idElement.getTextContent().trim();
-        logger.debug("Loading Notification Service with ID {}", serviceId);
-
-        final Element classElement = getChild(serviceElement, "class");
-        if (classElement == null) {
-            logger.error("Found configuration for Notification Service with no 'class' element; Service ID is '{}'. This service annot be loaded", serviceId);
-            return null;
-        }
-
-        final String className = classElement.getTextContent().trim();
-        final Class<?> clazz;
-        try {
-            clazz = Class.forName(className);
-        } catch (final Exception e) {
-            logger.error("Found configuration for Notification Service with ID '{}' and Class '{}' but could not load class.", serviceId, className);
-            logger.error("", e);
-            return null;
-        }
-
-        if (!NotificationService.class.isAssignableFrom(clazz)) {
-            logger.error("Found configuration for Notification Service with ID '{}' and Class '{}' but class is not a Notification Service.", serviceId, className);
-            return null;
-        }
-
-        final Object serviceObject;
-        try {
-            serviceObject = clazz.newInstance();
-        } catch (final Exception e) {
-            logger.error("Found configuration for Notification Service with ID '{}' and Class '{}' but could not instantiate Notification Service.", serviceId, className);
-            logger.error("", e);
-            return null;
-        }
-
-        final Map<String, String> propertyValues = new HashMap<>();
-        final List<Element> propertyElements = getChildElementsByTagName(serviceElement, "property");
-        for (final Element propertyElement : propertyElements) {
-            final String propName = propertyElement.getAttribute("name");
-            if (propName == null || propName.trim().isEmpty()) {
-                logger.warn("Found configuration for Notification Service with ID '{}' that has property value configured but no name for the property.", serviceId);
-                continue;
-            }
-
-            final String propValue = propertyElement.getTextContent().trim();
-            propertyValues.put(propName, propValue);
-        }
-
-        final NotificationService service = (NotificationService) serviceObject;
-
-        try {
-            service.initialize(new NotificationInitializationContext() {
-                @Override
-                public PropertyValue getProperty(final PropertyDescriptor descriptor) {
-                    final String propName = descriptor.getName();
-                    String value = propertyValues.get(propName);
-                    if (value == null) {
-                        value = descriptor.getDefaultValue();
-                    }
-
-                    return new NotificationServicePropertyValue(value);
-                }
-
-                @Override
-                public String getIdentifier() {
-                    return serviceId;
-                }
-            });
-        } catch (final Exception e) {
-            logger.error("Failed to load Notification Service with ID '{}'", serviceId);
-            logger.error("", e);
-        }
-
-        return new ConfiguredNotificationService(service, propertyValues);
-    }
-
-    public static Element getChild(final Element element, final String tagName) {
-        final List<Element> children = getChildElementsByTagName(element, tagName);
-        if (children.isEmpty()) {
-            return null;
-        }
-
-        if (children.size() > 1) {
-            return null;
-        }
-
-        return children.get(0);
-    }
-
-    public static List<Element> getChildElementsByTagName(final Element element, final String tagName) {
-        final List<Element> matches = new ArrayList<>();
-        final NodeList nodeList = element.getChildNodes();
-        for (int i = 0; i < nodeList.getLength(); i++) {
-            final Node node = nodeList.item(i);
-            if (!(node instanceof Element)) {
-                continue;
-            }
-
-            final Element child = (Element) nodeList.item(i);
-            if (child.getNodeName().equals(tagName)) {
-                matches.add(child);
-            }
-        }
-
-        return matches;
-    }
-
-    private static class ConfiguredNotificationService {
-        private final NotificationService service;
-        private final Map<String, String> properties;
-
-        public ConfiguredNotificationService(final NotificationService service, final Map<String, String> properties) {
-            this.service = service;
-            this.properties = properties;
-        }
-
-        public NotificationService getService() {
-            return service;
-        }
-
-        public Map<String, String> getProperties() {
-            return properties;
-        }
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.bootstrap;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.nifi.bootstrap.notification.NotificationContext;
+import org.apache.nifi.bootstrap.notification.NotificationInitializationContext;
+import org.apache.nifi.bootstrap.notification.NotificationService;
+import org.apache.nifi.bootstrap.notification.NotificationType;
+import org.apache.nifi.bootstrap.notification.NotificationValidationContext;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.components.ValidationContext;
+import org.apache.nifi.components.ValidationResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+public class NotificationServiceManager {
+    private static final Logger logger = LoggerFactory.getLogger(NotificationServiceManager.class);
+    private final Map<String, ConfiguredNotificationService> servicesById = new HashMap<>();
+    private final Map<NotificationType, List<ConfiguredNotificationService>> servicesByNotificationType = new HashMap<>();
+
+    private final ScheduledExecutorService notificationExecutor;
+    private int maxAttempts = 5;
+
+    public NotificationServiceManager() {
+        notificationExecutor = Executors.newScheduledThreadPool(1, new ThreadFactory() {
+            @Override
+            public Thread newThread(final Runnable r) {
+                final Thread t = Executors.defaultThreadFactory().newThread(r);
+                t.setName("Notification Service Dispatcher");
+                t.setDaemon(true);
+                return t;
+            }
+        });
+    }
+
+    public void setMaxNotificationAttempts(final int maxAttempts) {
+        this.maxAttempts = maxAttempts;
+    }
+
+    /**
+     * 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));
+                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);
+    }
+
+    public void notify(final NotificationType type, final String subject, final String message) {
+        final List<ConfiguredNotificationService> configs = servicesByNotificationType.get(type);
+        if (configs == null || configs.isEmpty()) {
+            return;
+        }
+
+        for (final ConfiguredNotificationService config : configs) {
+            final NotificationService service = config.getService();
+            final AtomicInteger attemptCount = new AtomicInteger(0);
+
+            notificationExecutor.submit(new Runnable() {
+                @Override
+                public void run() {
+                    // 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));
+                    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 the service is valid, attempt to send the notification
+                    boolean failure = false;
+                    if (invalidReasons.isEmpty()) {
+                        final NotificationContext context = buildNotificationContext(config);
+                        try {
+                            service.notify(context, subject, message);
+                            logger.info("Successfully sent notification of type {} to {}", type, service);
+                        } catch (final Throwable t) {   // keep running even if a Throwable is caught because we need to ensure that we are able to restart NiFi
+                            logger.error("Failed to send notification of type {} to {} with Subject {} due to {}. Will ",
+                                type, service == null ? "Unknown Notification Service" : service.toString(), subject, t.toString());
+                            logger.error("", t);
+                            failure = true;
+                        }
+                    } else {
+                        logger.warn("Notification Service {} is not valid for the following reasons: {}", service, invalidReasons);
+                        failure = true;
+                    }
+
+                    final int attempts = attemptCount.incrementAndGet();
+                    if (failure) {
+                        if (attempts < maxAttempts) {
+                            logger.info("After failing to send notification to {} {} times, will attempt again in 1 minute", service, attempts);
+                            notificationExecutor.schedule(this, 1, TimeUnit.MINUTES);
+                        } else {
+                            logger.info("After failing to send notification of type {} to {} {} times, will no longer attempt to send notification", type, service, attempts);
+                        }
+                    }
+                }
+            });
+
+            if (NotificationType.NIFI_STOPPED.equals(type)) {
+                // If we are stopping NiFi, we want to block until we've tried to send the notification at least once before
+                // we return. We do this because the executor used to run the task marks the threads as daemon, and on shutdown
+                // we don't want to return before the notifier has had a chance to perform its task.
+                while (attemptCount.get() == 0) {
+                    try {
+                        Thread.sleep(1000L);
+                    } catch (final InterruptedException ie) {
+                    }
+                }
+            }
+        }
+    }
+
+    private NotificationContext buildNotificationContext(final ConfiguredNotificationService config) {
+        return new NotificationContext() {
+            @Override
+            public PropertyValue getProperty(final PropertyDescriptor descriptor) {
+                final PropertyDescriptor fullPropDescriptor = config.getService().getPropertyDescriptor(descriptor.getName());
+                if (fullPropDescriptor == null) {
+                    return null;
+                }
+
+                String configuredValue = config.getProperties().get(fullPropDescriptor.getName());
+                if (configuredValue == null) {
+                    configuredValue = fullPropDescriptor.getDefaultValue();
+                }
+
+                return new NotificationServicePropertyValue(configuredValue);
+            }
+
+            @Override
+            public Map<PropertyDescriptor, String> getProperties() {
+                final Map<PropertyDescriptor, String> props = new HashMap<>();
+                final Map<String, String> configuredProps = config.getProperties();
+
+                final NotificationService service = config.getService();
+                for (final PropertyDescriptor descriptor : service.getPropertyDescriptors()) {
+                    final String configuredValue = configuredProps.get(descriptor.getName());
+                    if (configuredValue == null) {
+                        props.put(descriptor, descriptor.getDefaultValue());
+                    } else {
+                        props.put(descriptor, configuredValue);
+                    }
+                }
+
+                return props;
+            }
+
+        };
+    }
+
+    /**
+     * Registers the service that has the given identifier to respond to notifications of the given type
+     *
+     * @param type the type of notification to register the service for
+     * @param serviceId the identifier of the service
+     */
+    public void registerNotificationService(final NotificationType type, final String serviceId) {
+        final ConfiguredNotificationService service = servicesById.get(serviceId);
+        if (service == null) {
+            throw new IllegalArgumentException("No Notification Service exists with ID " + serviceId);
+        }
+
+        List<ConfiguredNotificationService> services = servicesByNotificationType.get(type);
+        if (services == null) {
+            services = new ArrayList<>();
+            servicesByNotificationType.put(type, services);
+        }
+
+        services.add(service);
+    }
+
+    /**
+     * Creates a Notification Service and initializes it. Then returns the service and its configured properties
+     *
+     * @param serviceElement the XML element from which to build the Notification Service
+     * @return a Tuple with the NotificationService as the key and the configured properties as the value, or <code>null</code> if
+     *         unable to create the service
+     */
+    private ConfiguredNotificationService createService(final Element serviceElement) {
+        final Element idElement = getChild(serviceElement, "id");
+        if (idElement == null) {
+            logger.error("Found configuration for Notification Service with no 'id' element; this service cannot be referenced so it will not be loaded");
+            return null;
+        }
+
+        final String serviceId = idElement.getTextContent().trim();
+        logger.debug("Loading Notification Service with ID {}", serviceId);
+
+        final Element classElement = getChild(serviceElement, "class");
+        if (classElement == null) {
+            logger.error("Found configuration for Notification Service with no 'class' element; Service ID is '{}'. This service annot be loaded", serviceId);
+            return null;
+        }
+
+        final String className = classElement.getTextContent().trim();
+        final Class<?> clazz;
+        try {
+            clazz = Class.forName(className);
+        } catch (final Exception e) {
+            logger.error("Found configuration for Notification Service with ID '{}' and Class '{}' but could not load class.", serviceId, className);
+            logger.error("", e);
+            return null;
+        }
+
+        if (!NotificationService.class.isAssignableFrom(clazz)) {
+            logger.error("Found configuration for Notification Service with ID '{}' and Class '{}' but class is not a Notification Service.", serviceId, className);
+            return null;
+        }
+
+        final Object serviceObject;
+        try {
+            serviceObject = clazz.newInstance();
+        } catch (final Exception e) {
+            logger.error("Found configuration for Notification Service with ID '{}' and Class '{}' but could not instantiate Notification Service.", serviceId, className);
+            logger.error("", e);
+            return null;
+        }
+
+        final Map<String, String> propertyValues = new HashMap<>();
+        final List<Element> propertyElements = getChildElementsByTagName(serviceElement, "property");
+        for (final Element propertyElement : propertyElements) {
+            final String propName = propertyElement.getAttribute("name");
+            if (propName == null || propName.trim().isEmpty()) {
+                logger.warn("Found configuration for Notification Service with ID '{}' that has property value configured but no name for the property.", serviceId);
+                continue;
+            }
+
+            final String propValue = propertyElement.getTextContent().trim();
+            propertyValues.put(propName, propValue);
+        }
+
+        final NotificationService service = (NotificationService) serviceObject;
+
+        try {
+            service.initialize(new NotificationInitializationContext() {
+                @Override
+                public PropertyValue getProperty(final PropertyDescriptor descriptor) {
+                    final String propName = descriptor.getName();
+                    String value = propertyValues.get(propName);
+                    if (value == null) {
+                        value = descriptor.getDefaultValue();
+                    }
+
+                    return new NotificationServicePropertyValue(value);
+                }
+
+                @Override
+                public String getIdentifier() {
+                    return serviceId;
+                }
+            });
+        } catch (final Exception e) {
+            logger.error("Failed to load Notification Service with ID '{}'", serviceId);
+            logger.error("", e);
+        }
+
+        return new ConfiguredNotificationService(service, propertyValues);
+    }
+
+    public static Element getChild(final Element element, final String tagName) {
+        final List<Element> children = getChildElementsByTagName(element, tagName);
+        if (children.isEmpty()) {
+            return null;
+        }
+
+        if (children.size() > 1) {
+            return null;
+        }
+
+        return children.get(0);
+    }
+
+    public static List<Element> getChildElementsByTagName(final Element element, final String tagName) {
+        final List<Element> matches = new ArrayList<>();
+        final NodeList nodeList = element.getChildNodes();
+        for (int i = 0; i < nodeList.getLength(); i++) {
+            final Node node = nodeList.item(i);
+            if (!(node instanceof Element)) {
+                continue;
+            }
+
+            final Element child = (Element) nodeList.item(i);
+            if (child.getNodeName().equals(tagName)) {
+                matches.add(child);
+            }
+        }
+
+        return matches;
+    }
+
+    private static class ConfiguredNotificationService {
+        private final NotificationService service;
+        private final Map<String, String> properties;
+
+        public ConfiguredNotificationService(final NotificationService service, final Map<String, String> properties) {
+            this.service = service;
+            this.properties = properties;
+        }
+
+        public NotificationService getService() {
+            return service;
+        }
+
+        public Map<String, String> getProperties() {
+            return properties;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/e2d3d1b7/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServicePropertyValue.java
----------------------------------------------------------------------
diff --git a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServicePropertyValue.java b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServicePropertyValue.java
index 944dff6..582b342 100644
--- a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServicePropertyValue.java
+++ b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/NotificationServicePropertyValue.java
@@ -1,120 +1,120 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.bootstrap;
-
-import java.util.concurrent.TimeUnit;
-
-import org.apache.nifi.attribute.expression.language.PreparedQuery;
-import org.apache.nifi.attribute.expression.language.Query;
-import org.apache.nifi.components.PropertyValue;
-import org.apache.nifi.controller.ControllerService;
-import org.apache.nifi.expression.AttributeValueDecorator;
-import org.apache.nifi.flowfile.FlowFile;
-import org.apache.nifi.processor.DataUnit;
-import org.apache.nifi.processor.exception.ProcessException;
-import org.apache.nifi.util.FormatUtils;
-
-public class NotificationServicePropertyValue implements PropertyValue {
-    private final String rawValue;
-    private final PreparedQuery preparedQuery;
-
-    public NotificationServicePropertyValue(final String rawValue) {
-        this.rawValue = rawValue;
-        this.preparedQuery = Query.prepare(rawValue);
-    }
-
-    @Override
-    public String getValue() {
-        return rawValue;
-    }
-
-    @Override
-    public Integer asInteger() {
-        return (rawValue == null) ? null : Integer.parseInt(rawValue.trim());
-    }
-
-    @Override
-    public Long asLong() {
-        return (rawValue == null) ? null : Long.parseLong(rawValue.trim());
-    }
-
-    @Override
-    public Boolean asBoolean() {
-        return (rawValue == null) ? null : Boolean.parseBoolean(rawValue.trim());
-    }
-
-    @Override
-    public Float asFloat() {
-        return (rawValue == null) ? null : Float.parseFloat(rawValue.trim());
-    }
-
-    @Override
-    public Double asDouble() {
-        return (rawValue == null) ? null : Double.parseDouble(rawValue.trim());
-    }
-
-    @Override
-    public Long asTimePeriod(final TimeUnit timeUnit) {
-        return (rawValue == null) ? null : FormatUtils.getTimeDuration(rawValue.trim(), timeUnit);
-    }
-
-    @Override
-    public Double asDataSize(final DataUnit dataUnit) {
-        return rawValue == null ? null : DataUnit.parseDataSize(rawValue.trim(), dataUnit);
-    }
-
-    @Override
-    public PropertyValue evaluateAttributeExpressions() throws ProcessException {
-        return evaluateAttributeExpressions((AttributeValueDecorator) null);
-    }
-
-    @Override
-    public PropertyValue evaluateAttributeExpressions(final AttributeValueDecorator decorator) throws ProcessException {
-        return new NotificationServicePropertyValue(preparedQuery.evaluateExpressions((FlowFile) null, decorator));
-    }
-
-    @Override
-    public PropertyValue evaluateAttributeExpressions(final FlowFile flowFile) throws ProcessException {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public PropertyValue evaluateAttributeExpressions(final FlowFile flowFile, final AttributeValueDecorator decorator) throws ProcessException {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public String toString() {
-        return rawValue;
-    }
-
-    @Override
-    public ControllerService asControllerService() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public <T extends ControllerService> T asControllerService(final Class<T> serviceType) throws IllegalArgumentException {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean isSet() {
-        return rawValue != null;
-    }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.bootstrap;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.nifi.attribute.expression.language.PreparedQuery;
+import org.apache.nifi.attribute.expression.language.Query;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.expression.AttributeValueDecorator;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.DataUnit;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.util.FormatUtils;
+
+public class NotificationServicePropertyValue implements PropertyValue {
+    private final String rawValue;
+    private final PreparedQuery preparedQuery;
+
+    public NotificationServicePropertyValue(final String rawValue) {
+        this.rawValue = rawValue;
+        this.preparedQuery = Query.prepare(rawValue);
+    }
+
+    @Override
+    public String getValue() {
+        return rawValue;
+    }
+
+    @Override
+    public Integer asInteger() {
+        return (rawValue == null) ? null : Integer.parseInt(rawValue.trim());
+    }
+
+    @Override
+    public Long asLong() {
+        return (rawValue == null) ? null : Long.parseLong(rawValue.trim());
+    }
+
+    @Override
+    public Boolean asBoolean() {
+        return (rawValue == null) ? null : Boolean.parseBoolean(rawValue.trim());
+    }
+
+    @Override
+    public Float asFloat() {
+        return (rawValue == null) ? null : Float.parseFloat(rawValue.trim());
+    }
+
+    @Override
+    public Double asDouble() {
+        return (rawValue == null) ? null : Double.parseDouble(rawValue.trim());
+    }
+
+    @Override
+    public Long asTimePeriod(final TimeUnit timeUnit) {
+        return (rawValue == null) ? null : FormatUtils.getTimeDuration(rawValue.trim(), timeUnit);
+    }
+
+    @Override
+    public Double asDataSize(final DataUnit dataUnit) {
+        return rawValue == null ? null : DataUnit.parseDataSize(rawValue.trim(), dataUnit);
+    }
+
+    @Override
+    public PropertyValue evaluateAttributeExpressions() throws ProcessException {
+        return evaluateAttributeExpressions((AttributeValueDecorator) null);
+    }
+
+    @Override
+    public PropertyValue evaluateAttributeExpressions(final AttributeValueDecorator decorator) throws ProcessException {
+        return new NotificationServicePropertyValue(preparedQuery.evaluateExpressions((FlowFile) null, decorator));
+    }
+
+    @Override
+    public PropertyValue evaluateAttributeExpressions(final FlowFile flowFile) throws ProcessException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public PropertyValue evaluateAttributeExpressions(final FlowFile flowFile, final AttributeValueDecorator decorator) throws ProcessException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String toString() {
+        return rawValue;
+    }
+
+    @Override
+    public ControllerService asControllerService() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public <T extends ControllerService> T asControllerService(final Class<T> serviceType) throws IllegalArgumentException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isSet() {
+        return rawValue != null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/e2d3d1b7/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/AbstractNotificationService.java
----------------------------------------------------------------------
diff --git a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/AbstractNotificationService.java b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/AbstractNotificationService.java
index a91a377..109b1bc 100644
--- a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/AbstractNotificationService.java
+++ b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/AbstractNotificationService.java
@@ -1,37 +1,37 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.bootstrap.notification;
-
-import org.apache.nifi.components.AbstractConfigurableComponent;
-
-public abstract class AbstractNotificationService extends AbstractConfigurableComponent implements NotificationService {
-    private String identifier;  // effectively final
-
-    @Override
-    public final void initialize(final NotificationInitializationContext context) {
-        this.identifier = context.getIdentifier();
-        init(context);
-    }
-
-    protected void init(final NotificationInitializationContext context) {
-    }
-
-    @Override
-    public String getIdentifier() {
-        return identifier;
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.bootstrap.notification;
+
+import org.apache.nifi.components.AbstractConfigurableComponent;
+
+public abstract class AbstractNotificationService extends AbstractConfigurableComponent implements NotificationService {
+    private String identifier;  // effectively final
+
+    @Override
+    public final void initialize(final NotificationInitializationContext context) {
+        this.identifier = context.getIdentifier();
+        init(context);
+    }
+
+    protected void init(final NotificationInitializationContext context) {
+    }
+
+    @Override
+    public String getIdentifier() {
+        return identifier;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/e2d3d1b7/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationContext.java
----------------------------------------------------------------------
diff --git a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationContext.java b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationContext.java
index 2c030a1..ba4ba93 100644
--- a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationContext.java
+++ b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationContext.java
@@ -1,44 +1,44 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.bootstrap.notification;
-
-import java.util.Map;
-
-import org.apache.nifi.components.PropertyDescriptor;
-import org.apache.nifi.components.PropertyValue;
-
-public interface NotificationContext {
-
-    /**
-     * Returns the configured value for the given PropertyDescriptor. Note that the implementation
-     * of PropertyValue will throw an Exception if calling {@link PropertyValue#asControllerService()} or
-     * {@link PropertyValue#asControllerService(Class)}, as Controller Services are not allowed to be
-     * referenced by Notification Services
-     *
-     * @param descriptor the property whose value should be returned
-     * @return the configured value for the given PropertyDescriptor, or the default
-     *         value for the PropertyDescriptor if no value has been configured
-     */
-    PropertyValue getProperty(PropertyDescriptor descriptor);
-
-    /**
-     * @return a Map of all PropertyDescriptors to their configured values. This
-     *         Map may or may not be modifiable, but modifying its values will not
-     *         change the values of the processor's properties
-     */
-    Map<PropertyDescriptor, String> getProperties();
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.bootstrap.notification;
+
+import java.util.Map;
+
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+
+public interface NotificationContext {
+
+    /**
+     * Returns the configured value for the given PropertyDescriptor. Note that the implementation
+     * of PropertyValue will throw an Exception if calling {@link PropertyValue#asControllerService()} or
+     * {@link PropertyValue#asControllerService(Class)}, as Controller Services are not allowed to be
+     * referenced by Notification Services
+     *
+     * @param descriptor the property whose value should be returned
+     * @return the configured value for the given PropertyDescriptor, or the default
+     *         value for the PropertyDescriptor if no value has been configured
+     */
+    PropertyValue getProperty(PropertyDescriptor descriptor);
+
+    /**
+     * @return a Map of all PropertyDescriptors to their configured values. This
+     *         Map may or may not be modifiable, but modifying its values will not
+     *         change the values of the processor's properties
+     */
+    Map<PropertyDescriptor, String> getProperties();
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/e2d3d1b7/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationFailedException.java
----------------------------------------------------------------------
diff --git a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationFailedException.java b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationFailedException.java
index fcab95b..93c3459 100644
--- a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationFailedException.java
+++ b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationFailedException.java
@@ -1,33 +1,33 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.bootstrap.notification;
-
-public class NotificationFailedException extends Exception {
-    private static final long serialVersionUID = 1L;
-
-    public NotificationFailedException(final String message) {
-        super(message);
-    }
-
-    public NotificationFailedException(final String message, final Throwable t) {
-        super(message, t);
-    }
-
-    public NotificationFailedException(final Throwable t) {
-        super(t);
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.bootstrap.notification;
+
+public class NotificationFailedException extends Exception {
+    private static final long serialVersionUID = 1L;
+
+    public NotificationFailedException(final String message) {
+        super(message);
+    }
+
+    public NotificationFailedException(final String message, final Throwable t) {
+        super(message, t);
+    }
+
+    public NotificationFailedException(final Throwable t) {
+        super(t);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/e2d3d1b7/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationInitializationContext.java
----------------------------------------------------------------------
diff --git a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationInitializationContext.java b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationInitializationContext.java
index c75c8f4..88e0445 100644
--- a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationInitializationContext.java
+++ b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationInitializationContext.java
@@ -1,39 +1,39 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.bootstrap.notification;
-
-
-import org.apache.nifi.components.PropertyDescriptor;
-import org.apache.nifi.components.PropertyValue;
-
-public interface NotificationInitializationContext {
-
-    /**
-     * Returns the configured value for the given PropertyDescriptor
-     *
-     * @param descriptor the property to fetch the value for
-     * @return the configured value for the given PropertyDescriptor, or the default value for the PropertyDescriptor
-     *         if no value has been configured.
-     */
-    PropertyValue getProperty(PropertyDescriptor descriptor);
-
-    /**
-     * @return the identifier for the NotificationService
-     */
-    String getIdentifier();
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.bootstrap.notification;
+
+
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+
+public interface NotificationInitializationContext {
+
+    /**
+     * Returns the configured value for the given PropertyDescriptor
+     *
+     * @param descriptor the property to fetch the value for
+     * @return the configured value for the given PropertyDescriptor, or the default value for the PropertyDescriptor
+     *         if no value has been configured.
+     */
+    PropertyValue getProperty(PropertyDescriptor descriptor);
+
+    /**
+     * @return the identifier for the NotificationService
+     */
+    String getIdentifier();
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/e2d3d1b7/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationService.java
----------------------------------------------------------------------
diff --git a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationService.java b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationService.java
index 044d906..abf9e80 100644
--- a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationService.java
+++ b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationService.java
@@ -1,56 +1,56 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.bootstrap.notification;
-
-import org.apache.nifi.components.ConfigurableComponent;
-
-/**
- * <p>
- * A NotificationService is simple mechanism that the Bootstrap can use to notify
- * interested parties when some event takes place, such as NiFi being started, stopped,
- * or restarted because the process died.
- * </p>
- *
- * <p>
- * <b>Note:</b> This feature was introduced in version 0.3.0 of NiFi and is likely to undergo
- * significant refactorings. As such, at this time it is NOT considered a public API and may well
- * change from version to version until the API has stabilized. At that point, it will become a public
- * API.
- * </p>
- *
- * @since 0.3.0
- */
-public interface NotificationService extends ConfigurableComponent {
-
-    /**
-     * Provides the NotificatoinService with access to objects that may be of use
-     * throughout the life of the service
-     *
-     * @param context of initialization
-     */
-    void initialize(NotificationInitializationContext context);
-
-    /**
-     * Notifies the configured recipients of some event
-     *
-     * @param context the context that is relevant for this notification
-     * @param subject the subject of the message
-     * @param message the message to be provided to recipients
-     */
-    void notify(NotificationContext context, String subject, String message) throws NotificationFailedException;
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.bootstrap.notification;
+
+import org.apache.nifi.components.ConfigurableComponent;
+
+/**
+ * <p>
+ * A NotificationService is simple mechanism that the Bootstrap can use to notify
+ * interested parties when some event takes place, such as NiFi being started, stopped,
+ * or restarted because the process died.
+ * </p>
+ *
+ * <p>
+ * <b>Note:</b> This feature was introduced in version 0.3.0 of NiFi and is likely to undergo
+ * significant refactorings. As such, at this time it is NOT considered a public API and may well
+ * change from version to version until the API has stabilized. At that point, it will become a public
+ * API.
+ * </p>
+ *
+ * @since 0.3.0
+ */
+public interface NotificationService extends ConfigurableComponent {
+
+    /**
+     * Provides the NotificatoinService with access to objects that may be of use
+     * throughout the life of the service
+     *
+     * @param context of initialization
+     */
+    void initialize(NotificationInitializationContext context);
+
+    /**
+     * Notifies the configured recipients of some event
+     *
+     * @param context the context that is relevant for this notification
+     * @param subject the subject of the message
+     * @param message the message to be provided to recipients
+     */
+    void notify(NotificationContext context, String subject, String message) throws NotificationFailedException;
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/e2d3d1b7/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationType.java
----------------------------------------------------------------------
diff --git a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationType.java b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationType.java
index 26d90da..a2645ec 100644
--- a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationType.java
+++ b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationType.java
@@ -1,23 +1,23 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.bootstrap.notification;
-
-public enum NotificationType {
-    NIFI_STARTED,
-    NIFI_STOPPED,
-    NIFI_DIED;
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.bootstrap.notification;
+
+public enum NotificationType {
+    NIFI_STARTED,
+    NIFI_STOPPED,
+    NIFI_DIED;
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/e2d3d1b7/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationValidationContext.java
----------------------------------------------------------------------
diff --git a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationValidationContext.java b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationValidationContext.java
index 8f556da..a02b108 100644
--- a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationValidationContext.java
+++ b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationValidationContext.java
@@ -1,105 +1,105 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.bootstrap.notification;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.nifi.attribute.expression.language.Query;
-import org.apache.nifi.attribute.expression.language.Query.Range;
-import org.apache.nifi.attribute.expression.language.StandardExpressionLanguageCompiler;
-import org.apache.nifi.bootstrap.NotificationServicePropertyValue;
-import org.apache.nifi.components.PropertyDescriptor;
-import org.apache.nifi.components.PropertyValue;
-import org.apache.nifi.components.ValidationContext;
-import org.apache.nifi.controller.ControllerService;
-import org.apache.nifi.controller.ControllerServiceLookup;
-import org.apache.nifi.expression.ExpressionLanguageCompiler;
-
-public class NotificationValidationContext implements ValidationContext {
-    private final NotificationContext context;
-    private final Map<String, Boolean> expressionLanguageSupported;
-
-    public NotificationValidationContext(final NotificationContext processContext) {
-        this.context = processContext;
-
-        final Map<PropertyDescriptor, String> properties = processContext.getProperties();
-        expressionLanguageSupported = new HashMap<>(properties.size());
-        for (final PropertyDescriptor descriptor : properties.keySet()) {
-            expressionLanguageSupported.put(descriptor.getName(), descriptor.isExpressionLanguageSupported());
-        }
-    }
-
-
-    @Override
-    public PropertyValue newPropertyValue(final String rawValue) {
-        return new NotificationServicePropertyValue(rawValue);
-    }
-
-    @Override
-    public ExpressionLanguageCompiler newExpressionLanguageCompiler() {
-        return new StandardExpressionLanguageCompiler();
-    }
-
-    @Override
-    public ValidationContext getControllerServiceValidationContext(final ControllerService controllerService) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public PropertyValue getProperty(final PropertyDescriptor property) {
-        return context.getProperty(property);
-    }
-
-    @Override
-    public Map<PropertyDescriptor, String> getProperties() {
-        return context.getProperties();
-    }
-
-    @Override
-    public String getAnnotationData() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public ControllerServiceLookup getControllerServiceLookup() {
-        return null;
-    }
-
-    @Override
-    public boolean isValidationRequired(final ControllerService service) {
-        return true;
-    }
-
-    @Override
-    public boolean isExpressionLanguagePresent(final String value) {
-        if (value == null) {
-            return false;
-        }
-
-        final List<Range> elRanges = Query.extractExpressionRanges(value);
-        return (elRanges != null && !elRanges.isEmpty());
-    }
-
-    @Override
-    public boolean isExpressionLanguageSupported(final String propertyName) {
-        final Boolean supported = expressionLanguageSupported.get(propertyName);
-        return Boolean.TRUE.equals(supported);
-    }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.bootstrap.notification;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.nifi.attribute.expression.language.Query;
+import org.apache.nifi.attribute.expression.language.Query.Range;
+import org.apache.nifi.attribute.expression.language.StandardExpressionLanguageCompiler;
+import org.apache.nifi.bootstrap.NotificationServicePropertyValue;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.components.ValidationContext;
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.controller.ControllerServiceLookup;
+import org.apache.nifi.expression.ExpressionLanguageCompiler;
+
+public class NotificationValidationContext implements ValidationContext {
+    private final NotificationContext context;
+    private final Map<String, Boolean> expressionLanguageSupported;
+
+    public NotificationValidationContext(final NotificationContext processContext) {
+        this.context = processContext;
+
+        final Map<PropertyDescriptor, String> properties = processContext.getProperties();
+        expressionLanguageSupported = new HashMap<>(properties.size());
+        for (final PropertyDescriptor descriptor : properties.keySet()) {
+            expressionLanguageSupported.put(descriptor.getName(), descriptor.isExpressionLanguageSupported());
+        }
+    }
+
+
+    @Override
+    public PropertyValue newPropertyValue(final String rawValue) {
+        return new NotificationServicePropertyValue(rawValue);
+    }
+
+    @Override
+    public ExpressionLanguageCompiler newExpressionLanguageCompiler() {
+        return new StandardExpressionLanguageCompiler();
+    }
+
+    @Override
+    public ValidationContext getControllerServiceValidationContext(final ControllerService controllerService) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public PropertyValue getProperty(final PropertyDescriptor property) {
+        return context.getProperty(property);
+    }
+
+    @Override
+    public Map<PropertyDescriptor, String> getProperties() {
+        return context.getProperties();
+    }
+
+    @Override
+    public String getAnnotationData() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ControllerServiceLookup getControllerServiceLookup() {
+        return null;
+    }
+
+    @Override
+    public boolean isValidationRequired(final ControllerService service) {
+        return true;
+    }
+
+    @Override
+    public boolean isExpressionLanguagePresent(final String value) {
+        if (value == null) {
+            return false;
+        }
+
+        final List<Range> elRanges = Query.extractExpressionRanges(value);
+        return (elRanges != null && !elRanges.isEmpty());
+    }
+
+    @Override
+    public boolean isExpressionLanguageSupported(final String propertyName) {
+        final Boolean supported = expressionLanguageSupported.get(propertyName);
+        return Boolean.TRUE.equals(supported);
+    }
+
+}