You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ma...@apache.org on 2015/04/10 15:43:36 UTC

[01/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Repository: incubator-nifi
Updated Branches:
  refs/heads/NIFI-25 373f470b6 -> 93a121044


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp b/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp
index 3c32299..4a23de4 100644
--- a/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp
+++ b/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp
@@ -31,7 +31,6 @@
         <link rel="stylesheet" href="../nifi/css/reset.css" type="text/css" />
         <link rel="stylesheet" href="css/main.css" type="text/css" />
         <script type="text/javascript" src="../nifi/js/jquery/jquery-2.1.1.min.js"></script>
-        <script src="http://code.jquery.com/jquery-migrate-1.2.1.js"></script>
         <script type="text/javascript" src="../nifi/js/jquery/jquery.center.js"></script>
         <script type="text/javascript" src="../nifi/js/jquery/jquery.each.js"></script>
         <script type="text/javascript" src="../nifi/js/jquery/jquery.tab.js"></script>
@@ -59,7 +58,7 @@
         <title>Update Attribute</title>
     </head>
     <body>
-        <div id="attribute-updater-processor-id" class="hidden">${param.processorId}</div>
+        <div id="attribute-updater-processor-id" class="hidden">${param.id}</div>
         <div id="attribute-updater-client-id" class="hidden">${param.clientId}</div>
         <div id="attribute-updater-revision" class="hidden">${param.revision}</div>
         <div id="attribute-updater-editable" class="hidden">${param.editable}</div>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/web.xml b/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/web.xml
index 8969fea..a102fe1 100644
--- a/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/web.xml
+++ b/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/web.xml
@@ -37,10 +37,10 @@
     </servlet>
     <servlet-mapping>
         <servlet-name>worksheet</servlet-name>
-        <url-pattern>/worksheet</url-pattern>
+        <url-pattern>/configure</url-pattern>
     </servlet-mapping>
     
     <welcome-file-list>
-        <welcome-file>worksheet</welcome-file>
+        <welcome-file>configure</welcome-file>
     </welcome-file-list>
 </web-app>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/js/application.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/js/application.js b/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/js/application.js
index f6d1d7b..eb7bdfb 100644
--- a/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/js/application.js
+++ b/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/js/application.js
@@ -289,8 +289,6 @@ var ua = {
                                     type: 'GET',
                                     data: {
                                         processorId: ua.getProcessorId(),
-                                        revision: ua.getRevision(),
-                                        clientId: ua.getClientId(),
                                         q: copyFromRuleName
                                     },
                                     dataType: 'json',
@@ -420,8 +418,6 @@ var ua = {
                     type: 'GET',
                     data: {
                         processorId: ua.getProcessorId(),
-                        revision: ua.getRevision(),
-                        clientId: ua.getClientId(),
                         q: request.term
                     },
                     dataType: 'json',
@@ -1024,8 +1020,6 @@ var ua = {
             type: 'GET',
             url: 'api/criteria/rules?' + $.param({
                 processorId: ua.getProcessorId(),
-                revision: ua.getRevision(),
-                clientId: ua.getClientId(),
                 verbose: true
             })
         }).done(function (response) {
@@ -1051,9 +1045,7 @@ var ua = {
         var evaluationContext = $.ajax({
             type: 'GET',
             url: 'api/criteria/evaluation-context?' + $.param({
-                processorId: ua.getProcessorId(),
-                revision: ua.getRevision(),
-                clientId: ua.getClientId()
+                processorId: ua.getProcessorId()
             })
         }).done(function (evaluationContext) {
             // record the currently selected value
@@ -1100,8 +1092,6 @@ var ua = {
                     type: 'GET',
                     url: 'api/criteria/rules/' + encodeURIComponent(ruleId) + '?' + $.param({
                         processorId: ua.getProcessorId(),
-                        revision: ua.getRevision(),
-                        clientId: ua.getClientId(),
                         verbose: true
                     })
                 }).then(function (response) {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/pom.xml b/nifi/pom.xml
index e7d8f5b..2e2346a 100644
--- a/nifi/pom.xml
+++ b/nifi/pom.xml
@@ -676,6 +676,11 @@
             </dependency>
             <dependency>
                 <groupId>org.apache.nifi</groupId>
+                <artifactId>nifi-ui-extension</artifactId>
+                <version>0.1.0-incubating-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.nifi</groupId>
                 <artifactId>nifi-flowfile-packager</artifactId>
                 <version>0.1.0-incubating-SNAPSHOT</version>
             </dependency>


[23/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardActionDAO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardActionDAO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardActionDAO.java
index 4b89655..34623f4 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardActionDAO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/impl/StandardActionDAO.java
@@ -32,7 +32,7 @@ import org.apache.nifi.action.Action;
 import org.apache.nifi.action.Component;
 import org.apache.nifi.action.Operation;
 import org.apache.nifi.action.component.details.ComponentDetails;
-import org.apache.nifi.action.component.details.ProcessorDetails;
+import org.apache.nifi.action.component.details.ExtensionDetails;
 import org.apache.nifi.action.component.details.RemoteProcessGroupDetails;
 import org.apache.nifi.action.details.ActionDetails;
 import org.apache.nifi.action.details.ConfigureDetails;
@@ -70,7 +70,7 @@ public class StandardActionDAO implements ActionDAO {
     // -----------------
     // component details
     // -----------------
-    private static final String INSERT_PROCESSOR_DETAILS = "INSERT INTO PROCESSOR_DETAILS ("
+    private static final String INSERT_EXTENSION_DETAILS = "INSERT INTO PROCESSOR_DETAILS ("
             + "ACTION_ID, TYPE"
             + ") VALUES ("
             + "?, "
@@ -145,7 +145,7 @@ public class StandardActionDAO implements ActionDAO {
     // -----------------
     // component details
     // -----------------
-    private static final String SELECT_PROCESSOR_DETAILS_FOR_ACTION = "SELECT * FROM PROCESSOR_DETAILS WHERE ACTION_ID = ?";
+    private static final String SELECT_EXTENSION_DETAILS_FOR_ACTION = "SELECT * FROM PROCESSOR_DETAILS WHERE ACTION_ID = ?";
 
     private static final String SELECT_REMOTE_PROCESS_GROUP_DETAILS_FOR_ACTION = "SELECT * FROM REMOTE_PROCESS_GROUP_DETAILS WHERE ACTION_ID = ?";
 
@@ -179,8 +179,8 @@ public class StandardActionDAO implements ActionDAO {
             + "ORDER BY A.ACTION_TIMESTAMP DESC "
             + "LIMIT 4";
 
-    private Connection connection;
-    private Map<String, String> columnMap;
+    private final Connection connection;
+    private final Map<String, String> columnMap;
 
     public StandardActionDAO(Connection connection) {
         this.connection = connection;
@@ -233,8 +233,8 @@ public class StandardActionDAO implements ActionDAO {
 
             // determine the type of component
             ComponentDetails componentDetails = action.getComponentDetails();
-            if (componentDetails instanceof ProcessorDetails) {
-                createProcessorDetails(action.getId(), (ProcessorDetails) componentDetails);
+            if (componentDetails instanceof ExtensionDetails) {
+                createExtensionDetails(action.getId(), (ExtensionDetails) componentDetails);
             } else if (componentDetails instanceof RemoteProcessGroupDetails) {
                 createRemoteProcessGroupDetails(action.getId(), (RemoteProcessGroupDetails) componentDetails);
             }
@@ -260,26 +260,26 @@ public class StandardActionDAO implements ActionDAO {
     }
 
     /**
-     * Persists the processor details.
+     * Persists the extension details.
      *
      * @param actionId
-     * @param processorDetails
+     * @param extensionDetails
      * @throws DataAccessException
      */
-    private void createProcessorDetails(int actionId, ProcessorDetails processorDetails) throws DataAccessException {
+    private void createExtensionDetails(int actionId, ExtensionDetails extensionDetails) throws DataAccessException {
         PreparedStatement statement = null;
         try {
-            // obtain a statement to insert to the processor action table
-            statement = connection.prepareStatement(INSERT_PROCESSOR_DETAILS);
+            // obtain a statement to insert to the extension action table
+            statement = connection.prepareStatement(INSERT_EXTENSION_DETAILS);
             statement.setInt(1, actionId);
-            statement.setString(2, StringUtils.left(processorDetails.getType(), 1000));
+            statement.setString(2, StringUtils.left(extensionDetails.getType(), 1000));
 
             // insert the action
             int updateCount = statement.executeUpdate();
 
             // ensure the operation completed successfully
             if (updateCount != 1) {
-                throw new DataAccessException("Unable to insert processor details.");
+                throw new DataAccessException("Unable to insert extension details.");
             }
         } catch (SQLException sqle) {
             throw new DataAccessException(sqle);
@@ -601,8 +601,8 @@ public class StandardActionDAO implements ActionDAO {
 
                 // get the component details if appropriate
                 ComponentDetails componentDetails = null;
-                if (Component.Processor.equals(component)) {
-                    componentDetails = getProcessorDetails(actionId);
+                if (Component.Processor.equals(component) || Component.ControllerService.equals(component)  || Component.ReportingTask.equals(component)) {
+                    componentDetails = getExtensionDetails(actionId);
                 } else if (Component.RemoteProcessGroup.equals(component)) {
                     componentDetails = getRemoteProcessGroupDetails(actionId);
                 }
@@ -675,8 +675,8 @@ public class StandardActionDAO implements ActionDAO {
 
                 // get the component details if appropriate
                 ComponentDetails componentDetails = null;
-                if (Component.Processor.equals(component)) {
-                    componentDetails = getProcessorDetails(actionId);
+                if (Component.Processor.equals(component) || Component.ControllerService.equals(component) || Component.ReportingTask.equals(component)) {
+                    componentDetails = getExtensionDetails(actionId);
                 } else if (Component.RemoteProcessGroup.equals(component)) {
                     componentDetails = getRemoteProcessGroupDetails(actionId);
                 }
@@ -713,19 +713,19 @@ public class StandardActionDAO implements ActionDAO {
     }
 
     /**
-     * Loads the specified processor details.
+     * Loads the specified extension details.
      *
      * @param actionId
      * @return
      * @throws DataAccessException
      */
-    private ProcessorDetails getProcessorDetails(Integer actionId) throws DataAccessException {
-        ProcessorDetails processorDetails = null;
+    private ExtensionDetails getExtensionDetails(Integer actionId) throws DataAccessException {
+        ExtensionDetails extensionDetails = null;
         PreparedStatement statement = null;
         ResultSet rs = null;
         try {
             // create the statement
-            statement = connection.prepareStatement(SELECT_PROCESSOR_DETAILS_FOR_ACTION);
+            statement = connection.prepareStatement(SELECT_EXTENSION_DETAILS_FOR_ACTION);
             statement.setInt(1, actionId);
 
             // execute the query
@@ -733,8 +733,8 @@ public class StandardActionDAO implements ActionDAO {
 
             // ensure results
             if (rs.next()) {
-                processorDetails = new ProcessorDetails();
-                processorDetails.setType(rs.getString("TYPE"));
+                extensionDetails = new ExtensionDetails();
+                extensionDetails.setType(rs.getString("TYPE"));
             }
         } catch (SQLException sqle) {
             throw new DataAccessException(sqle);
@@ -743,7 +743,7 @@ public class StandardActionDAO implements ActionDAO {
             RepositoryUtils.closeQuietly(statement);
         }
 
-        return processorDetails;
+        return extensionDetails;
     }
 
     /**
@@ -931,7 +931,7 @@ public class StandardActionDAO implements ActionDAO {
     }
 
     @Override
-    public Map<String, List<PreviousValue>> getPreviousValues(String processorId) {
+    public Map<String, List<PreviousValue>> getPreviousValues(String componentId) {
         Map<String, List<PreviousValue>> previousValues = new LinkedHashMap<>();
 
         PreparedStatement statement = null;
@@ -939,7 +939,7 @@ public class StandardActionDAO implements ActionDAO {
         try {
             // create the statement
             statement = connection.prepareStatement(SELECT_PREVIOUSLY_CONFIGURED_FIELDS);
-            statement.setString(1, processorId);
+            statement.setString(1, componentId);
 
             // execute the query
             rs = statement.executeQuery();
@@ -947,7 +947,7 @@ public class StandardActionDAO implements ActionDAO {
             // ensure results
             while (rs.next()) {
                 final String property = rs.getString("NAME");
-                previousValues.put(property, getPreviousValuesForProperty(processorId, property));
+                previousValues.put(property, getPreviousValuesForProperty(componentId, property));
             }
         } catch (SQLException sqle) {
             throw new DataAccessException(sqle);
@@ -959,7 +959,7 @@ public class StandardActionDAO implements ActionDAO {
         return previousValues;
     }
 
-    private List<PreviousValue> getPreviousValuesForProperty(final String processorId, final String property) {
+    private List<PreviousValue> getPreviousValuesForProperty(final String componentId, final String property) {
         List<PreviousValue> previousValues = new ArrayList<>();
 
         PreparedStatement statement = null;
@@ -967,7 +967,7 @@ public class StandardActionDAO implements ActionDAO {
         try {
             // create the statement
             statement = connection.prepareStatement(SELECT_PREVIOUS_VALUES);
-            statement.setString(1, processorId);
+            statement.setString(1, componentId);
             statement.setString(2, property);
 
             // execute the query

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/AuditService.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/AuditService.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/AuditService.java
index 0843bd8..7ca4e06 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/AuditService.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/AuditService.java
@@ -40,12 +40,12 @@ public interface AuditService {
 
     /**
      * Finds the previous values for the specified property in the specified
-     * processor. Returns null if there are none.
+     * component. Returns null if there are none.
      *
-     * @param processorId
+     * @param componentId
      * @return
      */
-    Map<String, List<PreviousValue>> getPreviousValues(String processorId);
+    Map<String, List<PreviousValue>> getPreviousValues(String componentId);
 
     /**
      * Get the actions within the given date range.

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetPreviousValues.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetPreviousValues.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetPreviousValues.java
index 5ce663e..569439b 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetPreviousValues.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/action/GetPreviousValues.java
@@ -28,16 +28,16 @@ import org.apache.nifi.history.PreviousValue;
  */
 public class GetPreviousValues implements AdministrationAction<Map<String, List<PreviousValue>>> {
 
-    private final String processorId;
+    private final String componentId;
 
-    public GetPreviousValues(String processorId) {
-        this.processorId = processorId;
+    public GetPreviousValues(String componentId) {
+        this.componentId = componentId;
     }
 
     @Override
     public Map<String, List<PreviousValue>> execute(DAOFactory daoFactory, AuthorityProvider authorityProvider) {
         ActionDAO actionDao = daoFactory.getActionDAO();
-        return actionDao.getPreviousValues(processorId);
+        return actionDao.getPreviousValues(componentId);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardAuditService.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardAuditService.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardAuditService.java
index 127f1df..721e6b2 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardAuditService.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/impl/StandardAuditService.java
@@ -81,7 +81,7 @@ public class StandardAuditService implements AuditService {
     }
 
     @Override
-    public Map<String, List<PreviousValue>> getPreviousValues(String processorId) {
+    public Map<String, List<PreviousValue>> getPreviousValues(String componentId) {
         Transaction transaction = null;
         Map<String, List<PreviousValue>> previousValues = null;
 
@@ -91,7 +91,7 @@ public class StandardAuditService implements AuditService {
             transaction = transactionBuilder.start();
 
             // seed the accounts
-            GetPreviousValues getActions = new GetPreviousValues(processorId);
+            GetPreviousValues getActions = new GetPreviousValues(componentId);
             previousValues = transaction.execute(getActions);
 
             // commit the transaction

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ComponentHistoryDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ComponentHistoryDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ComponentHistoryDTO.java
new file mode 100644
index 0000000..3bdbe28
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ComponentHistoryDTO.java
@@ -0,0 +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.web.api.dto;
+
+import java.util.Map;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * History of a component's properties.
+ */
+@XmlType(name = "componentHistory")
+public class ComponentHistoryDTO {
+
+    private String componentId;
+    private Map<String, PropertyHistoryDTO> propertyHistory;
+
+    /**
+     * The component id.
+     *
+     * @return
+     */
+    public String getComponentId() {
+        return componentId;
+    }
+
+    public void setComponentId(String componentId) {
+        this.componentId = componentId;
+    }
+
+    /**
+     * The history for this components properties.
+     *
+     * @return
+     */
+    public Map<String, PropertyHistoryDTO> getPropertyHistory() {
+        return propertyHistory;
+    }
+
+    public void setPropertyHistory(Map<String, PropertyHistoryDTO> propertyHistory) {
+        this.propertyHistory = propertyHistory;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerConfigurationDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerConfigurationDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerConfigurationDTO.java
index b916025..190cb47 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerConfigurationDTO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerConfigurationDTO.java
@@ -16,7 +16,10 @@
  */
 package org.apache.nifi.web.api.dto;
 
+import java.util.Date;
 import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import org.apache.nifi.web.api.dto.util.TimeAdapter;
 
 /**
  * Details for the controller configuration.
@@ -32,6 +35,7 @@ public class ControllerConfigurationDTO {
     private Long autoRefreshIntervalSeconds;
     private Boolean siteToSiteSecure;
 
+    private Date currentTime;
     private Integer timeOffset;
 
     private String contentViewerUrl;
@@ -118,6 +122,20 @@ public class ControllerConfigurationDTO {
     }
 
     /**
+     * The current time on the server.
+     * 
+     * @return 
+     */
+    @XmlJavaTypeAdapter(TimeAdapter.class)
+    public Date getCurrentTime() {
+        return currentTime;
+    }
+
+    public void setCurrentTime(Date currentTime) {
+        this.currentTime = currentTime;
+    }
+
+    /**
      * The time offset of the server.
      *
      * @return

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceDTO.java
new file mode 100644
index 0000000..75d18a2
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceDTO.java
@@ -0,0 +1,190 @@
+/*
+ * 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.web.api.dto;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * A Controller Service that can be shared by other components
+ */
+@XmlType(name = "controllerService")
+public class ControllerServiceDTO extends NiFiComponentDTO {
+
+    private String name;
+    private String type;
+    private String comments;
+    private String availability;
+    private String state;
+    
+    private Map<String, String> properties;
+    private Map<String, PropertyDescriptorDTO> descriptors;
+ 
+    private String customUiUrl;
+    private String annotationData;
+    
+    private Set<ControllerServiceReferencingComponentDTO> referencingComponents;
+    
+    private Collection<String> validationErrors;
+
+    /**
+     * The controller service name.
+     * 
+     * @return 
+     */
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * The controller service type.
+     * 
+     * @return 
+     */
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+    
+
+    /**
+     * The comment for the Controller Service
+     * @return
+     */
+    public String getComments() {
+		return comments;
+	}
+
+	public void setComments(String comments) {
+		this.comments = comments;
+	}
+
+	/**
+     * Where this service is available. Possible values are NCM, NODE.
+     * 
+     * @return 
+     */
+    public String getAvailability() {
+        return availability;
+    }
+
+    public void setAvailability(String availability) {
+        this.availability = availability;
+    }
+
+    /**
+     * The state of this controller service. Possible values are ENABLED, ENABLING, DISABLED, DISABLING.
+     * @return 
+     */
+    public String getState() {
+        return state;
+    }
+
+    public void setState(String state) {
+        this.state = state;
+    }
+
+    /**
+     * The controller service properties.
+     * 
+     * @return 
+     */
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    public void setProperties(Map<String, String> properties) {
+        this.properties = properties;
+    }
+
+    /**
+     * The descriptors for the controller service properties.
+     * 
+     * @return 
+     */
+    public Map<String, PropertyDescriptorDTO> getDescriptors() {
+        return descriptors;
+    }
+
+    public void setDescriptors(Map<String, PropertyDescriptorDTO> descriptors) {
+        this.descriptors = descriptors;
+    }
+
+    /**
+     * Returns the URL for this controller services custom configuration UI
+     * if applicable. Null otherwise.
+     *
+     * @return
+     */
+    public String getCustomUiUrl() {
+        return customUiUrl;
+    }
+
+    public void setCustomUiUrl(String customUiUrl) {
+        this.customUiUrl = customUiUrl;
+    }
+
+    /**
+     * The annotation data for this controller service.
+     * 
+     * @return 
+     */
+    public String getAnnotationData() {
+        return annotationData;
+    }
+
+    public void setAnnotationData(String annotationData) {
+        this.annotationData = annotationData;
+    }
+
+    /**
+     * All components referencing this controller service.
+     * 
+     * @return 
+     */
+    public Set<ControllerServiceReferencingComponentDTO> getReferencingComponents() {
+        return referencingComponents;
+    }
+
+    public void setReferencingComponents(Set<ControllerServiceReferencingComponentDTO> referencingComponents) {
+        this.referencingComponents = referencingComponents;
+    }
+
+    /**
+     * Gets the validation errors from this controller service. These validation errors
+     * represent the problems with the controller service that must be resolved before it
+     * can be enabled.
+     *
+     * @return The validation errors
+     */
+    public Collection<String> getValidationErrors() {
+        return validationErrors;
+    }
+
+    public void setValidationErrors(Collection<String> validationErrors) {
+        this.validationErrors = validationErrors;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceReferencingComponentDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceReferencingComponentDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceReferencingComponentDTO.java
new file mode 100644
index 0000000..7fc57ff
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceReferencingComponentDTO.java
@@ -0,0 +1,207 @@
+/*
+ * 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.web.api.dto;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * A component referencing a controller service. This can either be another
+ * controller service or a processor. Depending on the type of component
+ * different properties may be set.
+ */
+@XmlType(name = "controllerServiceReferencingComponent")
+public class ControllerServiceReferencingComponentDTO {
+    private String groupId;
+    private String id;
+    private String name;
+    private String type;
+    private String state;
+
+    private Map<String, String> properties;
+    private Map<String, PropertyDescriptorDTO> descriptors;
+    
+    private Collection<String> validationErrors;
+    
+    private String referenceType;
+    private Integer activeThreadCount;
+    
+    private Boolean referenceCycle;
+    private Set<ControllerServiceReferencingComponentDTO> referencingComponents;
+
+    /**
+     * Group id for this component referencing a controller service. If this
+     * component is another service, this field is blank.
+     * 
+     * @return 
+     */
+    public String getGroupId() {
+        return groupId;
+    }
+
+    public void setGroupId(String groupId) {
+        this.groupId = groupId;
+    }
+
+    /**
+     * The id for this component referencing a controller service.
+     * 
+     * @return 
+     */
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    /**
+     * The name for this component referencing a controller service.
+     * 
+     * @return 
+     */
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * The type for this component referencing a controller service.
+     * 
+     * @return 
+     */
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    /**
+     * The state of the processor referencing a controller service. If this
+     * component is another service, this field is blank.
+     * 
+     * @return 
+     */
+    public String getState() {
+        return state;
+    }
+
+    public void setState(String state) {
+        this.state = state;
+    }
+
+    /**
+     * The type of reference this is (Processor, ControllerService, or ReportingTask).
+     * @return 
+     */
+    public String getReferenceType() {
+        return referenceType;
+    }
+
+    public void setReferenceType(String referenceType) {
+        this.referenceType = referenceType;
+    }
+
+    /**
+     * The component properties.
+     * 
+     * @return 
+     */
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    public void setProperties(Map<String, String> properties) {
+        this.properties = properties;
+    }
+
+    /**
+     * The descriptors for the components properties.
+     * 
+     * @return 
+     */
+    public Map<String, PropertyDescriptorDTO> getDescriptors() {
+        return descriptors;
+    }
+
+    public void setDescriptors(Map<String, PropertyDescriptorDTO> descriptors) {
+        this.descriptors = descriptors;
+    }
+    
+    /**
+     * Any validation error associated with this component.
+     * 
+     * @return 
+     */
+    public Collection<String> getValidationErrors() {
+        return validationErrors;
+    }
+
+    public void setValidationErrors(Collection<String> validationErrors) {
+        this.validationErrors = validationErrors;
+    }
+    
+    /**
+     * The active thread count for the referencing component.
+     * 
+     * @return 
+     */
+    public Integer getActiveThreadCount() {
+        return activeThreadCount;
+    }
+
+    public void setActiveThreadCount(Integer activeThreadCount) {
+        this.activeThreadCount = activeThreadCount;
+    }
+
+    /**
+     * If this referencing component represents a ControllerService, these
+     * are the components that reference it.
+     * 
+     * @return 
+     */
+    public Set<ControllerServiceReferencingComponentDTO> getReferencingComponents() {
+        return referencingComponents;
+    }
+
+    public void setReferencingComponents(Set<ControllerServiceReferencingComponentDTO> referencingComponents) {
+        this.referencingComponents = referencingComponents;
+    }
+
+    /**
+     * If this referencing component represents a ControllerService, this indicates
+     * whether it has already been represented in this hierarchy.
+     * 
+     * @return 
+     */
+    public Boolean getReferenceCycle() {
+        return referenceCycle;
+    }
+
+    public void setReferenceCycle(Boolean referenceCycle) {
+        this.referenceCycle = referenceCycle;
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DocumentedTypeDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DocumentedTypeDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DocumentedTypeDTO.java
index 5a2d789..7cf1b84 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DocumentedTypeDTO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DocumentedTypeDTO.java
@@ -20,13 +20,13 @@ import java.util.Set;
 import javax.xml.bind.annotation.XmlType;
 
 /**
- * Class used for providing documentation of a specified type that may be
- * instantiated.
+ * Class used for providing documentation of a specified type.
  */
 @XmlType(name = "documentedType")
 public class DocumentedTypeDTO {
 
     private String type;
+    private Set<DocumentedTypeDTO> childTypes;
     private String description;
     private Set<String> tags;
 
@@ -57,7 +57,7 @@ public class DocumentedTypeDTO {
     }
 
     /**
-     * The tags associated with this type
+     * The tags associated with this type.
      *
      * @return
      */
@@ -68,4 +68,18 @@ public class DocumentedTypeDTO {
     public void setTags(final Set<String> tags) {
         this.tags = tags;
     }
+
+    /**
+     * Child types for this type.
+     * 
+     * @return 
+     */
+    public Set<DocumentedTypeDTO> getChildTypes() {
+        return childTypes;
+    }
+
+    public void setChildTypes(Set<DocumentedTypeDTO> childTypes) {
+        this.childTypes = childTypes;
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/FlowSnippetDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/FlowSnippetDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/FlowSnippetDTO.java
index 61c3c33..47a6871 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/FlowSnippetDTO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/FlowSnippetDTO.java
@@ -34,7 +34,8 @@ public class FlowSnippetDTO {
     private Set<ConnectionDTO> connections = new LinkedHashSet<>();
     private Set<LabelDTO> labels = new LinkedHashSet<>();
     private Set<FunnelDTO> funnels = new LinkedHashSet<>();
-
+    private Set<ControllerServiceDTO> controllerServices = new LinkedHashSet<>();
+    
     /**
      * The connections in this flow snippet.
      *
@@ -138,4 +139,16 @@ public class FlowSnippetDTO {
     public void setRemoteProcessGroups(Set<RemoteProcessGroupDTO> remoteProcessGroups) {
         this.remoteProcessGroups = remoteProcessGroups;
     }
+
+    /**
+     * Returns the Controller Services in this flow snippet
+     * @return
+     */
+    public Set<ControllerServiceDTO> getControllerServices() {
+        return controllerServices;
+    }
+
+    public void setControllerServices(Set<ControllerServiceDTO> controllerServices) {
+        this.controllerServices = controllerServices;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/NiFiComponentDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/NiFiComponentDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/NiFiComponentDTO.java
index e3c8445..2829287 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/NiFiComponentDTO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/NiFiComponentDTO.java
@@ -55,7 +55,7 @@ public class NiFiComponentDTO {
     }
 
     /**
-     * The id for the parent group of this component.
+     * The id for the parent group of this component if applicable, null otherwise.
      *
      * @return
      */
@@ -85,7 +85,7 @@ public class NiFiComponentDTO {
     }
 
     /**
-     * The position of this component in the UI.
+     * The position of this component in the UI if applicable, null otherwise.
      *
      * @return The position
      */

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessorConfigDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessorConfigDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessorConfigDTO.java
index 1481b0f..63ed005 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessorConfigDTO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessorConfigDTO.java
@@ -54,10 +54,10 @@ public class ProcessorConfigDTO {
     }
 
     /**
-     * The amount of time that should elapse between task executions. This will
-     * not affect currently scheduled tasks.
+     * The frequency with which to schedule the processor. The format of the value will
+     * depend on the value of {@link #getSchedulingStrategy()}.
      *
-     * @return The scheduling period in seconds
+     * @return The scheduling period
      */
     public String getSchedulingPeriod() {
         return schedulingPeriod;
@@ -207,7 +207,8 @@ public class ProcessorConfigDTO {
     }
 
     /**
-     * Whether of not this processor has a custom UI.
+     * Returns the URL for this processors custom configuration UI
+     * if applicable. Null otherwise.
      *
      * @return
      */
@@ -274,213 +275,4 @@ public class ProcessorConfigDTO {
         this.defaultSchedulingPeriod = defaultSchedulingPeriod;
     }
 
-    /**
-     * The allowable values for a property with a constrained set of options.
-     */
-    @XmlType(name = "allowableValue")
-    public static class AllowableValueDTO {
-
-        private String displayName;
-        private String value;
-        private String description;
-
-        /**
-         * Returns the human-readable value that is allowed for this
-         * PropertyDescriptor
-         *
-         * @return
-         */
-        public String getDisplayName() {
-            return displayName;
-        }
-
-        public void setDisplayName(String displayName) {
-            this.displayName = displayName;
-        }
-
-        /**
-         * Returns the value for this allowable value.
-         *
-         * @return
-         */
-        public String getValue() {
-            return value;
-        }
-
-        public void setValue(String value) {
-            this.value = value;
-        }
-
-        /**
-         * Returns a description of this Allowable Value, or <code>null</code>
-         * if no description is given
-         *
-         * @return
-         */
-        public String getDescription() {
-            return description;
-        }
-
-        public void setDescription(String description) {
-            this.description = description;
-        }
-
-        @Override
-        public boolean equals(final Object obj) {
-            if (obj == this) {
-                return true;
-            }
-
-            if (!(obj instanceof AllowableValueDTO)) {
-                return false;
-            }
-
-            final AllowableValueDTO other = (AllowableValueDTO) obj;
-            return (this.value.equals(other.getValue()));
-        }
-
-        @Override
-        public int hashCode() {
-            return 23984731 + 17 * value.hashCode();
-        }
-    }
-
-    /**
-     * A description of a processor property.
-     */
-    @XmlType(name = "propertyDescriptor")
-    public static class PropertyDescriptorDTO {
-
-        private String name;
-        private String displayName;
-        private String description;
-        private String defaultValue;
-        private Set<AllowableValueDTO> allowableValues;
-        private boolean required;
-        private boolean sensitive;
-        private boolean dynamic;
-        private boolean supportsEl;
-
-        /**
-         * The set of allowable values for this property. If empty then the
-         * allowable values are not constrained.
-         *
-         * @return
-         */
-        public Set<AllowableValueDTO> getAllowableValues() {
-            return allowableValues;
-        }
-
-        public void setAllowableValues(Set<AllowableValueDTO> allowableValues) {
-            this.allowableValues = allowableValues;
-        }
-
-        /**
-         * The default value for this property.
-         *
-         * @return
-         */
-        public String getDefaultValue() {
-            return defaultValue;
-        }
-
-        public void setDefaultValue(String defaultValue) {
-            this.defaultValue = defaultValue;
-        }
-
-        /**
-         * And explanation of the meaning of the given property. This
-         * description is meant to be displayed to a user or simply provide a
-         * mechanism of documenting intent.
-         *
-         * @return
-         */
-        public String getDescription() {
-            return description;
-        }
-
-        public void setDescription(String description) {
-            this.description = description;
-        }
-
-        /**
-         * The property name.
-         *
-         * @return
-         */
-        public String getName() {
-            return name;
-        }
-
-        public void setName(String name) {
-            this.name = name;
-        }
-
-        /**
-         * The human-readable name to display to users.
-         *
-         * @return
-         */
-        public String getDisplayName() {
-            return displayName;
-        }
-
-        public void setDisplayName(String displayName) {
-            this.displayName = displayName;
-        }
-
-        /**
-         * Determines whether the property is required for this processor.
-         *
-         * @return
-         */
-        public boolean isRequired() {
-            return required;
-        }
-
-        public void setRequired(boolean required) {
-            this.required = required;
-        }
-
-        /**
-         * Indicates that the value for this property should be considered
-         * sensitive and protected whenever stored or represented.
-         *
-         * @return
-         */
-        public boolean isSensitive() {
-            return sensitive;
-        }
-
-        public void setSensitive(boolean sensitive) {
-            this.sensitive = sensitive;
-        }
-
-        /**
-         * Indicates whether this property is dynamic.
-         *
-         * @return
-         */
-        public boolean isDynamic() {
-            return dynamic;
-        }
-
-        public void setDynamic(boolean dynamic) {
-            this.dynamic = dynamic;
-        }
-
-        /**
-         * Specifies whether or not this property support expression language.
-         *
-         * @return
-         */
-        public boolean getSupportsEl() {
-            return supportsEl;
-        }
-
-        public void setSupportsEl(boolean supportsEl) {
-            this.supportsEl = supportsEl;
-        }
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessorHistoryDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessorHistoryDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessorHistoryDTO.java
deleted file mode 100644
index 2741116..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessorHistoryDTO.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.web.api.dto;
-
-import java.util.Map;
-import javax.xml.bind.annotation.XmlType;
-
-/**
- * History of a processor's properties.
- */
-@XmlType(name = "processorHistory")
-public class ProcessorHistoryDTO {
-
-    private String processorId;
-    private Map<String, PropertyHistoryDTO> propertyHistory;
-
-    /**
-     * The processor id.
-     *
-     * @return
-     */
-    public String getProcessorId() {
-        return processorId;
-    }
-
-    public void setProcessorId(String processorId) {
-        this.processorId = processorId;
-    }
-
-    /**
-     * The history for this processors properties.
-     *
-     * @return
-     */
-    public Map<String, PropertyHistoryDTO> getPropertyHistory() {
-        return propertyHistory;
-    }
-
-    public void setPropertyHistory(Map<String, PropertyHistoryDTO> propertyHistory) {
-        this.propertyHistory = propertyHistory;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/PropertyDescriptorDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/PropertyDescriptorDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/PropertyDescriptorDTO.java
new file mode 100644
index 0000000..d10a324
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/PropertyDescriptorDTO.java
@@ -0,0 +1,243 @@
+/*
+ * 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.web.api.dto;
+
+import java.util.List;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * A description of a property.
+ */
+@XmlType(name = "propertyDescriptor")
+public class PropertyDescriptorDTO {
+
+    private String name;
+    private String displayName;
+    private String description;
+    private String defaultValue;
+    private List<AllowableValueDTO> allowableValues;
+    private boolean required;
+    private boolean sensitive;
+    private boolean dynamic;
+    private boolean supportsEl;
+    private boolean identifiesControllerService;
+
+    /**
+     * The set of allowable values for this property. If empty then the
+     * allowable values are not constrained.
+     *
+     * @return
+     */
+    public List<AllowableValueDTO> getAllowableValues() {
+        return allowableValues;
+    }
+
+    public void setAllowableValues(List<AllowableValueDTO> allowableValues) {
+        this.allowableValues = allowableValues;
+    }
+
+    /**
+     * The default value for this property.
+     *
+     * @return
+     */
+    public String getDefaultValue() {
+        return defaultValue;
+    }
+
+    public void setDefaultValue(String defaultValue) {
+        this.defaultValue = defaultValue;
+    }
+
+    /**
+     * And explanation of the meaning of the given property. This
+     * description is meant to be displayed to a user or simply provide a
+     * mechanism of documenting intent.
+     *
+     * @return
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * The property name.
+     *
+     * @return
+     */
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * The human-readable name to display to users.
+     *
+     * @return
+     */
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+
+    /**
+     * Determines whether the property is required for this processor.
+     *
+     * @return
+     */
+    public boolean isRequired() {
+        return required;
+    }
+
+    public void setRequired(boolean required) {
+        this.required = required;
+    }
+
+    /**
+     * Indicates that the value for this property should be considered
+     * sensitive and protected whenever stored or represented.
+     *
+     * @return
+     */
+    public boolean isSensitive() {
+        return sensitive;
+    }
+
+    public void setSensitive(boolean sensitive) {
+        this.sensitive = sensitive;
+    }
+
+    /**
+     * Indicates whether this property is dynamic.
+     *
+     * @return
+     */
+    public boolean isDynamic() {
+        return dynamic;
+    }
+
+    public void setDynamic(boolean dynamic) {
+        this.dynamic = dynamic;
+    }
+
+    /**
+     * Specifies whether or not this property support expression language.
+     *
+     * @return
+     */
+    public boolean getSupportsEl() {
+        return supportsEl;
+    }
+
+    public void setSupportsEl(boolean supportsEl) {
+        this.supportsEl = supportsEl;
+    }
+
+    /**
+     * Whether this descriptor represents a controller service.
+     * 
+     * @return 
+     */
+    public boolean isIdentifiesControllerService() {
+        return identifiesControllerService;
+    }
+
+    public void setIdentifiesControllerService(boolean identifiesControllerService) {
+        this.identifiesControllerService = identifiesControllerService;
+    }
+    
+    /**
+     * The allowable values for a property with a constrained set of options.
+     */
+    @XmlType(name = "allowableValue")
+    public static class AllowableValueDTO {
+
+        private String displayName;
+        private String value;
+        private String description;
+
+        /**
+         * Returns the human-readable value that is allowed for this
+         * PropertyDescriptor
+         *
+         * @return
+         */
+        public String getDisplayName() {
+            return displayName;
+        }
+
+        public void setDisplayName(String displayName) {
+            this.displayName = displayName;
+        }
+
+        /**
+         * Returns the value for this allowable value.
+         *
+         * @return
+         */
+        public String getValue() {
+            return value;
+        }
+
+        public void setValue(String value) {
+            this.value = value;
+        }
+
+        /**
+         * Returns a description of this Allowable Value, or <code>null</code>
+         * if no description is given
+         *
+         * @return
+         */
+        public String getDescription() {
+            return description;
+        }
+
+        public void setDescription(String description) {
+            this.description = description;
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            if (obj == this) {
+                return true;
+            }
+
+            if (!(obj instanceof AllowableValueDTO)) {
+                return false;
+            }
+
+            final AllowableValueDTO other = (AllowableValueDTO) obj;
+            return (this.value.equals(other.getValue()));
+        }
+
+        @Override
+        public int hashCode() {
+            return 23984731 + 17 * value.hashCode();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ReportingTaskDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ReportingTaskDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ReportingTaskDTO.java
new file mode 100644
index 0000000..a019f97
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ReportingTaskDTO.java
@@ -0,0 +1,228 @@
+/*
+ * 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.web.api.dto;
+
+import java.util.Collection;
+import java.util.Map;
+
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * Component that is capable of reporting internal NiFi state to an external service
+ */
+@XmlType(name = "reportingTask")
+public class ReportingTaskDTO extends NiFiComponentDTO {
+	private String name;
+	private String type;
+	private String state;
+	private String availability;
+    private String comments;
+	
+	private String schedulingPeriod;
+	private String schedulingStrategy;
+    private Map<String, String> defaultSchedulingPeriod;
+    
+	private Map<String, String> properties;
+    private Map<String, PropertyDescriptorDTO> descriptors;
+ 
+    private String customUiUrl;
+    private String annotationData;
+    
+    private Collection<String> validationErrors;
+    private Integer activeThreadCount;
+    
+    /**
+     * The user-defined name of the reporting task
+     * @return
+     */
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	/**
+	 * The user-defined comments for the reporting task
+	 * @return
+	 */
+	public String getComments() {
+		return comments;
+	}
+
+	public void setComments(String comments) {
+		this.comments = comments;
+	}
+
+	/**
+	 * The type of reporting task
+	 * @return
+	 */
+	public String getType() {
+		return type;
+	}
+
+	public void setType(String type) {
+		this.type = type;
+	}
+
+	/**
+     * The frequency with which to schedule the reporting task. The format of the value will
+     * depend on the value of {@link #getSchedulingStrategy()}.
+     *
+     * @return The scheduling period
+     */
+	public String getSchedulingPeriod() {
+		return schedulingPeriod;
+	}
+
+	public void setSchedulingPeriod(String schedulingPeriod) {
+		this.schedulingPeriod = schedulingPeriod;
+	}
+
+	/**
+	 * The current scheduling state of the reporting task
+	 * @return
+	 */
+	public String getState() {
+		return state;
+	}
+
+	public void setState(String state) {
+		this.state = state;
+	}
+
+	/**
+	 * The scheduling strategy that determines how the {@link #getSchedulingPeriod()} value should
+	 * be interpreted
+	 * 
+	 * @return
+	 */
+	public String getSchedulingStrategy() {
+		return schedulingStrategy;
+	}
+
+	public void setSchedulingStrategy(String schedulingStrategy) {
+		this.schedulingStrategy = schedulingStrategy;
+	}
+
+	/**
+     * Where this service is available. Possible values are CLUSTER_MANAGER_ONLY, NODE_ONLY, BOTH.
+     * 
+     * @return 
+     */
+	public String getAvailability() {
+		return availability;
+	}
+
+	public void setAvailability(String availability) {
+		this.availability = availability;
+	}
+
+	/**
+	 * The reporting task's properties
+	 * @return
+	 */
+	public Map<String, String> getProperties() {
+		return properties;
+	}
+
+	public void setProperties(Map<String, String> properties) {
+		this.properties = properties;
+	}
+
+	/**
+	 * Map of property name to descriptor
+	 * @return
+	 */
+	public Map<String, PropertyDescriptorDTO> getDescriptors() {
+		return descriptors;
+	}
+
+	public void setDescriptors(Map<String, PropertyDescriptorDTO> descriptors) {
+		this.descriptors = descriptors;
+	}
+
+    /**
+     * Returns the URL for this reporting task custom configuration UI
+     * if applicable. Null otherwise.
+     *
+     * @return
+     */
+    public String getCustomUiUrl() {
+        return customUiUrl;
+    }
+
+    public void setCustomUiUrl(String customUiUrl) {
+        this.customUiUrl = customUiUrl;
+    }
+
+	/**
+	 * The currently configured annotation data for the reporting task
+	 * @return
+	 */
+	public String getAnnotationData() {
+		return annotationData;
+	}
+
+	public void setAnnotationData(String annotationData) {
+		this.annotationData = annotationData;
+	}
+	
+    /**
+     * Gets the validation errors from this reporting task. These validation errors
+     * represent the problems with the reporting task that must be resolved before it
+     * can be scheduled to run.
+     *
+     * @return The validation errors
+     */
+    public Collection<String> getValidationErrors() {
+        return validationErrors;
+    }
+
+    public void setValidationErrors(Collection<String> validationErrors) {
+        this.validationErrors = validationErrors;
+    }
+
+    /**
+     * The default scheduling period for the different scheduling strategies.
+     * 
+     * @return 
+     */
+    public Map<String, String> getDefaultSchedulingPeriod() {
+        return defaultSchedulingPeriod;
+    }
+
+    public void setDefaultSchedulingPeriod(Map<String, String> defaultSchedulingPeriod) {
+        this.defaultSchedulingPeriod = defaultSchedulingPeriod;
+    }
+
+    /**
+     * The number of active threads for this reporting task.
+     * 
+     * @return 
+     */
+    public Integer getActiveThreadCount() {
+        return activeThreadCount;
+    }
+
+    public void setActiveThreadCount(Integer activeThreadCount) {
+        this.activeThreadCount = activeThreadCount;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/RevisionDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/RevisionDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/RevisionDTO.java
index e608a7e..3327b49 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/RevisionDTO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/RevisionDTO.java
@@ -26,8 +26,10 @@ public class RevisionDTO {
 
     private String clientId;
     private Long version;
+    private String lastModifier;
 
     /* getters / setters */
+    
     /**
      * A client identifier used to make a request. By including a client
      * identifier, the API can allow multiple requests without needing the
@@ -60,4 +62,17 @@ public class RevisionDTO {
         this.version = version;
     }
 
+    /**
+     * The user that last modified the flow.
+     * 
+     * @return 
+     */
+    public String getLastModifier() {
+        return lastModifier;
+    }
+
+    public void setLastModifier(String lastModifier) {
+        this.lastModifier = lastModifier;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/component/details/ComponentDetailsDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/component/details/ComponentDetailsDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/component/details/ComponentDetailsDTO.java
index 58086ce..b01a271 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/component/details/ComponentDetailsDTO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/component/details/ComponentDetailsDTO.java
@@ -24,7 +24,7 @@ import javax.xml.bind.annotation.XmlType;
  */
 @XmlType(name = "componentDetails")
 @XmlSeeAlso({
-    ProcessorDetailsDTO.class,
+    ExtensionDetailsDTO.class,
     RemoteProcessGroupDetailsDTO.class
 })
 public class ComponentDetailsDTO {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/component/details/ExtensionDetailsDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/component/details/ExtensionDetailsDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/component/details/ExtensionDetailsDTO.java
new file mode 100644
index 0000000..e2e49d6
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/component/details/ExtensionDetailsDTO.java
@@ -0,0 +1,41 @@
+/*
+ * 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.web.api.dto.action.component.details;
+
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * Extension details for an action.
+ */
+@XmlType(name = "extensionDetails")
+public class ExtensionDetailsDTO extends ComponentDetailsDTO {
+
+    private String type;
+
+    /**
+     * The extension type.
+     *
+     * @return
+     */
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/component/details/ProcessorDetailsDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/component/details/ProcessorDetailsDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/component/details/ProcessorDetailsDTO.java
deleted file mode 100644
index 3523f62..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/action/component/details/ProcessorDetailsDTO.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.web.api.dto.action.component.details;
-
-import javax.xml.bind.annotation.XmlType;
-
-/**
- * Processor details for an action.
- */
-@XmlType(name = "processorDetails")
-public class ProcessorDetailsDTO extends ComponentDetailsDTO {
-
-    private String type;
-
-    /**
-     * The processors type.
-     *
-     * @return
-     */
-    public String getType() {
-        return type;
-    }
-
-    public void setType(String type) {
-        this.type = type;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ComponentHistoryEntity.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ComponentHistoryEntity.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ComponentHistoryEntity.java
new file mode 100644
index 0000000..ca68211
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ComponentHistoryEntity.java
@@ -0,0 +1,45 @@
+/*
+ * 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.web.api.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.nifi.web.api.dto.ComponentHistoryDTO;
+
+/**
+ * A serialized representation of this class can be placed in the entity body of
+ * a request or response to or from the API. This particular entity holds a
+ * reference to a ComponentHistoryDTO.
+ */
+@XmlRootElement(name = "componentHistoryEntity")
+public class ComponentHistoryEntity extends Entity {
+
+    private ComponentHistoryDTO componentHistory;
+
+    /**
+     * The ComponentHistoryDTO that is being serialized.
+     *
+     * @return The ComponentHistoryDTO object
+     */
+    public ComponentHistoryDTO getComponentHistory() {
+        return componentHistory;
+    }
+
+    public void setComponentHistory(ComponentHistoryDTO componentHistory) {
+        this.componentHistory = componentHistory;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerServiceEntity.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerServiceEntity.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerServiceEntity.java
new file mode 100644
index 0000000..44364e7
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerServiceEntity.java
@@ -0,0 +1,45 @@
+/*
+ * 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.web.api.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
+
+/**
+ * A serialized representation of this class can be placed in the entity body of
+ * a response to the API. This particular entity holds a reference to a 
+ * controller service.
+ */
+@XmlRootElement(name = "controllerServiceEntity")
+public class ControllerServiceEntity extends Entity {
+
+    private ControllerServiceDTO controllerService;
+
+    /**
+     * The controller service that is being serialized.
+     *
+     * @return
+     */
+    public ControllerServiceDTO getControllerService() {
+        return controllerService;
+    }
+
+    public void setControllerService(ControllerServiceDTO controllerService) {
+        this.controllerService = controllerService;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerServiceReferencingComponentsEntity.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerServiceReferencingComponentsEntity.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerServiceReferencingComponentsEntity.java
new file mode 100644
index 0000000..6010f93
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerServiceReferencingComponentsEntity.java
@@ -0,0 +1,46 @@
+/*
+ * 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.web.api.entity;
+
+import java.util.Set;
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.nifi.web.api.dto.ControllerServiceReferencingComponentDTO;
+
+/**
+ * A serialized representation of this class can be placed in the entity body of
+ * a response to the API. This particular entity holds a reference to a list of
+ * controller services referencing components.
+ */
+@XmlRootElement(name = "controllerServiceReferencingComponentsEntity")
+public class ControllerServiceReferencingComponentsEntity extends Entity {
+
+    private Set<ControllerServiceReferencingComponentDTO> controllerServiceReferencingComponents;
+
+    /**
+     * The list of controller service referencing components that are being serialized.
+     *
+     * @return
+     */
+    public Set<ControllerServiceReferencingComponentDTO> getControllerServiceReferencingComponents() {
+        return controllerServiceReferencingComponents;
+    }
+
+    public void setControllerServiceReferencingComponents(Set<ControllerServiceReferencingComponentDTO> controllerServiceReferencingComponents) {
+        this.controllerServiceReferencingComponents = controllerServiceReferencingComponents;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerServiceTypesEntity.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerServiceTypesEntity.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerServiceTypesEntity.java
new file mode 100644
index 0000000..dafb8c2
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerServiceTypesEntity.java
@@ -0,0 +1,46 @@
+/*
+ * 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.web.api.entity;
+
+import java.util.Set;
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.nifi.web.api.dto.DocumentedTypeDTO;
+
+/**
+ * A serialized representation of this class can be placed in the entity body of
+ * a response to the API. This particular entity holds a reference to a list of
+ * controller service types.
+ */
+@XmlRootElement(name = "controllerServiceTypesEntity")
+public class ControllerServiceTypesEntity extends Entity {
+
+    private Set<DocumentedTypeDTO> controllerServiceTypes;
+
+    /**
+     * The list of controller service types that are being serialized.
+     *
+     * @return
+     */
+    public Set<DocumentedTypeDTO> getControllerServiceTypes() {
+        return controllerServiceTypes;
+    }
+
+    public void setControllerServiceTypes(Set<DocumentedTypeDTO> controllerServiceTypes) {
+        this.controllerServiceTypes = controllerServiceTypes;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerServicesEntity.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerServicesEntity.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerServicesEntity.java
new file mode 100644
index 0000000..4485b43
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ControllerServicesEntity.java
@@ -0,0 +1,46 @@
+/*
+ * 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.web.api.entity;
+
+import java.util.Set;
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
+
+/**
+ * A serialized representation of this class can be placed in the entity body of
+ * a response to the API. This particular entity holds a reference to a list of
+ * controller services.
+ */
+@XmlRootElement(name = "controllerServicesEntity")
+public class ControllerServicesEntity extends Entity {
+
+    private Set<ControllerServiceDTO> controllerServices;
+
+    /**
+     * The list of controller services that are being serialized.
+     *
+     * @return
+     */
+    public Set<ControllerServiceDTO> getControllerServices() {
+        return controllerServices;
+    }
+
+    public void setControllerServices(Set<ControllerServiceDTO> controllerServices) {
+        this.controllerServices = controllerServices;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ProcessorHistoryEntity.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ProcessorHistoryEntity.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ProcessorHistoryEntity.java
deleted file mode 100644
index 19166f7..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ProcessorHistoryEntity.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.web.api.entity;
-
-import javax.xml.bind.annotation.XmlRootElement;
-import org.apache.nifi.web.api.dto.ProcessorHistoryDTO;
-
-/**
- * A serialized representation of this class can be placed in the entity body of
- * a request or response to or from the API. This particular entity holds a
- * reference to a ProcessorHistoryDTO.
- */
-@XmlRootElement(name = "processorHistoryEntity")
-public class ProcessorHistoryEntity extends Entity {
-
-    private ProcessorHistoryDTO propertyHistory;
-
-    /**
-     * The ProcessorHistoryDTO that is being serialized.
-     *
-     * @return The ProcessorHistoryDTO object
-     */
-    public ProcessorHistoryDTO getProcessorHistory() {
-        return propertyHistory;
-    }
-
-    public void setProcessorHistory(ProcessorHistoryDTO propertyHistory) {
-        this.propertyHistory = propertyHistory;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/PropertyDescriptorEntity.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/PropertyDescriptorEntity.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/PropertyDescriptorEntity.java
new file mode 100644
index 0000000..9251b7f
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/PropertyDescriptorEntity.java
@@ -0,0 +1,46 @@
+/*
+ * 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.web.api.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.nifi.web.api.dto.ProcessorDTO;
+import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
+
+/**
+ * A serialized representation of this class can be placed in the entity body of
+ * a request or response to or from the API. This particular entity holds a
+ * reference to a PropertyDescriptorDTO.
+ */
+@XmlRootElement(name = "propertyDescriptor")
+public class PropertyDescriptorEntity extends Entity {
+
+    private PropertyDescriptorDTO propertyDescriptor;
+
+    /**
+     * The PropertyDescriptorDTO that is being serialized.
+     *
+     * @return The PropertyDescriptorDTO object
+     */
+    public PropertyDescriptorDTO getPropertyDescriptor() {
+        return propertyDescriptor;
+    }
+
+    public void setPropertyDescriptor(PropertyDescriptorDTO propertyDescriptor) {
+        this.propertyDescriptor = propertyDescriptor;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ReportingTaskEntity.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ReportingTaskEntity.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ReportingTaskEntity.java
new file mode 100644
index 0000000..a372751
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ReportingTaskEntity.java
@@ -0,0 +1,45 @@
+/*
+ * 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.web.api.entity;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.nifi.web.api.dto.ReportingTaskDTO;
+
+/**
+ * A serialized representation of this class can be placed in the entity body of
+ * a response to the API. This particular entity holds a reference to a 
+ * reporting task.
+ */
+@XmlRootElement(name = "reportingTaskEntity")
+public class ReportingTaskEntity extends Entity {
+
+    private ReportingTaskDTO reportingTask;
+
+    /**
+     * The reporting task that is being serialized.
+     *
+     * @return
+     */
+    public ReportingTaskDTO getReportingTask() {
+        return reportingTask;
+    }
+
+    public void setReportingTask(ReportingTaskDTO reportingTask) {
+        this.reportingTask = reportingTask;
+    }
+
+}


[60/62] [abbrv] incubator-nifi git commit: NIFI-503: Removed dependencies on commons-lang3 and commons-io

Posted by ma...@apache.org.
NIFI-503: Removed dependencies on commons-lang3 and commons-io


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/e18c0a7d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/e18c0a7d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/e18c0a7d

Branch: refs/heads/NIFI-25
Commit: e18c0a7d267460293a7e5a0189b223607212354f
Parents: 512ac9c
Author: Mark Payne <ma...@hotmail.com>
Authored: Fri Apr 10 09:37:53 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Fri Apr 10 09:37:53 2015 -0400

----------------------------------------------------------------------
 .../nifi-framework/nifi-documentation/pom.xml       |  8 --------
 .../documentation/html/HtmlDocumentationWriter.java | 15 ++++++++++++---
 .../html/HtmlProcessorDocumentationWriter.java      | 16 +++++++++++-----
 .../html/HtmlDocumentationWriterTest.java           |  8 ++++++++
 4 files changed, 31 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e18c0a7d/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml
index e522d30..5995f5c 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml
@@ -33,14 +33,6 @@
             <artifactId>nifi-properties</artifactId>
         </dependency>
         <dependency>
-            <groupId>commons-io</groupId>
-            <artifactId>commons-io</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-lang3</artifactId>
-        </dependency>
-        <dependency>
             <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-processor-utils</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e18c0a7d/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
index 34b1327..243aaa3 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
@@ -26,7 +26,6 @@ import javax.xml.stream.XMLOutputFactory;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
 
-import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.annotation.behavior.DynamicProperties;
 import org.apache.nifi.annotation.behavior.DynamicProperty;
 import org.apache.nifi.annotation.documentation.CapabilityDescription;
@@ -212,13 +211,23 @@ public class HtmlDocumentationWriter implements DocumentationWriter {
         xmlStreamWriter.writeEndElement();
         xmlStreamWriter.writeStartElement("p");
         if (tags != null) {
-            final String tagString = StringUtils.join(tags.value(), ", ");
+            final String tagString = join(tags.value(), ", ");
             xmlStreamWriter.writeCharacters(tagString);
         } else {
             xmlStreamWriter.writeCharacters("None.");
         }
         xmlStreamWriter.writeEndElement();
-
+    }
+    
+    static String join(final String[] toJoin, final String delimiter) {
+    	final StringBuilder sb = new StringBuilder();
+    	for (int i=0; i < toJoin.length; i++) {
+    		sb.append(toJoin[i]);
+    		if ( i < toJoin.length - 1 ) {
+    			sb.append(delimiter);
+    		}
+    	}
+    	return sb.toString();
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e18c0a7d/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java
index bc355f7..c253a4d 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java
@@ -23,7 +23,6 @@ import java.util.List;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
 
-import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.annotation.behavior.DynamicRelationship;
 import org.apache.nifi.annotation.behavior.ReadsAttribute;
 import org.apache.nifi.annotation.behavior.ReadsAttributes;
@@ -67,6 +66,13 @@ public class HtmlProcessorDocumentationWriter extends HtmlDocumentationWriter {
         handleWritesAttributes(xmlStreamWriter, processor);
     }
 
+    private String defaultIfBlank(final String test, final String defaultValue) {
+    	if ( test == null || test.trim().isEmpty() ) {
+    		return defaultValue;
+    	}
+    	return test;
+    }
+    
     /**
      * Writes out just the attributes that are being read in a table form.
      * 
@@ -91,10 +97,10 @@ public class HtmlProcessorDocumentationWriter extends HtmlDocumentationWriter {
             for (ReadsAttribute attribute : attributesRead) {
                 xmlStreamWriter.writeStartElement("tr");
                 writeSimpleElement(xmlStreamWriter, "td",
-                        StringUtils.defaultIfBlank(attribute.attribute(), "Not Specified"));
+                        defaultIfBlank(attribute.attribute(), "Not Specified"));
                 // TODO allow for HTML characters here.
                 writeSimpleElement(xmlStreamWriter, "td",
-                        StringUtils.defaultIfBlank(attribute.description(), "Not Specified"));
+                        defaultIfBlank(attribute.description(), "Not Specified"));
                 xmlStreamWriter.writeEndElement();
                 
             }
@@ -129,10 +135,10 @@ public class HtmlProcessorDocumentationWriter extends HtmlDocumentationWriter {
             for (WritesAttribute attribute : attributesRead) {
                 xmlStreamWriter.writeStartElement("tr");
                 writeSimpleElement(xmlStreamWriter, "td",
-                        StringUtils.defaultIfBlank(attribute.attribute(), "Not Specified"));
+                        defaultIfBlank(attribute.attribute(), "Not Specified"));
                 // TODO allow for HTML characters here.
                 writeSimpleElement(xmlStreamWriter, "td",
-                        StringUtils.defaultIfBlank(attribute.description(), "Not Specified"));
+                        defaultIfBlank(attribute.description(), "Not Specified"));
                 xmlStreamWriter.writeEndElement();
             }
             xmlStreamWriter.writeEndElement();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e18c0a7d/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/HtmlDocumentationWriterTest.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/HtmlDocumentationWriterTest.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/HtmlDocumentationWriterTest.java
index 9d7926e..90ff09f 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/HtmlDocumentationWriterTest.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/HtmlDocumentationWriterTest.java
@@ -30,10 +30,18 @@ import org.apache.nifi.reporting.ReportingTask;
 import org.junit.Test;
 
 import static org.apache.nifi.documentation.html.XmlValidator.assertContains;
+import static org.junit.Assert.assertEquals;
 
 public class HtmlDocumentationWriterTest {
 
 	@Test
+	public void testJoin() {
+		assertEquals("a, b, c", HtmlDocumentationWriter.join(new String[] {"a",  "b", "c"}, ", "));
+		assertEquals("a, b", HtmlDocumentationWriter.join(new String[] {"a", "b"}, ", "));
+		assertEquals("a", HtmlDocumentationWriter.join(new String[] {"a"}, ", "));
+	}
+	
+	@Test
 	public void testDocumentControllerService() throws InitializationException, IOException {
 
 		ControllerService controllerService = new FullyDocumentedControllerService();


[04/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-text-editor.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-text-editor.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-text-editor.js
deleted file mode 100644
index 8b65b3f..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-text-editor.js
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * 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.
- */
-nf.ProcessorPropertyTextEditor = function (args) {
-    var scope = this;
-    var initialValue = '';
-    var previousValue;
-    var propertyDescriptor;
-    var wrapper;
-    var isEmpty;
-    var input;
-
-    this.init = function () {
-        var container = $('body');
-
-        // get the property descriptor for this property
-        var details = $('#processor-configuration').data('processorDetails');
-        propertyDescriptor = details.config.descriptors[args.item.property];
-
-        // record the previous value
-        previousValue = args.item[args.column.field];
-
-        // create the wrapper
-        wrapper = $('<div></div>').css({
-            'z-index': 100000,
-            'position': 'absolute',
-            'background': 'white',
-            'padding': '5px',
-            'overflow': 'hidden',
-            'border': '3px solid #365C6A',
-            'box-shadow': '4px 4px 6px rgba(0, 0, 0, 0.9)',
-            'cursor': 'move'
-        }).draggable({
-            cancel: '.button, textarea, .nf-checkbox',
-            containment: 'parent'
-        }).appendTo(container);
-
-        // create the input field
-        input = $('<textarea hidefocus rows="5"/>').css({
-            'background': 'white',
-            'width': args.position.width + 'px',
-            'min-width': '150px',
-            'height': '80px',
-            'border-width': '0',
-            'outline': '0',
-            'overflow-y': 'auto',
-            'resize': 'both',
-            'margin-bottom': '28px'
-        }).tab().on('keydown', scope.handleKeyDown).appendTo(wrapper);
-
-        // create the button panel
-        var stringCheckPanel = $('<div class="string-check-container">');
-
-        // build the custom checkbox
-        isEmpty = $('<div class="nf-checkbox string-check"/>').appendTo(stringCheckPanel);
-        $('<span class="string-check-label">&nbsp;Empty</span>').appendTo(stringCheckPanel);
-
-        var ok = $('<div class="button button-normal">Ok</div>').on('click', scope.save);
-        var cancel = $('<div class="button button-normal">Cancel</div>').on('click', scope.cancel);
-        $('<div></div>').css({
-            'position': 'absolute',
-            'bottom': '0',
-            'left': '0',
-            'right': '0',
-            'padding': '0 3px 5px'
-        }).append(stringCheckPanel).append(ok).append(cancel).append('<div class="clear"></div>').appendTo(wrapper);
-
-        // position and focus
-        scope.position(args.position);
-        input.focus().select();
-    };
-
-    this.handleKeyDown = function (e) {
-        if (e.which === $.ui.keyCode.ENTER && !e.shiftKey) {
-            scope.save();
-        } else if (e.which === $.ui.keyCode.ESCAPE) {
-            e.preventDefault();
-            scope.cancel();
-        }
-    };
-
-    this.save = function () {
-        args.commitChanges();
-    };
-
-    this.cancel = function () {
-        input.val(initialValue);
-        args.cancelChanges();
-    };
-
-    this.hide = function () {
-        wrapper.hide();
-    };
-
-    this.show = function () {
-        wrapper.show();
-    };
-
-    this.position = function (position) {
-        wrapper.css({
-            'top': position.top - 5,
-            'left': position.left - 5
-        });
-    };
-
-    this.destroy = function () {
-        wrapper.remove();
-    };
-
-    this.focus = function () {
-        input.focus();
-    };
-
-    this.loadValue = function (item) {
-        // determine if this is a sensitive property
-        var isEmptyChecked = false;
-        var sensitive = nf.ProcessorPropertyTable.isSensitiveProperty(propertyDescriptor);
-
-        // determine the value to use when populating the text field
-        if (nf.Common.isDefinedAndNotNull(item[args.column.field])) {
-            if (sensitive) {
-                initialValue = nf.ProcessorPropertyTable.config.sensitiveText;
-            } else {
-                initialValue = item[args.column.field];
-                isEmptyChecked = initialValue === '';
-            }
-        }
-
-        // determine if its an empty string
-        var checkboxStyle = isEmptyChecked ? 'checkbox-checked' : 'checkbox-unchecked';
-        isEmpty.addClass(checkboxStyle);
-
-        // style sensitive properties differently
-        if (sensitive) {
-            input.addClass('sensitive').keydown(function () {
-                var sensitiveInput = $(this);
-                if (sensitiveInput.hasClass('sensitive')) {
-                    sensitiveInput.removeClass('sensitive');
-                    if (sensitiveInput.val() === nf.ProcessorPropertyTable.config.sensitiveText) {
-                        sensitiveInput.val('');
-                    }
-                }
-            });
-        }
-
-        input.val(initialValue);
-        input.select();
-    };
-
-    this.serializeValue = function () {
-        // if the field has been cleared, set the value accordingly
-        if (input.val() === '') {
-            // if the user has checked the empty string checkbox, use emtpy string
-            if (isEmpty.hasClass('checkbox-checked')) {
-                return '';
-            } else {
-                // otherwise if the property is required
-                if (nf.ProcessorPropertyTable.isRequiredProperty(propertyDescriptor)) {
-                    if (nf.Common.isBlank(propertyDescriptor.defaultValue)) {
-                        return previousValue;
-                    } else {
-                        return propertyDescriptor.defaultValue;
-                    }
-                } else {
-                    // if the property is not required, clear the value
-                    return null;
-                }
-            }
-        } else {
-            // if the field still has the sensitive class it means a property
-            // was edited but never modified so we should restore the previous
-            // value instead of setting it to the 'sensitive value set' string
-            if (input.hasClass('sensitive')) {
-                return previousValue;
-            } else {
-                // if there is text specified, use that value
-                return input.val();
-            }
-        }
-    };
-
-    this.applyValue = function (item, state) {
-        item[args.column.field] = state;
-    };
-
-    this.isValueChanged = function () {
-        return scope.serializeValue() !== previousValue;
-    };
-
-    this.validate = function () {
-        return {
-            valid: true,
-            msg: null
-        };
-    };
-
-    // initialize the custom long text editor
-    this.init();
-};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js
index be6ff1b..4b4a1ef 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.Processor = (function () {
 
     var PREVIEW_NAME_LENGTH = 25;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-registration.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-registration.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-registration.js
index ec64677..b678e27 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-registration.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-registration.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 nf.Registration = (function () {
 
     var config = {
@@ -35,9 +38,9 @@ nf.Registration = (function () {
             $('#expand-registration-button, #expand-registration-text').click(function () {
                 var registrationForm = $('#registration-form');
                 if (registrationForm.is(':visible')) {
-                    $('#expand-registration-button').removeClass('registration-expanded').addClass('registration-collapsed');
+                    $('#expand-registration-button').removeClass('registration-expanded').addClass('collapsed');
                 } else {
-                    $('#expand-registration-button').removeClass('registration-collapsed').addClass('registration-expanded');
+                    $('#expand-registration-button').removeClass('registration-collapsed').addClass('expanded');
                 }
                 registrationForm.toggle();
             });

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-configuration.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-configuration.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-configuration.js
index df451fb..d0d5e3c 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-configuration.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-configuration.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.RemoteProcessGroupConfiguration = (function () {
     return {
         init: function () {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-details.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-details.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-details.js
index c75c244..c572d29 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-details.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-details.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 nf.RemoteProcessGroupDetails = (function () {
     return {
         init: function () {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-ports.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-ports.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-ports.js
index d80e4ea..db41bf1 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-ports.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group-ports.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.RemoteProcessGroupPorts = (function () {
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js
index 6c4a302..8bbd772 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.RemoteProcessGroup = (function () {
 
     var PREVIEW_NAME_LENGTH = 30;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-reporting-task.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-reporting-task.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-reporting-task.js
new file mode 100644
index 0000000..7c95388
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-reporting-task.js
@@ -0,0 +1,700 @@
+/*
+ * 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.
+ */
+
+/* global nf */
+
+nf.ReportingTask = (function () {
+    
+    var config = {
+        edit: 'edit',
+        readOnly: 'read-only'
+    };
+
+    /**
+     * Handle any expected reporting task configuration errors.
+     * 
+     * @argument {object} xhr       The XmlHttpRequest
+     * @argument {string} status    The status of the request
+     * @argument {string} error     The error
+     */
+    var handleReportingTaskConfigurationError = function (xhr, status, error) {
+        if (xhr.status === 400) {
+            var errors = xhr.responseText.split('\n');
+
+            var content;
+            if (errors.length === 1) {
+                content = $('<span></span>').text(errors[0]);
+            } else {
+                content = nf.Common.formatUnorderedList(errors);
+            }
+
+            nf.Dialog.showOkDialog({
+                dialogContent: content,
+                overlayBackground: false,
+                headerText: 'Configuration Error'
+            });
+        } else {
+            nf.Common.handleAjaxError(xhr, status, error);
+        }
+    };
+
+    /**
+     * Determines whether the user has made any changes to the reporting task configuration
+     * that needs to be saved.
+     */
+    var isSaveRequired = function () {
+        var details = $('#reporting-task-configuration').data('reportingTaskDetails');
+
+        // determine if any reporting task settings have changed
+
+        if ($('#reporting-task-name').val() !== details.name) {
+            return true;
+        }
+        if ($('#reporting-task-comments').val() !== details['comments']) {
+            return true;
+        }
+        if ($('#reporting-task-enabled').hasClass('checkbox-checked') && details['state'] === 'DISABLED') {
+            return true;
+        } else if ($('#reporting-task-enabled').hasClass('checkbox-unchecked') && (details['state'] === 'RUNNING' || details['state'] === 'STOPPED')) {
+            return true;
+        }
+        
+        // consider the scheduling strategy
+        var schedulingStrategy = $('#reporting-task-scheduling-strategy-combo').combo('getSelectedOption').value;
+        if (schedulingStrategy !== (details['schedulingStrategy'] + '')) {
+            return true;
+        }
+        
+        // get the appropriate scheduling period field
+        var schedulingPeriod;
+        if (schedulingStrategy === 'CRON_DRIVEN') {
+            schedulingPeriod = $('#reporting-task-cron-driven-scheduling-period');
+        } else {
+            schedulingPeriod = $('#reporting-task-timer-driven-scheduling-period');
+        }
+        
+        // check the scheduling period
+        if (nf.Common.isDefinedAndNotNull(schedulingPeriod) && schedulingPeriod.val() !== (details['schedulingPeriod'] + '')) {
+            return true;
+        }
+        
+        // defer to the properties
+        return $('#reporting-task-properties').propertytable('isSaveRequired');
+    };
+
+    /**
+     * Marshals the data that will be used to update the reporting task's configuration.
+     */
+    var marshalDetails = function () {
+        // properties
+        var properties = $('#reporting-task-properties').propertytable('marshalProperties');
+
+        // get the scheduling strategy
+        var schedulingStrategy = $('#reporting-task-scheduling-strategy-combo').combo('getSelectedOption').value;
+
+        // get the appropriate scheduling period field
+        var schedulingPeriod;
+        if (schedulingStrategy === 'CRON_DRIVEN') {
+            schedulingPeriod = $('#reporting-task-cron-driven-scheduling-period');
+        } else {
+            schedulingPeriod = $('#reporting-task-timer-driven-scheduling-period');
+        }
+
+        // create the reporting task dto
+        var reportingTaskDto = {};
+        reportingTaskDto['id'] = $('#reporting-task-id').text();
+        reportingTaskDto['name'] = $('#reporting-task-name').val();
+        reportingTaskDto['schedulingStrategy'] = schedulingStrategy;
+        reportingTaskDto['schedulingPeriod'] = schedulingPeriod.val();
+        reportingTaskDto['comments'] = $('#reporting-task-comments').val();
+        
+        // mark the processor disabled if appropriate
+        if ($('#reporting-task-enabled').hasClass('checkbox-unchecked')) {
+            reportingTaskDto['state'] = 'DISABLED';
+        } else if ($('#reporting-task-enabled').hasClass('checkbox-checked')) {
+            reportingTaskDto['state'] = 'STOPPED';
+        }
+        
+        // set the properties
+        if ($.isEmptyObject(properties) === false) {
+            reportingTaskDto['properties'] = properties;
+        }
+        
+        // create the reporting task entity
+        var reportingTaskEntity = {};
+        reportingTaskEntity['revision'] = nf.Client.getRevision();
+        reportingTaskEntity['reportingTask'] = reportingTaskDto;
+
+        // return the marshaled details
+        return reportingTaskEntity;
+    };
+
+    /**
+     * Validates the specified details.
+     * 
+     * @argument {object} details       The details to validate
+     */
+    var validateDetails = function (details) {
+        var errors = [];
+        var reportingTask = details['reportingTask'];
+        
+        if (nf.Common.isBlank(reportingTask['schedulingPeriod'])) {
+            errors.push('Run schedule must be specified');
+        }
+        
+        if (errors.length > 0) {
+            nf.Dialog.showOkDialog({
+                dialogContent: nf.Common.formatUnorderedList(errors),
+                overlayBackground: false,
+                headerText: 'Configuration Error'
+            });
+            return false;
+        } else {
+            return true;
+        }
+    };
+    
+    /**
+     * Renders the specified reporting task.
+     * 
+     * @param {object} reportingTask
+     */
+    var renderReportingTask = function (reportingTask) {
+        // get the table and update the row accordingly
+        var reportingTaskGrid = $('#reporting-tasks-table').data('gridInstance');
+        var reportingTaskData = reportingTaskGrid.getData();
+        reportingTaskData.updateItem(reportingTask.id, reportingTask);
+    };
+    
+    /**
+     * 
+     * @param {object} reportingTask
+     * @param {boolean} running
+     */
+    var setRunning = function (reportingTask, running) {
+        var revision = nf.Client.getRevision();
+        return $.ajax({
+            type: 'PUT',
+            url: reportingTask.uri,
+            data: {
+                clientId: revision.clientId,
+                version: revision.version,
+                state: running === true ? 'RUNNING' : 'STOPPED'
+            },
+            dataType: 'json'
+        }).done(function (response) {
+            // update the revision
+            nf.Client.setRevision(response.revision);
+            
+            // update the task
+            renderReportingTask(response.reportingTask);
+            nf.ControllerService.reloadReferencedServices(response.reportingTask);
+        }).fail(nf.Common.handleAjaxError);
+    };
+    
+    /**
+     * Gets a property descriptor for the controller service currently being configured.
+     * 
+     * @param {type} propertyName
+     */
+    var getReportingTaskPropertyDescriptor = function (propertyName) {
+        var details = $('#reporting-task-configuration').data('reportingTaskDetails');
+        return $.ajax({
+            type: 'GET',
+            url: details.uri + '/descriptors',
+            data: {
+                propertyName: propertyName
+            },
+            dataType: 'json'
+        }).fail(nf.Common.handleAjaxError);
+    };
+    
+    return {
+        /**
+         * Initializes the reporting task configuration dialog.
+         */
+        init: function () {
+            // initialize the configuration dialog tabs
+            $('#reporting-task-configuration-tabs').tabbs({
+                tabStyle: 'tab',
+                selectedTabStyle: 'selected-tab',
+                tabs: [{
+                        name: 'Settings',
+                        tabContentId: 'reporting-task-standard-settings-tab-content'
+                    }, {
+                        name: 'Properties',
+                        tabContentId: 'reporting-task-properties-tab-content'
+                    }, {
+                        name: 'Comments',
+                        tabContentId: 'reporting-task-comments-tab-content'
+                    }],
+                select: function () {
+                    // update the property table size in case this is the first time its rendered
+                    if ($(this).text() === 'Properties') {
+                        $('#reporting-task-properties').propertytable('resetTableSize');
+                    }
+
+                    // close all fields currently being edited
+                    $('#reporting-task-properties').propertytable('saveRow');
+                }
+            });
+            
+            // we clustered we need to show the controls for editing the availability
+            if (nf.Canvas.isClustered()) {
+                $('#reporting-task-availability-setting-container').show();
+            }
+
+            // initialize the reporting task configuration dialog
+            $('#reporting-task-configuration').data('mode', config.edit).modal({
+                headerText: 'Configure Reporting Task',
+                overlayBackground: false,
+                handler: {
+                    close: function () {
+                        // cancel any active edits
+                        $('#reporting-task-properties').propertytable('cancelEdit');
+
+                        // clear the tables
+                        $('#reporting-task-properties').propertytable('clear');
+                        
+                        // clear the comments
+                        nf.Common.clearField('read-only-reporting-task-comments');
+                        
+                        // removed the cached reporting task details
+                        $('#reporting-task-configuration').removeData('reportingTaskDetails');
+                    }
+                }
+            }).draggable({
+                containment: 'parent',
+                handle: '.dialog-header'
+            });
+
+            // initialize the property table
+            $('#reporting-task-properties').propertytable({
+                readOnly: false,
+                newPropertyDialogContainer: '#new-reporting-task-property-container',
+                deferredDescriptor: getReportingTaskPropertyDescriptor
+            });
+        },
+        
+        /**
+         * Shows the configuration dialog for the specified reporting task.
+         * 
+         * @argument {reportingTask} reportingTask      The reporting task
+         */
+        showConfiguration: function (reportingTask) {
+            var reportingTaskDialog = $('#reporting-task-configuration');
+            if (reportingTaskDialog.data('mode') === config.readOnly) {
+                // update the visibility
+                $('#reporting-task-configuration .reporting-task-read-only').hide();
+                $('#reporting-task-configuration .reporting-task-editable').show();
+                
+                // initialize the property table
+                $('#reporting-task-properties').propertytable('destroy').propertytable({
+                    readOnly: false,
+                    newPropertyDialogContainer: '#new-reporting-task-property-container',
+                    deferredDescriptor: getReportingTaskPropertyDescriptor
+                });
+                
+                // update the mode
+                reportingTaskDialog.data('mode', config.edit);
+            }
+            
+            // reload the task in case the property descriptors have changed
+            var reloadTask = $.ajax({
+                type: 'GET',
+                url: reportingTask.uri,
+                dataType: 'json'
+            });
+            
+            // get the reporting task history
+            var loadHistory = $.ajax({
+                type: 'GET',
+                url: '../nifi-api/controller/history/reporting-tasks/' + encodeURIComponent(reportingTask.id),
+                dataType: 'json'
+            });
+            
+            // once everything is loaded, show the dialog
+            $.when(reloadTask, loadHistory).done(function (taskResponse, historyResponse) {
+                // get the updated reporting task
+                reportingTask = taskResponse[0].reportingTask;
+                
+                // get the reporting task history
+                var reportingTaskHistory = historyResponse[0].componentHistory;
+                
+                // record the reporting task details
+                $('#reporting-task-configuration').data('reportingTaskDetails', reportingTask);
+
+                // determine if the enabled checkbox is checked or not
+                var reportingTaskEnableStyle = 'checkbox-checked';
+                if (reportingTask['state'] === 'DISABLED') {
+                    reportingTaskEnableStyle = 'checkbox-unchecked';
+                }
+                
+                // populate the reporting task settings
+                nf.Common.populateField('reporting-task-id', reportingTask['id']);
+                nf.Common.populateField('reporting-task-type', nf.Common.substringAfterLast(reportingTask['type'], '.'));
+                $('#reporting-task-name').val(reportingTask['name']);
+                $('#reporting-task-enabled').removeClass('checkbox-unchecked checkbox-checked').addClass(reportingTaskEnableStyle);
+                $('#reporting-task-comments').val(reportingTask['comments']);
+
+                // select the availability when appropriate
+                if (nf.Canvas.isClustered()) {
+                    if (reportingTask['availability'] === 'node') {
+                        $('#reporting-task-availability').text('Node');
+                    } else {
+                        $('#reporting-task-availability').text('Cluster Manager');
+                    }
+                }
+                
+                // get the default schedule period
+                var defaultSchedulingPeriod = reportingTask['defaultSchedulingPeriod'];
+                var cronSchedulingPeriod = $('#reporting-task-cron-driven-scheduling-period').val(defaultSchedulingPeriod['CRON_DRIVEN']);
+                var timerSchedulingPeriod = $('#reporting-task-timer-driven-scheduling-period').val(defaultSchedulingPeriod['TIMER_DRIVEN']);
+                
+                // set the scheduling period as appropriate
+                if (reportingTask['schedulingStrategy'] === 'CRON_DRIVEN') {
+                    cronSchedulingPeriod.val(reportingTask['schedulingPeriod']);
+                } else {
+                    timerSchedulingPeriod.val(reportingTask['schedulingPeriod']);
+                }
+                
+                // initialize the scheduling strategy
+                $('#reporting-task-scheduling-strategy-combo').combo({
+                        options: [{
+                        text: 'Timer driven',
+                        value: 'TIMER_DRIVEN',
+                        description: 'Reporting task will be scheduled to run on an interval defined by the run schedule.'
+                    }, {
+                        text: 'CRON driven',
+                        value: 'CRON_DRIVEN',
+                        description: 'Reporting task will be scheduled to run on at specific times based on the specified CRON string.'
+                    }],
+                    selectedOption: {
+                        value: reportingTask['schedulingStrategy']
+                    },
+                    select: function (selectedOption) {
+                        if (selectedOption.value === 'CRON_DRIVEN') {
+                            timerSchedulingPeriod.hide();
+                            cronSchedulingPeriod.show();
+                        } else {
+                            timerSchedulingPeriod.show();
+                            cronSchedulingPeriod.hide();
+                        }
+                    }
+                });
+                
+                var buttons = [{
+                        buttonText: 'Apply',
+                        handler: {
+                            click: function () {
+                                // close all fields currently being edited
+                                $('#reporting-task-properties').propertytable('saveRow');
+
+                                // marshal the settings and properties and update the reporting task
+                                var updatedReportingTask = marshalDetails();
+
+                                // ensure details are valid as far as we can tell
+                                if (validateDetails(updatedReportingTask)) {
+                                    // update the selected component
+                                    $.ajax({
+                                        type: 'PUT',
+                                        data: JSON.stringify(updatedReportingTask),
+                                        url: reportingTask.uri,
+                                        dataType: 'json',
+                                        processData: false,
+                                        contentType: 'application/json'
+                                    }).done(function (response) {
+                                        if (nf.Common.isDefinedAndNotNull(response.reportingTask)) {
+                                            // update the revision
+                                            nf.Client.setRevision(response.revision);
+
+                                            // reload the reporting task
+                                            renderReportingTask(response.reportingTask);
+                                            nf.ControllerService.reloadReferencedServices(response.reportingTask);
+
+                                            // close the details panel
+                                            $('#reporting-task-configuration').modal('hide');
+                                        }
+                                    }).fail(handleReportingTaskConfigurationError);
+                                }
+                            }
+                        }
+                    }, {
+                        buttonText: 'Cancel',
+                        handler: {
+                            click: function () {
+                                $('#reporting-task-configuration').modal('hide');
+                            }
+                        }
+                    }];
+
+                // determine if we should show the advanced button
+                if (nf.Common.isDefinedAndNotNull(reportingTask.customUiUrl) && reportingTask.customUiUrl !== '') {
+                    buttons.push({
+                        buttonText: 'Advanced',
+                        handler: {
+                            click: function () {
+                                var openCustomUi = function () {
+                                    // reset state and close the dialog manually to avoid hiding the faded background
+                                    $('#reporting-task-configuration').modal('hide');
+
+                                    // close the settings dialog since the custom ui is also opened in the shell
+                                    $('#shell-close-button').click();
+
+                                    // show the custom ui
+                                    nf.CustomUi.showCustomUi($('#reporting-task-id').text(), reportingTask.customUiUrl, true).done(function () {
+                                        // once the custom ui is closed, reload the reporting task
+                                        nf.ReportingTask.reload(reportingTask.id).done(function (response) {
+                                            nf.ControllerService.reloadReferencedServices(response.reportingTask);
+                                        });
+                                        
+                                        // show the settings
+                                        nf.Settings.showSettings();
+                                    });
+                                };
+
+                                // close all fields currently being edited
+                                $('#reporting-task-properties').propertytable('saveRow');
+
+                                // determine if changes have been made
+                                if (isSaveRequired()) {
+                                    // see if those changes should be saved
+                                    nf.Dialog.showYesNoDialog({
+                                        dialogContent: 'Save changes before opening the advanced configuration?',
+                                        overlayBackground: false,
+                                        noHandler: openCustomUi,
+                                        yesHandler: function () {
+                                            // marshal the settings and properties and update the reporting task
+                                            var updatedReportingTask = marshalDetails();
+
+                                            // ensure details are valid as far as we can tell
+                                            if (validateDetails(updatedReportingTask)) {
+                                                // update the selected component
+                                                $.ajax({
+                                                    type: 'PUT',
+                                                    data: JSON.stringify(updatedReportingTask),
+                                                    url: reportingTask.uri,
+                                                    dataType: 'json',
+                                                    processData: false,
+                                                    contentType: 'application/json'
+                                                }).done(function (response) {
+                                                    if (nf.Common.isDefinedAndNotNull(response.reportingTask)) {
+                                                        // update the revision
+                                                        nf.Client.setRevision(response.revision);
+
+                                                        // open the custom ui
+                                                        openCustomUi();
+                                                    }
+                                                }).fail(handleReportingTaskConfigurationError);
+                                            }
+                                        }
+                                    });
+                                } else {
+                                    // if there were no changes, simply open the custom ui
+                                    openCustomUi();
+                                }
+                            }
+                        }
+                    });
+                }
+
+                // set the button model
+                $('#reporting-task-configuration').modal('setButtonModel', buttons);
+                
+                // load the property table
+                $('#reporting-task-properties').propertytable('loadProperties', reportingTask.properties, reportingTask.descriptors, reportingTaskHistory.propertyHistory);
+
+                // show the details
+                $('#reporting-task-configuration').modal('show');
+            }).fail(nf.Common.handleAjaxError);
+        }, 
+        
+        /**
+         * Shows the reporting task details in a read only dialog.
+         * 
+         * @param {object} reportingTask
+         */
+        showDetails: function(reportingTask) {
+            var reportingTaskDialog = $('#reporting-task-configuration');
+            if (reportingTaskDialog.data('mode') === config.edit) {
+                // update the visibility
+                $('#reporting-task-configuration .reporting-task-read-only').show();
+                $('#reporting-task-configuration .reporting-task-editable').hide();
+                
+                // initialize the property table
+                $('#reporting-task-properties').propertytable('destroy').propertytable({
+                    readOnly: true
+                });
+                
+                // update the mode
+                reportingTaskDialog.data('mode', config.readOnly);
+            }
+            
+            // reload the task in case the property descriptors have changed
+            var reloadTask = $.ajax({
+                type: 'GET',
+                url: reportingTask.uri,
+                dataType: 'json'
+            });
+            
+            // get the reporting task history
+            var loadHistory = $.ajax({
+                type: 'GET',
+                url: '../nifi-api/controller/history/reporting-tasks/' + encodeURIComponent(reportingTask.id),
+                dataType: 'json'
+            });
+            
+            // once everything is loaded, show the dialog
+            $.when(reloadTask, loadHistory).done(function (taskResponse, historyResponse) {
+                // get the updated reporting task
+                reportingTask = taskResponse[0].reportingTask;
+                
+                // get the reporting task history
+                var reportingTaskHistory = historyResponse[0].componentHistory;
+                
+                // populate the reporting task settings
+                nf.Common.populateField('reporting-task-id', reportingTask['id']);
+                nf.Common.populateField('reporting-task-type', nf.Common.substringAfterLast(reportingTask['type'], '.'));
+                nf.Common.populateField('read-only-reporting-task-name', reportingTask['name']);
+                nf.Common.populateField('read-only-reporting-task-comments', reportingTask['comments']);
+
+                // select the availability when appropriate
+                if (nf.Canvas.isClustered()) {
+                    if (reportingTask['availability'] === 'node') {
+                        $('#reporting-task-availability').text('Node');
+                    } else {
+                        $('#reporting-task-availability').text('Cluster Manager');
+                    }
+                }
+                
+                // make the scheduling strategy human readable
+                var schedulingStrategy = reportingTask['schedulingStrategy'];
+                if (schedulingStrategy === 'CRON_DRIVEN') {
+                    schedulingStrategy = 'CRON driven';
+                } else {
+                    schedulingStrategy = "Timer driven";
+                }
+                nf.Common.populateField('read-only-reporting-task-scheduling-strategy', schedulingStrategy);
+                nf.Common.populateField('read-only-reporting-task-scheduling-period', reportingTask['schedulingPeriod']);
+                
+                var buttons = [{
+                        buttonText: 'Ok',
+                        handler: {
+                            click: function () {
+                                // hide the dialog
+                                reportingTaskDialog.modal('hide');
+                            }
+                        }
+                    }];
+
+                // determine if we should show the advanced button
+                if (nf.Common.isDefinedAndNotNull(nf.CustomUi) && nf.Common.isDefinedAndNotNull(reportingTask.customUiUrl) && reportingTask.customUiUrl !== '') {
+                    buttons.push({
+                        buttonText: 'Advanced',
+                        handler: {
+                            click: function () {
+                                // reset state and close the dialog manually to avoid hiding the faded background
+                                reportingTaskDialog.modal('hide');
+
+                                // close the settings dialog since the custom ui is also opened in the shell
+                                $('#shell-close-button').click();
+
+                                // show the custom ui
+                                nf.CustomUi.showCustomUi(reportingTask.id, reportingTask.customUiUrl, false).done(function() {
+                                    nf.Settings.showSettings();
+                                });
+                            }
+                        }
+                    });
+                }
+                
+                // show the dialog
+                reportingTaskDialog.modal('setButtonModel', buttons).modal('show');
+                
+                // load the property table
+                $('#reporting-task-properties').propertytable('loadProperties', reportingTask.properties, reportingTask.descriptors, reportingTaskHistory.propertyHistory);
+                
+                // show the details
+                reportingTaskDialog.modal('show');
+            });
+        },
+        
+        /**
+         * Starts the specified reporting task.
+         * 
+         * @param {object} reportingTask
+         */
+        start: function(reportingTask) {
+            setRunning(reportingTask, true);
+        },
+        
+        /**
+         * Stops the specified reporting task.
+         * 
+         * @param {object} reportingTask
+         */
+        stop: function(reportingTask) {
+            setRunning(reportingTask, false);
+        },
+        
+        /**
+         * Reloads the specified reporting task.
+         * 
+         * @param {string} id
+         */
+        reload: function (id) {
+            var reportingTaskGrid = $('#reporting-tasks-table').data('gridInstance');
+            var reportingTaskData = reportingTaskGrid.getData();
+            var reportingTask = reportingTaskData.getItemById(id);
+        
+            return $.ajax({
+                type: 'GET',
+                url: reportingTask.uri,
+                dataType: 'json'
+            }).done(function (response) {
+                renderReportingTask(response.reportingTask);
+            }).fail(nf.Common.handleAjaxError);
+        },
+        
+        /**
+         * Deletes the specified reporting task.
+         * 
+         * @param {object} reportingTask
+         */
+        remove: function(reportingTask) {
+            // prompt for removal?
+                    
+            var revision = nf.Client.getRevision();
+            $.ajax({
+                type: 'DELETE',
+                url: reportingTask.uri + '?' + $.param({
+                    version: revision.version,
+                    clientId: revision.clientId
+                }),
+                dataType: 'json'
+            }).done(function (response) {
+                // update the revision
+                nf.Client.setRevision(response.revision);
+
+                // remove the task
+                var reportingTaskGrid = $('#reporting-tasks-table').data('gridInstance');
+                var reportingTaskData = reportingTaskGrid.getData();
+                reportingTaskData.deleteItem(reportingTask.id);
+            }).fail(nf.Common.handleAjaxError);
+        }
+    };
+}());

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-secure-port-details.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-secure-port-details.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-secure-port-details.js
index fec4229..63afac0 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-secure-port-details.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-secure-port-details.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 nf.SecurePortDetails = (function () {
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-selectable.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-selectable.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-selectable.js
index 552d51e..c44a91b 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-selectable.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-selectable.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.Selectable = (function () {
 
     return {


[17/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/ServiceB.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/ServiceB.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/ServiceB.java
new file mode 100644
index 0000000..070b156
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/ServiceB.java
@@ -0,0 +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.controller.service.mock;
+
+import org.apache.nifi.controller.AbstractControllerService;
+
+public class ServiceB extends AbstractControllerService {
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/processor/TestStandardPropertyValue.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/processor/TestStandardPropertyValue.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/processor/TestStandardPropertyValue.java
index 7390098..a0bf30d 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/processor/TestStandardPropertyValue.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/processor/TestStandardPropertyValue.java
@@ -17,6 +17,7 @@
 package org.apache.nifi.processor;
 
 import org.apache.nifi.processor.StandardPropertyValue;
+
 import static org.junit.Assert.assertEquals;
 
 import java.util.Calendar;
@@ -29,7 +30,6 @@ import org.apache.nifi.controller.ControllerService;
 import org.apache.nifi.controller.ControllerServiceLookup;
 import org.apache.nifi.controller.repository.StandardFlowFileRecord;
 import org.apache.nifi.flowfile.FlowFile;
-
 import org.junit.Test;
 
 public class TestStandardPropertyValue {
@@ -163,5 +163,14 @@ public class TestStandardPropertyValue {
             return true;
         }
 
+        @Override
+        public String getControllerServiceName(String serviceIdentifier) {
+        	return null;
+        }
+        
+        @Override
+        public boolean isControllerServiceEnabling(String serviceIdentifier) {
+            return false;
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/.gitignore
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/.gitignore b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/.gitignore
index ea8c4bf..29546b5 100755
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/.gitignore
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/.gitignore
@@ -1 +1,2 @@
 /target
+/target/

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/controller-services.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/controller-services.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/controller-services.xml
deleted file mode 100644
index f5bd96a..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/controller-services.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
-  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.
--->
-<services>
-
-</services>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
index fd16cb5..90b3cdd 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
@@ -25,8 +25,6 @@ nifi.administrative.yield.duration=${nifi.administrative.yield.duration}
 nifi.bored.yield.duration=${nifi.bored.yield.duration}
 
 nifi.authority.provider.configuration.file=${nifi.authority.provider.configuration.file}
-nifi.reporting.task.configuration.file=${nifi.reporting.task.configuration.file}
-nifi.controller.service.configuration.file=${nifi.controller.service.configuration.file}
 nifi.templates.directory=${nifi.templates.directory}
 nifi.ui.banner.text=${nifi.ui.banner.text}
 nifi.ui.autorefresh.interval=${nifi.ui.autorefresh.interval}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/reporting-tasks.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/reporting-tasks.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/reporting-tasks.xml
deleted file mode 100644
index 3f60b93..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/reporting-tasks.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
-  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.
--->
-<tasks>
-    <property name="system">NiFi</property>
-    <task>
-        <id>status-report</id>
-        <name>Controller Status Report</name>
-        <class>org.apache.nifi.controller.ControllerStatusReportingTask</class>
-        <schedulingPeriod>3 mins</schedulingPeriod>
-    </task>
-
-    <!-- Monitors disk usage for FlowFile Repository and Content Repository, creating bulletins/logs if a threshold is exceeded -->    
-    <!--
-    <task>
-        <id>Monitor Disk Usage</id>
-        <name>Monitor Disk Usage</name>
-        <class>org.apache.nifi.controller.MonitorDiskUsage</class>
-        <schedulingPeriod>2 mins</schedulingPeriod>
-        <property name="Content Repository Threshold">80%</property>
-        <property name="FlowFile Repository Threshold">80%</property>
-    </task>
-    -->
-    
-    <!-- Monitors JVM Heap, creating bulletins/logs if a threshold is exceeded immediately after Garbage Collection occurs -->
-    <!--
-    <task>
-        <id>Monitor Old Gen</id>
-        <name>Monitor Old Gen</name>
-        <class>org.apache.nifi.controller.MonitorMemory</class>
-        <schedulingPeriod>2 mins</schedulingPeriod>
-        <property name="Memory Pool">PS Old Gen</property>
-        <property name="Usage Threshold">65%</property>
-        <property name="Reporting Interval">5 min</property>
-    </task>
-    -->
-</tasks>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-user-actions/src/main/java/org/apache/nifi/action/Component.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-user-actions/src/main/java/org/apache/nifi/action/Component.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-user-actions/src/main/java/org/apache/nifi/action/Component.java
index 62dd68f..7f62c92 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-user-actions/src/main/java/org/apache/nifi/action/Component.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-user-actions/src/main/java/org/apache/nifi/action/Component.java
@@ -27,5 +27,7 @@ public enum Component {
     ProcessGroup,
     RemoteProcessGroup,
 	Funnel,
-    Connection;
+    Connection,
+    ControllerService,
+    ReportingTask;
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-user-actions/src/main/java/org/apache/nifi/action/component/details/ExtensionDetails.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-user-actions/src/main/java/org/apache/nifi/action/component/details/ExtensionDetails.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-user-actions/src/main/java/org/apache/nifi/action/component/details/ExtensionDetails.java
new file mode 100644
index 0000000..e557548
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-user-actions/src/main/java/org/apache/nifi/action/component/details/ExtensionDetails.java
@@ -0,0 +1,34 @@
+/*
+ * 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.action.component.details;
+
+/**
+ *
+ */
+public class ExtensionDetails extends ComponentDetails {
+
+    private String type;
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-user-actions/src/main/java/org/apache/nifi/action/component/details/ProcessorDetails.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-user-actions/src/main/java/org/apache/nifi/action/component/details/ProcessorDetails.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-user-actions/src/main/java/org/apache/nifi/action/component/details/ProcessorDetails.java
deleted file mode 100644
index ef0af7c..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-user-actions/src/main/java/org/apache/nifi/action/component/details/ProcessorDetails.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.action.component.details;
-
-/**
- *
- */
-public class ProcessorDetails extends ComponentDetails {
-
-    private String type;
-
-    public String getType() {
-        return type;
-    }
-
-    public void setType(String type) {
-        this.type = type;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletConfigurationRequestContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletConfigurationRequestContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletConfigurationRequestContext.java
new file mode 100644
index 0000000..986ce4c
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletConfigurationRequestContext.java
@@ -0,0 +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.web;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * An implementation of the ConfigurationRequestContext that retrieves configuration
+ * from a HttpServletRequest instance.
+ */
+public class HttpServletConfigurationRequestContext extends HttpServletRequestContext implements NiFiWebConfigurationRequestContext {
+
+    private static final String CLIENT_ID_PARAM = "clientId";
+    private static final String REVISION_PARAM = "revision";
+
+    private final HttpServletRequest request;
+
+    public HttpServletConfigurationRequestContext(final UiExtensionType extensionType, final HttpServletRequest request) {
+        super(extensionType, request);
+        this.request = request;
+    }
+
+    /**
+     * @return the revision retrieved from the request parameters with keys
+     * equal to "clientId" and "revision".
+     */
+    @Override
+    public Revision getRevision() {
+        final String revisionParamVal = request.getParameter(REVISION_PARAM);
+        Long revision;
+        try {
+            revision = Long.parseLong(revisionParamVal);
+        } catch (final Exception ex) {
+            revision = null;
+        }
+
+        final String clientId = request.getParameter(CLIENT_ID_PARAM);
+
+        return new Revision(revision, clientId);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletRequestContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletRequestContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletRequestContext.java
new file mode 100644
index 0000000..311fbc7
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletRequestContext.java
@@ -0,0 +1,100 @@
+/*
+ * 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.web;
+
+import java.security.cert.X509Certificate;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * An implementation of the ConfigurationRequestContext that retrieves configuration
+ * from a HttpServletRequest instance.
+ */
+public class HttpServletRequestContext implements NiFiWebRequestContext {
+
+    private static final String ID_PARAM = "id";
+
+    private final UiExtensionType extensionType;
+    private final HttpServletRequest request;
+
+    public HttpServletRequestContext(final UiExtensionType extensionType, final HttpServletRequest request) {
+        this.extensionType = extensionType;
+        this.request = request;
+    }
+
+    @Override
+    public UiExtensionType getExtensionType() {
+        return extensionType;
+    }
+
+    @Override
+    public String getProxiedEntitiesChain() {
+        String xProxiedEntitiesChain = request.getHeader("X-ProxiedEntitiesChain");
+        final X509Certificate cert = extractClientCertificate(request);
+        if (cert != null) {
+            final String extractedPrincipal = extractPrincipal(cert);
+            final String formattedPrincipal = formatProxyDn(extractedPrincipal);
+            if (xProxiedEntitiesChain == null || xProxiedEntitiesChain.trim().isEmpty()) {
+                xProxiedEntitiesChain = formattedPrincipal;
+            } else {
+                xProxiedEntitiesChain += formattedPrincipal;
+            }
+        }
+
+        return xProxiedEntitiesChain;
+    }
+
+    /**
+     * @return the protocol scheme of the HttpServletRequest instance.
+     */
+    @Override
+    public String getScheme() {
+        return request.getScheme();
+    }
+
+    /**
+     * @return the ID retrieved from the request parameter with key
+     * equal to "id".
+     */
+    @Override
+    public String getId() {
+        return request.getParameter(ID_PARAM);
+    }
+
+    /**
+     * Utility methods that have been copied into this class to reduce the
+     * dependency footprint of this artifact. These utility methods typically
+     * live in web-utilities but that would pull in spring, jersey, jackson,
+     * etc.
+     */
+    private X509Certificate extractClientCertificate(HttpServletRequest request) {
+        X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
+
+        if (certs != null && certs.length > 0) {
+            return certs[0];
+        }
+
+        return null;
+    }
+
+    private String extractPrincipal(X509Certificate cert) {
+        return cert.getSubjectDN().getName().trim();
+    }
+
+    private String formatProxyDn(String dn) {
+        return "<" + dn + ">";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletRequestContextConfig.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletRequestContextConfig.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletRequestContextConfig.java
index e39ebcc..e376ab6 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletRequestContextConfig.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-custom-ui-utilities/src/main/java/org/apache/nifi/web/HttpServletRequestContextConfig.java
@@ -23,6 +23,7 @@ import javax.servlet.http.HttpServletRequest;
  * An implementation of the NiFiWebContextConfig that retrieves configuration
  * from a HttpServletRequest instance.
  */
+@Deprecated
 public class HttpServletRequestContextConfig implements NiFiWebContextConfig {
 
     public static final String PROCESSOR_ID_PARAM = "processorId";

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/pom.xml
index 9f1cf99..4db9637 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/pom.xml
@@ -125,6 +125,11 @@
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-ui-extension</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-web-security</artifactId>
             <scope>compile</scope>
         </dependency>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
index 54111a1..1134c77 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
@@ -53,6 +53,10 @@ import org.apache.nifi.web.NiFiWebContext;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.web.ContentAccess;
+import org.apache.nifi.ui.extension.UiExtension;
+import org.apache.nifi.ui.extension.UiExtensionMapping;
+import org.apache.nifi.web.NiFiWebConfigurationContext;
+import org.apache.nifi.web.UiExtensionType;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.HttpConfiguration;
@@ -97,14 +101,23 @@ public class JettyServer implements NiFiServer {
     };
 
     private final Server server;
+    private final NiFiProperties props;
+    
     private ExtensionMapping extensionMapping;
     private WebAppContext webApiContext;
     private WebAppContext webDocsContext;
+    
+    // content viewer and mime type specific extensions
     private WebAppContext webContentViewerContext;
-    private Collection<WebAppContext> customUiWebContexts;
     private Collection<WebAppContext> contentViewerWebContexts;
-    private final NiFiProperties props;
 
+    // component (processor, controller service, reporting task) ui extensions
+    private UiExtensionMapping componentUiExtensions;
+    private Collection<WebAppContext> componentUiExtensionWebContexts;
+    
+    @Deprecated
+    private Collection<WebAppContext> customUiWebContexts;
+    
     /**
      * Creates and configures a new Jetty instance.
      *
@@ -200,23 +213,34 @@ public class JettyServer implements NiFiServer {
 
         // handlers for each war and init params for the web api
         final HandlerCollection handlers = new HandlerCollection();
-        final Map<String, String> customUiMappings = new HashMap<>();
-        final Map<String, String> mimeTypeMappings = new HashMap<>();
+        final Map<String, String> mimeMappings = new HashMap<>();
         final ClassLoader frameworkClassLoader = getClass().getClassLoader();
         final ClassLoader jettyClassLoader = frameworkClassLoader.getParent();
 
+        @Deprecated
+        final Map<String, String> customUiMappings = new HashMap<>();
+        
         // deploy the other wars
         if (CollectionUtils.isNotEmpty(otherWars)) {
+            // hold onto to the web contexts for all ui extensions
             customUiWebContexts = new ArrayList<>();
+            componentUiExtensionWebContexts = new ArrayList<>();
             contentViewerWebContexts = new ArrayList<>();
             
+            // ui extension organized by component type
+            final Map<String, List<UiExtension>> componentUiExtensionsByType = new HashMap<>();
             for (File war : otherWars) {
                 // see if this war is a custom processor ui
+                @Deprecated
                 List<String> customUiProcessorTypes = getWarExtensions(war, "META-INF/nifi-processor");
-                List<String> contentViewerMimeTypes = getWarExtensions(war, "META-INF/nifi-content-viewer");
 
-                // only include wars that are for extensions
-                if (!customUiProcessorTypes.isEmpty() || !contentViewerMimeTypes.isEmpty()) {
+                // identify all known extension types in the war
+                final Map<UiExtensionType, List<String>> uiExtensionInWar = new HashMap<>();
+                identifyUiExtensionsForComponents(uiExtensionInWar, war);
+                
+                // only include wars that are for custom processor ui's
+                if (!customUiProcessorTypes.isEmpty() || !uiExtensionInWar.isEmpty()) {
+                    // get the context path
                     String warName = StringUtils.substringBeforeLast(war.getName(), ".");
                     String warContextPath = String.format("/%s", warName);
 
@@ -234,24 +258,62 @@ public class JettyServer implements NiFiServer {
                     // also store it by type so we can populate the appropriate initialization parameters
                     if (!customUiProcessorTypes.isEmpty()) {
                         customUiWebContexts.add(extensionUiContext);
+                        
+                        // @Deprecated - supported custom uis as init params to the web api
+                        for (String customUiProcessorType : customUiProcessorTypes) {
+                            // map the processor type to the custom ui path
+                            customUiMappings.put(customUiProcessorType, warContextPath);
+                        }
                     } else {
-                        // record the mime type to web app mapping (need to handle type collision)
-                        contentViewerWebContexts.add(extensionUiContext);
+                        // create the ui extensions
+                        for (final Map.Entry<UiExtensionType, List<String>> entry : uiExtensionInWar.entrySet()) {
+                            final UiExtensionType extensionType = entry.getKey();
+                            final List<String> types = entry.getValue();
+
+                            if (UiExtensionType.ContentViewer.equals(extensionType)) {
+                                // consider each content type identified
+                                for (final String contentType : types) {
+                                    // map the content type to the context path
+                                    mimeMappings.put(contentType, warContextPath);
+                                }
+
+                                // this ui extension provides a content viewer
+                                contentViewerWebContexts.add(extensionUiContext);
+                            } else {
+                                // consider each component type identified
+                                for (final String componentType : types) {
+                                    logger.info(String.format("Loading UI extension [%s, %s] for %s", extensionType, warContextPath, types));
+
+                                    // record the extension definition
+                                    final UiExtension uiExtension = new UiExtension(extensionType, warContextPath);
+
+                                    // create if this is the first extension for this component type
+                                    List<UiExtension> componentUiExtensionsForType = componentUiExtensionsByType.get(componentType);
+                                    if (componentUiExtensionsForType == null) {
+                                        componentUiExtensionsForType = new ArrayList<>();
+                                        componentUiExtensionsByType.put(componentType, componentUiExtensionsForType);
+                                    }
+
+                                    // record this extension
+                                    componentUiExtensionsForType.add(uiExtension);
+                                }
+
+                                // this ui extension provides a component custom ui
+                                componentUiExtensionWebContexts.add(extensionUiContext);
+                            }
+                        }
                     }
 
                     // include custom ui web context in the handlers
                     handlers.addHandler(extensionUiContext);
-
-                    // add the initialization paramters
-                    for (String customUiProcessorType : customUiProcessorTypes) {
-                        // map the processor type to the custom ui path
-                        customUiMappings.put(customUiProcessorType, warContextPath);
-                    }
-                    for (final String contentViewerMimeType : contentViewerMimeTypes) {
-                        mimeTypeMappings.put(contentViewerMimeType, warContextPath);
-                    }
                 }
+                
             }
+            
+            // record all ui extensions to give to the web api
+            componentUiExtensions = new UiExtensionMapping(componentUiExtensionsByType);
+        } else {
+            componentUiExtensions = new UiExtensionMapping(Collections.EMPTY_MAP);
         }
 
         // load the web ui app
@@ -264,7 +326,7 @@ public class JettyServer implements NiFiServer {
 
         // load the content viewer app
         webContentViewerContext = loadWar(webContentViewerWar, "/nifi-content-viewer", frameworkClassLoader);
-        webContentViewerContext.getInitParams().putAll(mimeTypeMappings);
+        webContentViewerContext.getInitParams().putAll(mimeMappings);
         handlers.addHandler(webContentViewerContext);
         
         // create a web app for the docs
@@ -315,6 +377,67 @@ public class JettyServer implements NiFiServer {
         return wars;
     }
 
+    private void readUiExtensions(final Map<UiExtensionType, List<String>> uiExtensions, final UiExtensionType uiExtensionType, final JarFile jarFile, final JarEntry jarEntry) throws IOException {
+        if (jarEntry == null) {
+            return;
+        }
+        
+        // get an input stream for the nifi-processor configuration file
+        BufferedReader in = new BufferedReader(new InputStreamReader(jarFile.getInputStream(jarEntry)));
+
+        // read in each configured type
+        String rawComponentType;
+        while ((rawComponentType = in.readLine()) != null) {
+            // extract the component type
+            final String componentType = extractComponentType(rawComponentType);
+            if (componentType != null) {
+                List<String> extensions = uiExtensions.get(uiExtensionType);
+                
+                // if there are currently no extensions for this type create it
+                if (extensions == null) {
+                    extensions = new ArrayList<>();
+                    uiExtensions.put(uiExtensionType, extensions);
+                }
+                
+                // add the specified type
+                extensions.add(componentType);
+            }
+        }
+    }
+    
+    /**
+     * Identifies all known UI extensions and stores them in the specified map.
+     * 
+     * @param uiExtensions
+     * @param warFile 
+     */
+    private void identifyUiExtensionsForComponents(final Map<UiExtensionType, List<String>> uiExtensions, final File warFile) {
+        try (final JarFile jarFile = new JarFile(warFile)) {
+            // locate the ui extensions
+            readUiExtensions(uiExtensions, UiExtensionType.ContentViewer, jarFile, jarFile.getJarEntry("META-INF/nifi-content-viewer"));
+            readUiExtensions(uiExtensions, UiExtensionType.ProcessorConfiguration, jarFile, jarFile.getJarEntry("META-INF/nifi-processor-configuration"));
+            readUiExtensions(uiExtensions, UiExtensionType.ControllerServiceConfiguration, jarFile, jarFile.getJarEntry("META-INF/nifi-controller-service-configuration"));
+            readUiExtensions(uiExtensions, UiExtensionType.ReportingTaskConfiguration, jarFile, jarFile.getJarEntry("META-INF/nifi-reporting-task-configuration"));
+        } catch (IOException ioe) {
+            logger.warn(String.format("Unable to inspect %s for a UI extensions.", warFile));
+        }
+    }
+    
+    /**
+     * Extracts the component type. Trims the line and considers comments. Returns null if no type was found.
+     * 
+     * @param line
+     * @return 
+     */
+    private String extractComponentType(final String line) {
+        final String trimmedLine = line.trim();
+        if (!trimmedLine.isEmpty() && !trimmedLine.startsWith("#")) {
+            final int indexOfPound = trimmedLine.indexOf("#");
+            return (indexOfPound > 0) ? trimmedLine.substring(0, indexOfPound) : trimmedLine;
+        }
+        return null;
+    }
+    
     /**
      * Returns the extension in the specified WAR using the specified path.
      *
@@ -335,10 +458,11 @@ public class JettyServer implements NiFiServer {
                 BufferedReader in = new BufferedReader(new InputStreamReader(jarFile.getInputStream(jarEntry)));
 
                 // read in each configured type
-                String processorType;
-                while ((processorType = in.readLine()) != null) {
-                    // ensure the line isn't blank
-                    if (StringUtils.isNotBlank(processorType)) {
+                String rawProcessorType;
+                while ((rawProcessorType = in.readLine()) != null) {
+                    // extract the processor type
+                    final String processorType = extractComponentType(rawProcessorType);
+                    if (processorType != null) {
                         processorTypes.add(processorType);
                     }
                 }
@@ -558,13 +682,18 @@ public class JettyServer implements NiFiServer {
                     }
                 }
             }
-
+            
             // ensure the appropriate wars deployed successfully before injecting the NiFi context and security filters - 
             // this must be done after starting the server (and ensuring there were no start up failures)
             if (webApiContext != null) {
+                // give the web api the component ui extensions
                 final ServletContext webApiServletContext = webApiContext.getServletHandler().getServletContext();
+                webApiServletContext.setAttribute("nifi-ui-extensions", componentUiExtensions);
+                
+                // get the application context
                 final WebApplicationContext webApplicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(webApiServletContext);
 
+                // @Deprecated
                 if (CollectionUtils.isNotEmpty(customUiWebContexts)) {
                     final NiFiWebContext niFiWebContext = webApplicationContext.getBean("nifiWebContext", NiFiWebContext.class);
                     
@@ -576,22 +705,40 @@ public class JettyServer implements NiFiServer {
                         // add the security filter to any custom ui wars
                         final FilterHolder securityFilter = webApiContext.getServletHandler().getFilter("springSecurityFilterChain");
                         if (securityFilter != null) {
-                            customUiContext.addFilter(securityFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
+                            customUiContext.addFilter(securityFilter, "/*", EnumSet.allOf(DispatcherType.class));
                         }
                     }
                 }
                 
+                // component ui extensions
+                if (CollectionUtils.isNotEmpty(componentUiExtensionWebContexts)) {
+                    final NiFiWebConfigurationContext configurationContext = webApplicationContext.getBean("nifiWebConfigurationContext", NiFiWebConfigurationContext.class);
+
+                    for (final WebAppContext customUiContext : componentUiExtensionWebContexts) {
+                        // set the NiFi context in each custom ui servlet context
+                        final ServletContext customUiServletContext = customUiContext.getServletHandler().getServletContext();
+                        customUiServletContext.setAttribute("nifi-web-configuration-context", configurationContext);
+
+                        // add the security filter to any ui extensions wars
+                        final FilterHolder securityFilter = webApiContext.getServletHandler().getFilter("springSecurityFilterChain");
+                        if (securityFilter != null) {
+                            customUiContext.addFilter(securityFilter, "/*", EnumSet.allOf(DispatcherType.class));
+                        }
+                    }
+                }
+                
+                // content viewer extensions
                 if (CollectionUtils.isNotEmpty(contentViewerWebContexts)) {
                     for (final WebAppContext contentViewerContext : contentViewerWebContexts) {
                         // add the security filter to any content viewer  wars
                         final FilterHolder securityFilter = webApiContext.getServletHandler().getFilter("springSecurityFilterChain");
                         if (securityFilter != null) {
-                            contentViewerContext.addFilter(securityFilter, "/*", EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE));
+                            contentViewerContext.addFilter(securityFilter, "/*", EnumSet.allOf(DispatcherType.class));
                         }
                     }
                 }
                 
-                // ensure the web content viewer war was loaded
+                // content viewer controller
                 if (webContentViewerContext != null) {
                     final ContentAccess contentAccess = webApplicationContext.getBean("contentAccess", ContentAccess.class);
                     
@@ -599,14 +746,13 @@ public class JettyServer implements NiFiServer {
                     final ServletContext webContentViewerServletContext = webContentViewerContext.getServletHandler().getServletContext();
                     webContentViewerServletContext.setAttribute("nifi-content-access", contentAccess);
                     
-                    // add the security filter to the content viewer controller
                     final FilterHolder securityFilter = webApiContext.getServletHandler().getFilter("springSecurityFilterChain");
                     if (securityFilter != null) {
-                        webContentViewerContext.addFilter(securityFilter, "/*", EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE));
+                        webContentViewerContext.addFilter(securityFilter, "/*", EnumSet.allOf(DispatcherType.class));
                     }
                 }
             }
-
+            
             // ensure the web document war was loaded and provide the extension mapping
             if (webDocsContext != null) {
                 final ServletContext webDocsServletContext = webDocsContext.getServletHandler().getServletContext();
@@ -735,4 +881,4 @@ public class JettyServer implements NiFiServer {
             logger.warn("Failed to stop web server", ex);
         }
     }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/pom.xml
new file mode 100644
index 0000000..22388b3
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/pom.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-web</artifactId>
+        <version>0.1.0-incubating-SNAPSHOT</version>
+    </parent>
+    <artifactId>nifi-ui-extension</artifactId>
+    <packaging>jar</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-user-actions</artifactId>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtension.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtension.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtension.java
new file mode 100644
index 0000000..e5b9b3e
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtension.java
@@ -0,0 +1,52 @@
+/*
+ * 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.ui.extension;
+
+import org.apache.nifi.web.UiExtensionType;
+
+/**
+ * Information about a UI extension required to be invoked.
+ */
+public class UiExtension {
+
+    private final UiExtensionType extensionType;
+    private final String contextPath;
+
+    public UiExtension(final UiExtensionType extensionType, final String contextPath) {
+        this.extensionType = extensionType;
+        this.contextPath = contextPath;
+    }
+
+    /**
+     * The type of this UI extension.
+     * 
+     * @return 
+     */
+    public UiExtensionType getExtensionType() {
+        return extensionType;
+    }
+
+    /**
+     * The context path of this UI extenion.
+     * 
+     * @return 
+     */
+    public String getContextPath() {
+        return contextPath;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionMapping.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionMapping.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionMapping.java
new file mode 100644
index 0000000..16bffd0
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionMapping.java
@@ -0,0 +1,52 @@
+/*
+ * 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.ui.extension;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Mapping of all discovered UI extensions.
+ */
+public class UiExtensionMapping {
+
+    private final Map<String, List<UiExtension>> uiExtensions;
+
+    public UiExtensionMapping(Map<String, List<UiExtension>> uiExtensions) {
+        this.uiExtensions = uiExtensions;
+    }
+
+    /**
+     * Returns whether there are any UI extensions for the specified component type.
+     * 
+     * @param type
+     * @return 
+     */
+    public boolean hasUiExtension(final String type) {
+        return uiExtensions.containsKey(type);
+    }
+    
+    /**
+     * Gets the listing of all discovered UI extensions for the specified component type.
+     * @param type
+     * @return 
+     */
+    public List<UiExtension> getUiExtension(final String type) {
+        return uiExtensions.get(type);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml
index 6a51838..5a87ff8 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml
@@ -159,6 +159,11 @@
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-ui-extension</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-administration</artifactId>
             <scope>provided</scope>
         </dependency>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerServiceAuditor.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerServiceAuditor.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerServiceAuditor.java
new file mode 100644
index 0000000..ea3af14
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ControllerServiceAuditor.java
@@ -0,0 +1,475 @@
+/*
+ * 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.audit;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.nifi.action.Action;
+import org.apache.nifi.action.Component;
+import org.apache.nifi.action.Operation;
+import org.apache.nifi.action.component.details.ExtensionDetails;
+import org.apache.nifi.action.details.ActionDetails;
+import org.apache.nifi.action.details.ConfigureDetails;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.controller.ConfiguredComponent;
+import org.apache.nifi.controller.ProcessorNode;
+import org.apache.nifi.controller.ReportingTaskNode;
+import org.apache.nifi.controller.ScheduledState;
+import org.apache.nifi.web.security.user.NiFiUserUtils;
+import org.apache.nifi.user.NiFiUser;
+import org.apache.nifi.controller.service.ControllerServiceNode;
+import org.apache.nifi.controller.service.ControllerServiceReference;
+import org.apache.nifi.controller.service.ControllerServiceState;
+import org.apache.nifi.reporting.ReportingTask;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
+import org.apache.nifi.web.dao.ControllerServiceDAO;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Audits controller service creation/removal and configuration changes.
+ */
+@Aspect
+public class ControllerServiceAuditor extends NiFiAuditor {
+
+    private static final Logger logger = LoggerFactory.getLogger(ControllerServiceAuditor.class);
+
+    private static final String COMMENTS = "Comments";
+    private static final String NAME = "Name";
+    private static final String ANNOTATION_DATA = "Annotation Data";
+
+    /**
+     * Audits the creation of controller service via createControllerService().
+     *
+     * This method only needs to be run 'after returning'. However, in Java 7
+     * the order in which these methods are returned from
+     * Class.getDeclaredMethods (even though there is no order guaranteed) seems
+     * to differ from Java 6. SpringAOP depends on this ordering to determine
+     * advice precedence. By normalizing all advice into Around advice we can
+     * alleviate this issue.
+     *
+     * @param proceedingJoinPoint
+     * @return
+     * @throws java.lang.Throwable
+     */
+    @Around("within(org.apache.nifi.web.dao.ControllerServiceDAO+) && "
+            + "execution(org.apache.nifi.controller.service.ControllerServiceNode createControllerService(org.apache.nifi.web.api.dto.ControllerServiceDTO))")
+    public ControllerServiceNode createControllerServiceAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
+        // update the controller service state
+        ControllerServiceNode controllerService = (ControllerServiceNode) proceedingJoinPoint.proceed();
+
+        // if no exceptions were thrown, add the controller service action...
+        final Action action = generateAuditRecord(controllerService, Operation.Add);
+
+        // save the actions
+        if (action != null) {
+            saveAction(action, logger);
+        }
+
+        return controllerService;
+    }
+
+    /**
+     * Audits the configuration of a single controller service.
+     *
+     * @param proceedingJoinPoint
+     * @param controllerServiceDTO
+     * @param controllerServiceDAO
+     * @return
+     * @throws Throwable
+     */
+    @Around("within(org.apache.nifi.web.dao.ControllerServiceDAO+) && "
+            + "execution(org.apache.nifi.controller.service.ControllerServiceNode updateControllerService(org.apache.nifi.web.api.dto.ControllerServiceDTO)) && "
+            + "args(controllerServiceDTO) && "
+            + "target(controllerServiceDAO)")
+    public Object updateControllerServiceAdvice(ProceedingJoinPoint proceedingJoinPoint, ControllerServiceDTO controllerServiceDTO, ControllerServiceDAO controllerServiceDAO) throws Throwable {
+        // determine the initial values for each property/setting thats changing
+        ControllerServiceNode controllerService = controllerServiceDAO.getControllerService(controllerServiceDTO.getId());
+        final Map<String, String> values = extractConfiguredPropertyValues(controllerService, controllerServiceDTO);
+        final boolean isDisabled = isDisabled(controllerService);
+
+        // update the controller service state
+        final ControllerServiceNode updatedControllerService = (ControllerServiceNode) proceedingJoinPoint.proceed();
+
+        // if no exceptions were thrown, add the controller service action...
+        controllerService = controllerServiceDAO.getControllerService(updatedControllerService.getIdentifier());
+
+        // get the current user
+        NiFiUser user = NiFiUserUtils.getNiFiUser();
+
+        // ensure the user was found
+        if (user != null) {
+            // determine the updated values
+            Map<String, String> updatedValues = extractConfiguredPropertyValues(controllerService, controllerServiceDTO);
+
+            // create the controller service details
+            ExtensionDetails serviceDetails = new ExtensionDetails();
+            serviceDetails.setType(controllerService.getControllerServiceImplementation().getClass().getSimpleName());
+
+            // create a controller service action
+            Date actionTimestamp = new Date();
+            Collection<Action> actions = new ArrayList<>();
+
+            // go through each updated value
+            for (String property : updatedValues.keySet()) {
+                String newValue = updatedValues.get(property);
+                String oldValue = values.get(property);
+                Operation operation = null;
+
+                // determine the type of operation
+                if (oldValue == null || newValue == null || !newValue.equals(oldValue)) {
+                    operation = Operation.Configure;
+                }
+
+                // create a configuration action accordingly
+                if (operation != null) {
+                    // clear the value if this property is sensitive
+                    final PropertyDescriptor propertyDescriptor = controllerService.getControllerServiceImplementation().getPropertyDescriptor(property);
+                    if (propertyDescriptor != null && propertyDescriptor.isSensitive()) {
+                        if (newValue != null) {
+                            newValue = "********";
+                        }
+                        if (oldValue != null) {
+                            oldValue = "********";
+                        }
+                    } else if (ANNOTATION_DATA.equals(property)) {
+                        if (newValue != null) {
+                            newValue = "<annotation data not shown>";
+                        }
+                        if (oldValue != null) {
+                            oldValue = "<annotation data not shown>";
+                        }
+                    }
+
+                    final ConfigureDetails actionDetails = new ConfigureDetails();
+                    actionDetails.setName(property);
+                    actionDetails.setValue(newValue);
+                    actionDetails.setPreviousValue(oldValue);
+
+                    // create a configuration action
+                    Action configurationAction = new Action();
+                    configurationAction.setUserDn(user.getDn());
+                    configurationAction.setUserName(user.getUserName());
+                    configurationAction.setOperation(operation);
+                    configurationAction.setTimestamp(actionTimestamp);
+                    configurationAction.setSourceId(controllerService.getIdentifier());
+                    configurationAction.setSourceName(controllerService.getName());
+                    configurationAction.setSourceType(Component.ControllerService);
+                    configurationAction.setComponentDetails(serviceDetails);
+                    configurationAction.setActionDetails(actionDetails);
+                    actions.add(configurationAction);
+                }
+            }
+
+            // determine the new executing state
+            final boolean updateIsDisabled = isDisabled(updatedControllerService);
+
+            // determine if the running state has changed and its not disabled
+            if (isDisabled != updateIsDisabled) {
+                // create a controller service action
+                Action serviceAction = new Action();
+                serviceAction.setUserDn(user.getDn());
+                serviceAction.setUserName(user.getUserName());
+                serviceAction.setTimestamp(new Date());
+                serviceAction.setSourceId(controllerService.getIdentifier());
+                serviceAction.setSourceName(controllerService.getName());
+                serviceAction.setSourceType(Component.ControllerService);
+                serviceAction.setComponentDetails(serviceDetails);
+
+                // set the operation accordingly
+                if (updateIsDisabled) {
+                    serviceAction.setOperation(Operation.Disable);
+                } else {
+                    serviceAction.setOperation(Operation.Enable);
+                }
+                actions.add(serviceAction);
+            }
+
+            // ensure there are actions to record
+            if (!actions.isEmpty()) {
+                // save the actions
+                saveActions(actions, logger);
+            }
+        }
+
+        return updatedControllerService;
+    }
+
+    /**
+     * Audits the update of a component referencing a controller service.
+     *
+     * @param proceedingJoinPoint
+     * @param controllerServiceId
+     * @return
+     * @throws Throwable
+     */
+    @Around("within(org.apache.nifi.web.dao.ControllerServiceDAO+) && "
+            + "execution(org.apache.nifi.controller.service.ControllerServiceReference updateControllerServiceReferencingComponents(java.lang.String, org.apache.nifi.controller.ScheduledState, org.apache.nifi.controller.service.ControllerServiceState))")
+    public Object updateControllerServiceReferenceAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
+        // update the controller service references
+        final ControllerServiceReference controllerServiceReference = (ControllerServiceReference) proceedingJoinPoint.proceed();
+        
+        // get the current user
+        final NiFiUser user = NiFiUserUtils.getNiFiUser();
+        
+        if (user != null) {
+            final Collection<Action> actions = new ArrayList<>();
+            final Collection<String> visitedServices = new ArrayList<>();
+            visitedServices.add(controllerServiceReference.getReferencedComponent().getIdentifier());
+            
+            // get all applicable actions
+            getUpdateActionsForReferencingComponents(user, actions, visitedServices, controllerServiceReference.getReferencingComponents());
+            
+            // ensure there are actions to record
+            if (!actions.isEmpty()) {
+                // save the actions
+                saveActions(actions, logger);
+            }
+        }
+        
+        return controllerServiceReference;
+    }
+    
+    /**
+     * Gets the update actions for all specified referencing components.
+     * 
+     * @param user
+     * @param actions
+     * @param visitedServices
+     * @param referencingComponents 
+     */
+    private void getUpdateActionsForReferencingComponents(final NiFiUser user, final Collection<Action> actions, final Collection<String> visitedServices, final Set<ConfiguredComponent> referencingComponents) {
+        // consider each component updates
+        for (final ConfiguredComponent component : referencingComponents) {
+            if (component instanceof ProcessorNode) {
+                final ProcessorNode processor = ((ProcessorNode) component);
+
+                // create the processor details
+                ExtensionDetails processorDetails = new ExtensionDetails();
+                processorDetails.setType(processor.getProcessor().getClass().getSimpleName());
+
+                // create a processor action
+                Action processorAction = new Action();
+                processorAction.setUserDn(user.getDn());
+                processorAction.setUserName(user.getUserName());
+                processorAction.setTimestamp(new Date());
+                processorAction.setSourceId(processor.getIdentifier());
+                processorAction.setSourceName(processor.getName());
+                processorAction.setSourceType(Component.Processor);
+                processorAction.setComponentDetails(processorDetails);
+                processorAction.setOperation(ScheduledState.RUNNING.equals(processor.getScheduledState()) ? Operation.Start : Operation.Stop);
+                actions.add(processorAction);
+            } else if (component instanceof ReportingTask) {
+                final ReportingTaskNode reportingTask = ((ReportingTaskNode) component);
+
+                // create the reporting task details
+                ExtensionDetails processorDetails = new ExtensionDetails();
+                processorDetails.setType(reportingTask.getReportingTask().getClass().getSimpleName());
+
+                // create a reporting task action
+                Action reportingTaskAction = new Action();
+                reportingTaskAction.setUserDn(user.getDn());
+                reportingTaskAction.setUserName(user.getUserName());
+                reportingTaskAction.setTimestamp(new Date());
+                reportingTaskAction.setSourceId(reportingTask.getIdentifier());
+                reportingTaskAction.setSourceName(reportingTask.getName());
+                reportingTaskAction.setSourceType(Component.ReportingTask);
+                reportingTaskAction.setComponentDetails(processorDetails);
+                reportingTaskAction.setOperation(ScheduledState.RUNNING.equals(reportingTask.getScheduledState()) ? Operation.Start : Operation.Stop);
+                actions.add(reportingTaskAction);
+            } else if (component instanceof ControllerServiceNode) {
+                final ControllerServiceNode controllerService = ((ControllerServiceNode) component);
+
+                // create the controller service details
+                ExtensionDetails serviceDetails = new ExtensionDetails();
+                serviceDetails.setType(controllerService.getControllerServiceImplementation().getClass().getSimpleName());
+
+                // create a controller service action
+                Action serviceAction = new Action();
+                serviceAction.setUserDn(user.getDn());
+                serviceAction.setUserName(user.getUserName());
+                serviceAction.setTimestamp(new Date());
+                serviceAction.setSourceId(controllerService.getIdentifier());
+                serviceAction.setSourceName(controllerService.getName());
+                serviceAction.setSourceType(Component.ControllerService);
+                serviceAction.setComponentDetails(serviceDetails);
+                serviceAction.setOperation(isDisabled(controllerService) ? Operation.Disable : Operation.Enable);
+                actions.add(serviceAction);
+
+                // need to consider components referencing this controller service (transitive)
+                if (!visitedServices.contains(controllerService.getIdentifier())) {
+                    getUpdateActionsForReferencingComponents(user, actions, visitedServices, controllerService.getReferences().getReferencingComponents());
+                }
+            }
+        }
+    }
+    
+    /**
+     * Audits the removal of a controller service via deleteControllerService().
+     *
+     * @param proceedingJoinPoint
+     * @param controllerServiceId
+     * @param controllerServiceDAO
+     * @throws Throwable
+     */
+    @Around("within(org.apache.nifi.web.dao.ControllerServiceDAO+) && "
+            + "execution(void deleteControllerService(java.lang.String)) && "
+            + "args(controllerServiceId) && "
+            + "target(controllerServiceDAO)")
+    public void removeControllerServiceAdvice(ProceedingJoinPoint proceedingJoinPoint, String controllerServiceId, ControllerServiceDAO controllerServiceDAO) throws Throwable {
+        // get the controller service before removing it
+        ControllerServiceNode controllerService = controllerServiceDAO.getControllerService(controllerServiceId);
+
+        // remove the controller service
+        proceedingJoinPoint.proceed();
+
+        // if no exceptions were thrown, add removal actions...
+        // audit the controller service removal
+        final Action action = generateAuditRecord(controllerService, Operation.Remove);
+
+        // save the actions
+        if (action != null) {
+            saveAction(action, logger);
+        }
+    }
+
+    /**
+     * Generates an audit record for the creation of a controller service.
+     *
+     * @param controllerService
+     * @param operation
+     * @return
+     */
+    private Action generateAuditRecord(ControllerServiceNode controllerService, Operation operation) {
+        return generateAuditRecord(controllerService, operation, null);
+    }
+
+    /**
+     * Generates an audit record for the creation of a controller service.
+     *
+     * @param controllerService
+     * @param operation
+     * @param actionDetails
+     * @return
+     */
+    private Action generateAuditRecord(ControllerServiceNode controllerService, Operation operation, ActionDetails actionDetails) {
+        Action action = null;
+
+        // get the current user
+        NiFiUser user = NiFiUserUtils.getNiFiUser();
+
+        // ensure the user was found
+        if (user != null) {
+            // create the controller service details
+            ExtensionDetails serviceDetails = new ExtensionDetails();
+            serviceDetails.setType(controllerService.getControllerServiceImplementation().getClass().getSimpleName());
+
+            // create the controller service action for adding this controller service
+            action = new Action();
+            action.setUserDn(user.getDn());
+            action.setUserName(user.getUserName());
+            action.setOperation(operation);
+            action.setTimestamp(new Date());
+            action.setSourceId(controllerService.getIdentifier());
+            action.setSourceName(controllerService.getName());
+            action.setSourceType(Component.ControllerService);
+            action.setComponentDetails(serviceDetails);
+
+            if (actionDetails != null) {
+                action.setActionDetails(actionDetails);
+            }
+        }
+
+        return action;
+    }
+
+    /**
+     * Extracts the values for the configured properties from the specified
+     * ControllerService.
+     *
+     * @param controllerService
+     * @param controllerServiceDTO
+     * @return
+     */
+    private Map<String, String> extractConfiguredPropertyValues(ControllerServiceNode controllerService, ControllerServiceDTO controllerServiceDTO) {
+        Map<String, String> values = new HashMap<>();
+
+        if (controllerServiceDTO.getName() != null) {
+            values.put(NAME, controllerService.getName());
+        }
+        if (controllerServiceDTO.getAnnotationData() != null) {
+            values.put(ANNOTATION_DATA, controllerService.getAnnotationData());
+        }
+        if (controllerServiceDTO.getProperties() != null) {
+            // for each property specified, extract its configured value
+            Map<String, String> properties = controllerServiceDTO.getProperties();
+            Map<PropertyDescriptor, String> configuredProperties = controllerService.getProperties();
+            for (String propertyName : properties.keySet()) {
+                // build a descriptor for getting the configured value
+                PropertyDescriptor propertyDescriptor = new PropertyDescriptor.Builder().name(propertyName).build();
+                String configuredPropertyValue = configuredProperties.get(propertyDescriptor);
+
+                // if the configured value couldn't be found, use the default value from the actual descriptor
+                if (configuredPropertyValue == null) {
+                    propertyDescriptor = locatePropertyDescriptor(configuredProperties.keySet(), propertyDescriptor);
+                    configuredPropertyValue = propertyDescriptor.getDefaultValue();
+                }
+                values.put(propertyName, configuredPropertyValue);
+            }
+        }
+        if (controllerServiceDTO.getComments() != null) {
+            values.put(COMMENTS, controllerService.getComments());
+        }
+
+        return values;
+    }
+
+    /**
+     * Locates the actual property descriptor for the given spec property
+     * descriptor.
+     *
+     * @param propertyDescriptors
+     * @param specDescriptor
+     * @return
+     */
+    private PropertyDescriptor locatePropertyDescriptor(Set<PropertyDescriptor> propertyDescriptors, PropertyDescriptor specDescriptor) {
+        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
+            if (propertyDescriptor.equals(specDescriptor)) {
+                return propertyDescriptor;
+            }
+        }
+        return specDescriptor;
+    }
+
+    /**
+     * Returns whether the specified controller service is disabled (or disabling).
+     * 
+     * @param controllerService
+     * @return 
+     */
+    private boolean isDisabled(final ControllerServiceNode controllerService) {
+        return ControllerServiceState.DISABLED.equals(controllerService.getState()) || ControllerServiceState.DISABLING.equals(controllerService.getState());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/FunnelAuditor.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/FunnelAuditor.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/FunnelAuditor.java
index ef66dc6..b079da8 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/FunnelAuditor.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/FunnelAuditor.java
@@ -50,7 +50,7 @@ public class FunnelAuditor extends NiFiAuditor {
      */
     @Around("within(org.apache.nifi.web.dao.FunnelDAO+) && "
             + "execution(org.apache.nifi.connectable.Funnel createFunnel(java.lang.String, org.apache.nifi.web.api.dto.FunnelDTO))")
-    public Object createFunnelAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
+    public Funnel createFunnelAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
         // perform the underlying operation
         Funnel funnel = (Funnel) proceedingJoinPoint.proceed();
 
@@ -69,6 +69,7 @@ public class FunnelAuditor extends NiFiAuditor {
      * Audits the removal of a funnel.
      *
      * @param proceedingJoinPoint
+     * @param groupId
      * @param funnelId
      * @param funnelDAO
      * @throws Throwable
@@ -97,6 +98,8 @@ public class FunnelAuditor extends NiFiAuditor {
      * Generates an audit record for the creation of the specified funnel.
      *
      * @param funnel
+     * @param operation
+     * @return 
      */
     public Action generateAuditRecord(Funnel funnel, Operation operation) {
         return generateAuditRecord(funnel, operation, null);
@@ -106,6 +109,9 @@ public class FunnelAuditor extends NiFiAuditor {
      * Generates an audit record for the creation of the specified funnel.
      *
      * @param funnel
+     * @param operation
+     * @param actionDetails
+     * @return 
      */
     public Action generateAuditRecord(Funnel funnel, Operation operation, ActionDetails actionDetails) {
         Action action = null;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/NiFiAuditor.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/NiFiAuditor.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/NiFiAuditor.java
index 046d5ff..adff9d1 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/NiFiAuditor.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/NiFiAuditor.java
@@ -54,14 +54,13 @@ public abstract class NiFiAuditor {
      * Records the actions.
      *
      * @param actions
+     * @param logger
      */
     protected void saveActions(Collection<Action> actions, Logger logger) {
-        /*
-         * if we're a clustered node, then set actions on threadlocal
-         */
-        if (serviceFacade.isClustered()) {
-            // if we're a connected node, then put audit actions on threadlocal to propagate back to manager
-            ClusterContext ctx = ClusterContextThreadLocal.getContext();
+        ClusterContext ctx = ClusterContextThreadLocal.getContext();
+        
+        // if we're a connected node, then put audit actions on threadlocal to propagate back to manager
+        if (ctx != null) {
             ctx.getActions().addAll(actions);
         } else {
             // if we're the cluster manager, or a disconnected node, or running standalone, then audit actions

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/PortAuditor.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/PortAuditor.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/PortAuditor.java
index 58dcc39..af4b5bd 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/PortAuditor.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/PortAuditor.java
@@ -60,7 +60,7 @@ public class PortAuditor extends NiFiAuditor {
      */
     @Around("within(org.apache.nifi.web.dao.PortDAO+) && "
             + "execution(org.apache.nifi.connectable.Port createPort(java.lang.String, org.apache.nifi.web.api.dto.PortDTO))")
-    public Object createPortAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
+    public Port createPortAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
         // perform the underlying operation
         Port port = (Port) proceedingJoinPoint.proceed();
 
@@ -79,6 +79,9 @@ public class PortAuditor extends NiFiAuditor {
      * Audits the update of a port.
      *
      * @param proceedingJoinPoint
+     * @param groupId
+     * @param portDTO
+     * @param portDAO
      * @return
      * @throws Throwable
      */
@@ -86,7 +89,7 @@ public class PortAuditor extends NiFiAuditor {
             + "execution(org.apache.nifi.connectable.Port updatePort(java.lang.String, org.apache.nifi.web.api.dto.PortDTO)) && "
             + "args(groupId, portDTO) && "
             + "target(portDAO)")
-    public Object updatePortAdvice(ProceedingJoinPoint proceedingJoinPoint, String groupId, PortDTO portDTO, PortDAO portDAO) throws Throwable {
+    public Port updatePortAdvice(ProceedingJoinPoint proceedingJoinPoint, String groupId, PortDTO portDTO, PortDAO portDAO) throws Throwable {
         final Port port = portDAO.getPort(groupId, portDTO.getId());
         final ScheduledState scheduledState = port.getScheduledState();
         final String name = port.getName();
@@ -261,8 +264,9 @@ public class PortAuditor extends NiFiAuditor {
      * Audits the removal of a processor via deleteProcessor().
      *
      * @param proceedingJoinPoint
-     * @param processorId
-     * @param processorDAO
+     * @param groupId
+     * @param portId
+     * @param portDAO
      * @throws Throwable
      */
     @Around("within(org.apache.nifi.web.dao.PortDAO+) && "
@@ -290,6 +294,8 @@ public class PortAuditor extends NiFiAuditor {
      * Generates an audit record for the creation of the specified port.
      *
      * @param port
+     * @param operation
+     * @return 
      */
     public Action generateAuditRecord(Port port, Operation operation) {
         return generateAuditRecord(port, operation, null);
@@ -299,6 +305,9 @@ public class PortAuditor extends NiFiAuditor {
      * Generates an audit record for the creation of the specified port.
      *
      * @param port
+     * @param operation
+     * @param actionDetails
+     * @return 
      */
     public Action generateAuditRecord(Port port, Operation operation, ActionDetails actionDetails) {
         Action action = null;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java
index 1b2af7d..4391881 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java
@@ -55,15 +55,12 @@ public class ProcessGroupAuditor extends NiFiAuditor {
      * alleviate this issue.
      *
      * @param proceedingJoinPoint
+     * @return 
+     * @throws java.lang.Throwable
      */
-//    @AfterReturning(
-//        pointcut="within(org.apache.nifi.web.dao.ProcessGroupDAO+) && "
-//            + "execution(org.apache.nifi.web.api.dto.ProcessGroupDTO createProcessGroup(java.lang.String, org.apache.nifi.web.api.dto.ProcessGroupDTO))",
-//        returning="processGroup"
-//    )
     @Around("within(org.apache.nifi.web.dao.ProcessGroupDAO+) && "
             + "execution(org.apache.nifi.groups.ProcessGroup createProcessGroup(java.lang.String, org.apache.nifi.web.api.dto.ProcessGroupDTO))")
-    public Object createProcessGroupAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
+    public ProcessGroup createProcessGroupAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
         // create the process group
         ProcessGroup processGroup = (ProcessGroup) proceedingJoinPoint.proceed();
 
@@ -83,13 +80,14 @@ public class ProcessGroupAuditor extends NiFiAuditor {
      * Audits the update of process group configuration.
      *
      * @param proceedingJoinPoint
+     * @param processGroupDTO
      * @return
      * @throws Throwable
      */
     @Around("within(org.apache.nifi.web.dao.ProcessGroupDAO+) && "
             + "execution(org.apache.nifi.groups.ProcessGroup updateProcessGroup(org.apache.nifi.web.api.dto.ProcessGroupDTO)) && "
             + "args(processGroupDTO)")
-    public Object updateProcessGroupAdvice(ProceedingJoinPoint proceedingJoinPoint, ProcessGroupDTO processGroupDTO) throws Throwable {
+    public ProcessGroup updateProcessGroupAdvice(ProceedingJoinPoint proceedingJoinPoint, ProcessGroupDTO processGroupDTO) throws Throwable {
         ProcessGroupDAO processGroupDAO = getProcessGroupDAO();
         ProcessGroup processGroup = processGroupDAO.getProcessGroup(processGroupDTO.getId());
 
@@ -170,7 +168,7 @@ public class ProcessGroupAuditor extends NiFiAuditor {
                 processGroupAction.setTimestamp(new Date());
 
                 // determine the running state
-                if (processGroupDTO.isRunning().booleanValue()) {
+                if (processGroupDTO.isRunning()) {
                     processGroupAction.setOperation(Operation.Start);
                 } else {
                     processGroupAction.setOperation(Operation.Stop);
@@ -194,7 +192,6 @@ public class ProcessGroupAuditor extends NiFiAuditor {
      *
      * @param proceedingJoinPoint
      * @param groupId
-     * @param processGroupDAO
      * @throws Throwable
      */
     @Around("within(org.apache.nifi.web.dao.ProcessGroupDAO+) && "
@@ -222,6 +219,7 @@ public class ProcessGroupAuditor extends NiFiAuditor {
      * Generates an audit record for the creation of a process group.
      *
      * @param processGroup
+     * @param operation
      * @return
      */
     public Action generateAuditRecord(ProcessGroup processGroup, Operation operation) {
@@ -232,6 +230,8 @@ public class ProcessGroupAuditor extends NiFiAuditor {
      * Generates an audit record for the creation of a process group.
      *
      * @param processGroup
+     * @param operation
+     * @param actionDetails
      * @return
      */
     public Action generateAuditRecord(ProcessGroup processGroup, Operation operation, ActionDetails actionDetails) {


[27/62] [abbrv] incubator-nifi git commit: NIFI-480: - Addressing issues caused by canceling a dialog by pressing escape. Previously pressing escape would cause too many dialogs to close. This could lead to potential configuration loss if there was other

Posted by ma...@apache.org.
NIFI-480:
- Addressing issues caused by canceling a dialog by pressing escape. Previously pressing escape would cause too many dialogs to close. This could lead to potential configuration loss if there was other unsaved changes.

Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/30fc75e2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/30fc75e2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/30fc75e2

Branch: refs/heads/NIFI-25
Commit: 30fc75e2b8a3a8cf0a36d66d5ee3eed6bc9dedb7
Parents: 4752e28
Author: Matt Gilman <ma...@gmail.com>
Authored: Wed Apr 1 09:42:33 2015 -0400
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Apr 1 09:42:33 2015 -0400

----------------------------------------------------------------------
 .../main/webapp/js/jquery/modal/jquery.modal.js |  2 +-
 .../propertytable/jquery.propertytable.js       | 15 +---
 .../src/main/webapp/js/nf/canvas/nf-actions.js  | 14 ----
 .../main/webapp/js/nf/canvas/nf-canvas-utils.js |  9 +++
 .../src/main/webapp/js/nf/canvas/nf-canvas.js   | 80 ++++++++++++++++++--
 5 files changed, 86 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/30fc75e2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/modal/jquery.modal.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/modal/jquery.modal.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/modal/jquery.modal.js
index 9faaefd..00b240c 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/modal/jquery.modal.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/modal/jquery.modal.js
@@ -90,7 +90,7 @@
                 if (isDefinedAndNotNull(options)) {
 
                     // get the combo
-                    var dialog = $(this).addClass('dialog cancellable');
+                    var dialog = $(this).addClass('dialog cancellable modal');
                     var dialogHeaderText = $('<span class="dialog-header-text"></span>');
                     var dialogHeader = $('<div class="dialog-header"></div>').append(dialogHeaderText);
 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/30fc75e2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
index 1c25460..c2672f3 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
@@ -58,7 +58,7 @@
             previousValue = args.item[args.column.field];
 
             // create the wrapper
-            wrapper = $('<div></div>').css({
+            wrapper = $('<div></div>').addClass('slickgrid-editor').css({
                 'z-index': 100000,
                 'position': 'absolute',
                 'background': 'white',
@@ -598,7 +598,7 @@
      */
     var showPropertyValue = function (propertyGrid, descriptors, row, cell) {
         // remove any currently open detail dialogs
-        removeAllPropertyDetailDialogs();
+        nf.CanvasUtils.removeAllPropertyDetailDialogs();
 
         // get the property in question
         var propertyData = propertyGrid.getData();
@@ -694,13 +694,6 @@
         }
     };
 
-    /**
-     * Removes all currently open property detail dialogs.
-     */
-    var removeAllPropertyDetailDialogs = function () {
-        $('body').children('div.property-detail').hide().remove();
-    };
-
     var initPropertiesTable = function (table, options) {
         // function for formatting the property name
         var nameFormatter = function (row, cell, value, columnDef, dataContext) {
@@ -1029,7 +1022,7 @@
     var clear = function (propertyTableContainer) {
         var options = propertyTableContainer.data('options');
         if (options.readOnly === true) {
-            removeAllPropertyDetailDialogs();
+            nf.CanvasUtils.removeAllPropertyDetailDialogs();
         } else {
             // clear any existing new property dialogs
             if (nf.Common.isDefinedAndNotNull(options.newPropertyDialogContainer)) {
@@ -1077,7 +1070,7 @@
                     if (options.readOnly !== true && nf.Common.isDefinedAndNotNull(options.newPropertyDialogContainer)) {
                         // build the new property dialog
                         var newPropertyDialogMarkup = 
-                                '<div class="new-property-dialog dialog">' +
+                                '<div class="new-property-dialog dialog cancellable">' +
                                     '<div>' +
                                         '<div class="setting-name">Property name</div>' +
                                         '<div class="setting-field new-property-name-container">' +

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/30fc75e2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
index 47532ad..4d2da84 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
@@ -679,20 +679,6 @@ nf.Actions = (function () {
         },
         
         /**
-         * Hides and open cancellable dialogs.
-         */
-        hideDialogs: function () {
-            // ensure all cancellable dialogs are closed
-            var cancellable = $('.cancellable');
-            $.each(cancellable, function () {
-                // if this dialog is open, close it 
-                if ($(this).is(':visible')) {
-                    $(this).modal('hide');
-                }
-            });
-        },
-        
-        /**
          * Reloads the status for the entire canvas (components and flow.)
          */
         reloadStatus: function () {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/30fc75e2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
index f1cb458..9460fa6 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
@@ -871,6 +871,15 @@ nf.CanvasUtils = (function () {
         },
         
         /**
+         * Removes all read only property detail dialogs.
+         */
+        removeAllPropertyDetailDialogs: function () {
+            var propertyDetails = $('body').children('div.property-detail');
+            propertyDetails.find('div.nfel-editor').nfeditor('destroy');
+            propertyDetails.hide().remove();
+        },
+        
+        /**
          * Persists the current user view.
          */
         persistUserView: function () {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/30fc75e2/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
index 626ba34..82f455b 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
@@ -538,18 +538,82 @@ nf.Canvas = (function () {
             // consider escape, before checking dialogs
             if (!isCtrl && evt.keyCode === 27) {
                 // esc
-                var target = $(evt.target);
-                if (target.length) {
-                    if (target.get(0) === $('#canvas-body').get(0)) {
-                        nf.Actions.hideDialogs();
-                    } else {
-                        target.closest('.cancellable.dialog:visible').modal('hide');
-                    }
+
+                // prevent escape when a property value is being edited and it is unable to close itself 
+                // (due to focus loss on field) - allowing this to continue would could cause other
+                // unsaved changes to be lost as it would end up cancel the entire configuration dialog
+                // not just the field itself
+                if ($('div.value-combo').is(':visible') || $('div.slickgrid-nfel-editor').is(':visible') || $('div.slickgrid-editor').is(':visible')) {
+                    return;
+                }
+
+                // first consider read only property detail dialog
+                if ($('div.property-detail').is(':visible')) {
+                    nf.CanvasUtils.removeAllPropertyDetailDialogs();
                     
+                    // prevent further bubbling as we're already handled it
                     evt.stopPropagation();
                     evt.preventDefault();
-                }
+                } else {
+                    var target = $(evt.target);
+                    if (target.length) {
+                        var isBody = target.get(0) === $('#canvas-body').get(0);
+                        var inShell = target.closest('#shell-dialog').length;
+
+                        // special handling for body and shell
+                        if (isBody || inShell) {
+                            var cancellables = $('.cancellable');
+                            if (cancellables.length) {
+                                var zIndexMax = null;
+                                var dialogMax = null;
+
+                                // identify the top most cancellable
+                                $.each(cancellables, function(_, cancellable) {
+                                    var dialog = $(cancellable);
+                                    var zIndex = dialog.css('zIndex');
+
+                                    // if the dialog has a zIndex consider it
+                                    if (dialog.is(':visible') && nf.Common.isDefinedAndNotNull(zIndex)) {
+                                        zIndex = parseInt(zIndex, 10);
+                                        if (zIndexMax === null || zIndex > zIndexMax) {
+                                            zIndexMax = zIndex;
+                                            dialogMax = dialog;
+                                        }
+                                    }
+                                });
+
+                                // if we've identified a dialog to close do so and stop propagation
+                                if (dialogMax !== null) {
+                                    // hide the cancellable
+                                    if (dialogMax.hasClass('modal')) {
+                                        dialogMax.modal('hide');
+                                    } else {
+                                        dialogMax.hide();
+                                    }
+
+                                    // prevent further bubbling as we're already handled it
+                                    evt.stopPropagation();
+                                    evt.preventDefault();
+                                }
+                            }
+                        } else {
+                            // otherwise close the closest visible cancellable
+                            var parentDialog = target.closest('.cancellable:visible').first();
+                            if (parentDialog.length) {
+                                if (parentDialog.hasClass('modal')) {
+                                    parentDialog.modal('hide');
+                                } else {
+                                    parentDialog.hide();
+                                }
 
+                                // prevent further bubbling as we're already handled it
+                                evt.stopPropagation();
+                                evt.preventDefault();
+                            }
+                        }
+                    }
+                }
+                
                 return;
             }
             


[35/62] [abbrv] incubator-nifi git commit: NIFI-486: Fixed NPE that occurs if processor allows for controller service as optional property and no value set

Posted by ma...@apache.org.
NIFI-486: Fixed NPE that occurs if processor allows for controller service as optional property and no value set

Signed-off-by: joewitt <jo...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/3a2b12d7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/3a2b12d7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/3a2b12d7

Branch: refs/heads/NIFI-25
Commit: 3a2b12d7c9919c8f3be2a01125ad60ca22fecc03
Parents: 4a16845
Author: Mark Payne <ma...@hotmail.com>
Authored: Mon Apr 6 17:59:07 2015 -0400
Committer: joewitt <jo...@apache.org>
Committed: Mon Apr 6 21:13:30 2015 -0400

----------------------------------------------------------------------
 .../nifi/controller/scheduling/StandardProcessScheduler.java     | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/3a2b12d7/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java
index 89850cc..43e05dd 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java
@@ -297,7 +297,9 @@ public final class StandardProcessScheduler implements ProcessScheduler {
                         final Class<? extends ControllerService> serviceDefinition = descriptor.getControllerServiceDefinition();
                         if ( serviceDefinition != null ) {
                             final String serviceId = processContext.getProperty(descriptor).getValue();
-                            serviceIds.add(serviceId);
+                            if ( serviceId != null ) {
+                            	serviceIds.add(serviceId);
+                            }
                         }
                     }
                     


[30/62] [abbrv] incubator-nifi git commit: NIFI-481: - Enabling enter to add a new property. - Autoselecting and scrolling to new property rows.

Posted by ma...@apache.org.
NIFI-481:
- Enabling enter to add a new property.
- Autoselecting and scrolling to new property rows.

Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/6bbd1723
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/6bbd1723
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/6bbd1723

Branch: refs/heads/NIFI-25
Commit: 6bbd172399f9a9966c666bb51ea3ff09affbcd11
Parents: 2c35226
Author: Matt Gilman <ma...@gmail.com>
Authored: Thu Apr 2 22:34:34 2015 -0400
Committer: Matt Gilman <ma...@gmail.com>
Committed: Thu Apr 2 22:34:34 2015 -0400

----------------------------------------------------------------------
 .../js/jquery/propertytable/jquery.propertytable.js | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/6bbd1723/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
index 31495bb..bbafc60 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
@@ -1159,8 +1159,9 @@
                                 // add a row for the new property
                                 var propertyGrid = table.data('gridInstance');
                                 var propertyData = propertyGrid.getData();
+                                var id = propertyData.getLength(); 
                                 propertyData.addItem({
-                                    id: propertyData.getLength(),
+                                    id: id,
                                     hidden: false,
                                     property: propertyName,
                                     displayName: propertyName,
@@ -1168,6 +1169,11 @@
                                     value: null,
                                     type: 'userDefined'
                                 });
+                                
+                                // select the new properties row
+                                var row = propertyData.getRowById(id);
+                                propertyGrid.setSelectedRows([row]);
+                                propertyGrid.scrollRowIntoView(row);
                             } else {
                                 nf.Dialog.showOkDialog({
                                     dialogContent: 'Property name must be specified.',
@@ -1182,6 +1188,14 @@
                         var cancel = function () {
                             newPropertyDialog.hide();
                         };
+                        
+                        // enable enter to add
+                        newPropertyNameField.on('keydown', function (e) {
+                            var code = e.keyCode ? e.keyCode : e.which;
+                            if (code === $.ui.keyCode.ENTER) {
+                                add();
+                            }
+                        });
 
                         // make the new property dialog draggable
                         newPropertyDialog.draggable({


[22/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ReportingTaskTypesEntity.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ReportingTaskTypesEntity.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ReportingTaskTypesEntity.java
new file mode 100644
index 0000000..4b021ef
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ReportingTaskTypesEntity.java
@@ -0,0 +1,46 @@
+/*
+ * 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.web.api.entity;
+
+import java.util.Set;
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.nifi.web.api.dto.DocumentedTypeDTO;
+
+/**
+ * A serialized representation of this class can be placed in the entity body of
+ * a response to the API. This particular entity holds a reference to a list of
+ * reporting task types.
+ */
+@XmlRootElement(name = "reportingTaskTypesEntity")
+public class ReportingTaskTypesEntity extends Entity {
+
+    private Set<DocumentedTypeDTO> reportingTaskTypes;
+
+    /**
+     * The list of reporting task types that are being serialized.
+     *
+     * @return
+     */
+    public Set<DocumentedTypeDTO> getReportingTaskTypes() {
+        return reportingTaskTypes;
+    }
+
+    public void setReportingTaskTypes(Set<DocumentedTypeDTO> reportingTaskTypes) {
+        this.reportingTaskTypes = reportingTaskTypes;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ReportingTasksEntity.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ReportingTasksEntity.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ReportingTasksEntity.java
new file mode 100644
index 0000000..4699d5d
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ReportingTasksEntity.java
@@ -0,0 +1,46 @@
+/*
+ * 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.web.api.entity;
+
+import java.util.Set;
+import javax.xml.bind.annotation.XmlRootElement;
+import org.apache.nifi.web.api.dto.ReportingTaskDTO;
+
+/**
+ * A serialized representation of this class can be placed in the entity body of
+ * a response to the API. This particular entity holds a reference to a list of
+ * reporting tasks.
+ */
+@XmlRootElement(name = "reportingTasksEntity")
+public class ReportingTasksEntity extends Entity {
+
+    private Set<ReportingTaskDTO> reportingTasks;
+
+    /**
+     * The list of reporting tasks that are being serialized.
+     *
+     * @return
+     */
+    public Set<ReportingTaskDTO> getReportingTasks() {
+        return reportingTasks;
+    }
+
+    public void setReportingTasks(Set<ReportingTaskDTO> reportingTasks) {
+        this.reportingTasks = reportingTasks;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java
index fcd3ea3..69ce8d9 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java
@@ -18,6 +18,7 @@ package org.apache.nifi.documentation.mock;
 
 import org.apache.nifi.controller.ControllerServiceInitializationContext;
 import org.apache.nifi.controller.ControllerServiceLookup;
+import org.apache.nifi.logging.ComponentLog;
 
 /**
  * A Mock ControllerServiceInitializationContext so that ControllerServices can
@@ -37,4 +38,9 @@ public class MockControllerServiceInitializationContext implements ControllerSer
         return new MockControllerServiceLookup();
     }
 
+    @Override
+    public ComponentLog getLogger() {
+        return null;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java
index f11bc68..5c60881 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java
@@ -52,4 +52,14 @@ public class MockControllerServiceLookup implements ControllerServiceLookup {
         return Collections.emptySet();
     }
 
+    @Override
+    public boolean isControllerServiceEnabling(String serviceIdentifier) {
+        return false;
+    }
+
+    @Override
+    public String getControllerServiceName(String serviceIdentifier) {
+        return serviceIdentifier;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java
index 910ce5a..dc6e236 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java
@@ -19,6 +19,7 @@ package org.apache.nifi.documentation.mock;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.nifi.controller.ControllerServiceLookup;
+import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.reporting.ReportingInitializationContext;
 import org.apache.nifi.scheduling.SchedulingStrategy;
 
@@ -26,8 +27,6 @@ import org.apache.nifi.scheduling.SchedulingStrategy;
  * A Mock ReportingInitializationContext that can be used to initialize a
  * ReportingTask for the purposes of documentation generation.
  *
- * @author Alligator
- *
  */
 public class MockReportingInitializationContext implements ReportingInitializationContext {
 
@@ -60,4 +59,9 @@ public class MockReportingInitializationContext implements ReportingInitializati
     public SchedulingStrategy getSchedulingStrategy() {
         return SchedulingStrategy.TIMER_DRIVEN;
     }
+
+    @Override
+    public ComponentLog getLogger() {
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-web/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-web/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-web/pom.xml
index f9ee703..70dcc81 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-web/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-web/pom.xml
@@ -34,10 +34,6 @@
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-web-optimistic-locking</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-administration</artifactId>
         </dependency>
         <dependency>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-web/src/main/java/org/apache/nifi/cluster/context/ClusterContextThreadLocal.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-web/src/main/java/org/apache/nifi/cluster/context/ClusterContextThreadLocal.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-web/src/main/java/org/apache/nifi/cluster/context/ClusterContextThreadLocal.java
index 012e7c7..c8c7206 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-web/src/main/java/org/apache/nifi/cluster/context/ClusterContextThreadLocal.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-web/src/main/java/org/apache/nifi/cluster/context/ClusterContextThreadLocal.java
@@ -32,12 +32,7 @@ public class ClusterContextThreadLocal {
     }
     
     public static ClusterContext getContext() {
-        ClusterContext ctx = contextHolder.get();
-        if(ctx == null) {
-            ctx = createEmptyContext();
-            contextHolder.set(ctx);
-        }
-        return ctx;
+        return contextHolder.get();
     }
     
     public static void setContext(final ClusterContext context) {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-web/src/main/java/org/apache/nifi/web/ClusterAwareOptimisticLockingManager.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-web/src/main/java/org/apache/nifi/web/ClusterAwareOptimisticLockingManager.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-web/src/main/java/org/apache/nifi/web/ClusterAwareOptimisticLockingManager.java
deleted file mode 100644
index 90b8a37..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-web/src/main/java/org/apache/nifi/web/ClusterAwareOptimisticLockingManager.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.web;
-
-import org.apache.nifi.cluster.context.ClusterContext;
-import org.apache.nifi.cluster.context.ClusterContextThreadLocal;
-
-/**
- * An optimistic locking manager that provides for optimistic locking in a clustered
- * environment.
- * 
- * @author unattributed
- */
-public class ClusterAwareOptimisticLockingManager implements OptimisticLockingManager {
-
-    private final OptimisticLockingManager optimisticLockingManager;
-    
-    public ClusterAwareOptimisticLockingManager(final OptimisticLockingManager optimisticLockingManager) {
-        this.optimisticLockingManager = optimisticLockingManager;
-    }
-    
-    @Override
-    public Revision checkRevision(Revision revision) throws InvalidRevisionException {
-        final Revision currentRevision = getRevision();
-        if(currentRevision.equals(revision) == false) {
-            throw new InvalidRevisionException(String.format("Given revision %s does not match current revision %s.", revision, currentRevision));
-        } else {
-            return revision.increment(revision.getClientId());
-        }
-    }
-
-    @Override
-    public boolean isCurrent(Revision revision) {
-        return getRevision().equals(revision);
-    }
-
-    @Override
-    public Revision getRevision() {
-        final ClusterContext ctx = ClusterContextThreadLocal.getContext();
-        if(ctx == null || ctx.getRevision() == null) {
-            return optimisticLockingManager.getRevision();
-        } else {
-            return ctx.getRevision();
-        }
-    }
-
-    @Override
-    public void setRevision(final Revision revision) {
-        final ClusterContext ctx = ClusterContextThreadLocal.getContext();
-        if(ctx != null) {
-            ctx.setRevision(revision);
-        }
-        optimisticLockingManager.setRevision(revision);
-    }
-
-    @Override
-    public Revision incrementRevision() {
-        final Revision currentRevision = getRevision();
-        final Revision incRevision = currentRevision.increment();
-        setRevision(incRevision);
-        return incRevision;
-    }
-
-    @Override
-    public Revision incrementRevision(final String clientId) {
-        final Revision currentRevision = getRevision();
-        final Revision incRevision = currentRevision.increment(clientId);
-        setRevision(incRevision);
-        return incRevision;
-    }
-
-    @Override
-    public String getLastModifier() {
-        return optimisticLockingManager.getLastModifier();
-    }
-
-    @Override
-    public void setLastModifier(final String lastModifier) {
-        optimisticLockingManager.setLastModifier(lastModifier);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/pom.xml
index 7b6a418..bdff00f 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/pom.xml
@@ -47,6 +47,10 @@
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-web-optimistic-locking</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-framework-core</artifactId>
         </dependency>
         <dependency>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/ClusterDataFlow.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/ClusterDataFlow.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/ClusterDataFlow.java
index eedb88f..c17b429 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/ClusterDataFlow.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/ClusterDataFlow.java
@@ -27,14 +27,25 @@ import org.apache.nifi.cluster.protocol.StandardDataFlow;
 public class ClusterDataFlow {
 
     private final StandardDataFlow dataFlow;
-
     private final NodeIdentifier primaryNodeId;
+    private final byte[] controllerServices;
+    private final byte[] reportingTasks;
 
-    public ClusterDataFlow(final StandardDataFlow dataFlow, final NodeIdentifier primaryNodeId) {
+    public ClusterDataFlow(final StandardDataFlow dataFlow, final NodeIdentifier primaryNodeId, final byte[] controllerServices, final byte[] reportingTasks) {
         this.dataFlow = dataFlow;
         this.primaryNodeId = primaryNodeId;
+        this.controllerServices = controllerServices;
+        this.reportingTasks = reportingTasks;
     }
 
+    public byte[] getControllerServices() {
+    	return controllerServices;
+    }
+    
+    public byte[] getReportingTasks() {
+    	return reportingTasks;
+    }
+    
     public NodeIdentifier getPrimaryNodeId() {
         return primaryNodeId;
     }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/DataFlowManagementService.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/DataFlowManagementService.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/DataFlowManagementService.java
index 339d904..082d65e 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/DataFlowManagementService.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/DataFlowManagementService.java
@@ -17,6 +17,7 @@
 package org.apache.nifi.cluster.flow;
 
 import java.util.Set;
+
 import org.apache.nifi.cluster.protocol.NodeIdentifier;
 
 /**
@@ -67,6 +68,22 @@ public interface DataFlowManagementService {
     void updatePrimaryNode(NodeIdentifier nodeId) throws DaoException;
 
     /**
+     * Updates the dataflow with the given serialized form of the Controller Services that are to exist on the NCM.
+     * 
+     * @param serializedControllerServices
+     * @throws DaoException
+     */
+    void updateControllerServices(byte[] serializedControllerServices) throws DaoException;
+    
+    /**
+     * Updates the dataflow with the given serialized form of Reporting Tasks that are to exist on the NCM.
+     * 
+     * @param serviceNodes
+     * @throws DaoException
+     */
+    void updateReportingTasks(byte[] serializedReportingTasks) throws DaoException;
+    
+    /**
      * Sets the state of the flow.
      *
      * @param flowState the state

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/impl/DataFlowDaoImpl.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/impl/DataFlowDaoImpl.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/impl/DataFlowDaoImpl.java
index 72b594a..dd9d2a3 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/impl/DataFlowDaoImpl.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/impl/DataFlowDaoImpl.java
@@ -111,6 +111,8 @@ public class DataFlowDaoImpl implements DataFlowDao {
     public static final String FLOW_XML_FILENAME = "flow.xml";
     public static final String TEMPLATES_FILENAME = "templates.xml";
     public static final String SNIPPETS_FILENAME = "snippets.xml";
+    public static final String CONTROLLER_SERVICES_FILENAME = "controller-services.xml";
+    public static final String REPORTING_TASKS_FILENAME = "reporting-tasks.xml";
     public static final String CLUSTER_INFO_FILENAME = "cluster-info.xml";
 
     private static final Logger logger = new NiFiLog(LoggerFactory.getLogger(DataFlowDaoImpl.class));
@@ -408,13 +410,7 @@ public class DataFlowDaoImpl implements DataFlowDao {
         final File stateFile = new File(dir, FLOW_PACKAGE);
         stateFile.createNewFile();
 
-        final byte[] flowBytes = getEmptyFlowBytes();
-        final byte[] templateBytes = new byte[0];
-        final byte[] snippetBytes = new byte[0];
-        final DataFlow dataFlow = new StandardDataFlow(flowBytes, templateBytes, snippetBytes);
-
-        final ClusterMetadata clusterMetadata = new ClusterMetadata();
-        writeDataFlow(stateFile, dataFlow, clusterMetadata);
+        writeDataFlow(stateFile, new ClusterDataFlow(null, null, new byte[0], new byte[0]), new ClusterMetadata());
 
         return stateFile;
     }
@@ -479,7 +475,9 @@ public class DataFlowDaoImpl implements DataFlowDao {
         byte[] templateBytes = new byte[0];
         byte[] snippetBytes = new byte[0];
         byte[] clusterInfoBytes = new byte[0];
-
+        byte[] controllerServiceBytes = new byte[0];
+        byte[] reportingTaskBytes = new byte[0];
+        
         try (final InputStream inStream = new FileInputStream(file);
                 final TarArchiveInputStream tarIn = new TarArchiveInputStream(new BufferedInputStream(inStream))) {
             TarArchiveEntry tarEntry;
@@ -501,6 +499,14 @@ public class DataFlowDaoImpl implements DataFlowDao {
                         clusterInfoBytes = new byte[(int) tarEntry.getSize()];
                         StreamUtils.fillBuffer(tarIn, clusterInfoBytes, true);
                         break;
+                    case CONTROLLER_SERVICES_FILENAME:
+                    	controllerServiceBytes = new byte[(int) tarEntry.getSize()];
+                    	StreamUtils.fillBuffer(tarIn, controllerServiceBytes, true);
+                    	break;
+                    case REPORTING_TASKS_FILENAME:
+                    	reportingTaskBytes = new byte[(int) tarEntry.getSize()];
+                    	StreamUtils.fillBuffer(tarIn, reportingTaskBytes, true);
+                    	break;
                     default:
                         throw new DaoException("Found Unexpected file in dataflow configuration: " + tarEntry.getName());
                 }
@@ -518,7 +524,7 @@ public class DataFlowDaoImpl implements DataFlowDao {
         final StandardDataFlow dataFlow = new StandardDataFlow(flowBytes, templateBytes, snippetBytes);
         dataFlow.setAutoStartProcessors(autoStart);
 
-        return new ClusterDataFlow(dataFlow, (clusterMetadata == null) ? null : clusterMetadata.getPrimaryNodeId());
+        return new ClusterDataFlow(dataFlow, (clusterMetadata == null) ? null : clusterMetadata.getPrimaryNodeId(), controllerServiceBytes, reportingTaskBytes);
     }
 
     private void writeDataFlow(final File file, final ClusterDataFlow clusterDataFlow) throws IOException, JAXBException {
@@ -536,7 +542,7 @@ public class DataFlowDaoImpl implements DataFlowDao {
         clusterMetadata.setPrimaryNodeId(clusterDataFlow.getPrimaryNodeId());
 
         // write to disk
-        writeDataFlow(file, dataFlow, clusterMetadata);
+        writeDataFlow(file, clusterDataFlow, clusterMetadata);
     }
 
     private void writeTarEntry(final TarArchiveOutputStream tarOut, final String filename, final byte[] bytes) throws IOException {
@@ -547,14 +553,23 @@ public class DataFlowDaoImpl implements DataFlowDao {
         tarOut.closeArchiveEntry();
     }
 
-    private void writeDataFlow(final File file, final DataFlow dataFlow, final ClusterMetadata clusterMetadata) throws IOException, JAXBException {
+    private void writeDataFlow(final File file, final ClusterDataFlow clusterDataFlow, final ClusterMetadata clusterMetadata) throws IOException, JAXBException {
 
         try (final OutputStream fos = new FileOutputStream(file);
                 final TarArchiveOutputStream tarOut = new TarArchiveOutputStream(new BufferedOutputStream(fos))) {
 
-            writeTarEntry(tarOut, FLOW_XML_FILENAME, dataFlow.getFlow());
-            writeTarEntry(tarOut, TEMPLATES_FILENAME, dataFlow.getTemplates());
-            writeTarEntry(tarOut, SNIPPETS_FILENAME, dataFlow.getSnippets());
+            final DataFlow dataFlow = clusterDataFlow.getDataFlow();
+            if ( dataFlow == null ) {
+                writeTarEntry(tarOut, FLOW_XML_FILENAME, getEmptyFlowBytes());
+                writeTarEntry(tarOut, TEMPLATES_FILENAME, new byte[0]);
+                writeTarEntry(tarOut, SNIPPETS_FILENAME, new byte[0]);
+            } else {
+                writeTarEntry(tarOut, FLOW_XML_FILENAME, dataFlow.getFlow());
+                writeTarEntry(tarOut, TEMPLATES_FILENAME, dataFlow.getTemplates());
+                writeTarEntry(tarOut, SNIPPETS_FILENAME, dataFlow.getSnippets());
+            }
+            writeTarEntry(tarOut, CONTROLLER_SERVICES_FILENAME, clusterDataFlow.getControllerServices());
+            writeTarEntry(tarOut, REPORTING_TASKS_FILENAME, clusterDataFlow.getReportingTasks());
 
             final ByteArrayOutputStream baos = new ByteArrayOutputStream(256);
             writeClusterMetadata(clusterMetadata, baos);

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/impl/DataFlowManagementServiceImpl.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/impl/DataFlowManagementServiceImpl.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/impl/DataFlowManagementServiceImpl.java
index e135af3..1bb8ca3 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/impl/DataFlowManagementServiceImpl.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/flow/impl/DataFlowManagementServiceImpl.java
@@ -41,7 +41,6 @@ import org.apache.nifi.cluster.protocol.message.FlowRequestMessage;
 import org.apache.nifi.cluster.protocol.message.FlowResponseMessage;
 import org.apache.nifi.logging.NiFiLog;
 import org.apache.nifi.util.FormatUtils;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -154,17 +153,74 @@ public class DataFlowManagementServiceImpl implements DataFlowManagementService
             final ClusterDataFlow existingClusterDataFlow = flowDao.loadDataFlow();
 
             final StandardDataFlow dataFlow;
+            final byte[] controllerServiceBytes;
+            final byte[] reportingTaskBytes;
             if (existingClusterDataFlow == null) {
                 dataFlow = null;
+                controllerServiceBytes = new byte[0];
+                reportingTaskBytes = new byte[0];
             } else {
                 dataFlow = existingClusterDataFlow.getDataFlow();
+                controllerServiceBytes = existingClusterDataFlow.getControllerServices();
+                reportingTaskBytes = existingClusterDataFlow.getReportingTasks();
             }
 
-            flowDao.saveDataFlow(new ClusterDataFlow(dataFlow, nodeId));
+            flowDao.saveDataFlow(new ClusterDataFlow(dataFlow, nodeId, controllerServiceBytes, reportingTaskBytes));
         } finally {
             resourceLock.unlock("updatePrimaryNode");
         }
     }
+    
+    
+    @Override
+    public void updateControllerServices(final byte[] controllerServiceBytes) throws DaoException {
+    	resourceLock.lock();
+    	try {
+    		final ClusterDataFlow existingClusterDataFlow = flowDao.loadDataFlow();
+
+            final StandardDataFlow dataFlow;
+            final byte[] reportingTaskBytes;
+            final NodeIdentifier nodeId;
+            if (existingClusterDataFlow == null) {
+                dataFlow = null;
+                nodeId = null;
+                reportingTaskBytes = new byte[0];
+            } else {
+                dataFlow = existingClusterDataFlow.getDataFlow();
+                nodeId = existingClusterDataFlow.getPrimaryNodeId();
+                reportingTaskBytes = existingClusterDataFlow.getReportingTasks();
+            }
+
+            flowDao.saveDataFlow(new ClusterDataFlow(dataFlow, nodeId, controllerServiceBytes, reportingTaskBytes));
+    	} finally {
+    		resourceLock.unlock("updateControllerServices");
+    	}
+    }
+    
+    @Override
+    public void updateReportingTasks(final byte[] reportingTaskBytes) throws DaoException {
+    	resourceLock.lock();
+    	try {
+    		final ClusterDataFlow existingClusterDataFlow = flowDao.loadDataFlow();
+
+            final StandardDataFlow dataFlow;
+            final byte[] controllerServiceBytes;
+            final NodeIdentifier nodeId;
+            if (existingClusterDataFlow == null) {
+                dataFlow = null;
+                nodeId = null;
+                controllerServiceBytes = null;
+            } else {
+                dataFlow = existingClusterDataFlow.getDataFlow();
+                nodeId = existingClusterDataFlow.getPrimaryNodeId();
+                controllerServiceBytes = existingClusterDataFlow.getControllerServices();
+            }
+
+            flowDao.saveDataFlow(new ClusterDataFlow(dataFlow, nodeId, controllerServiceBytes, reportingTaskBytes));
+    	} finally {
+    		resourceLock.unlock("updateControllerServices");
+    	}
+    }
 
     @Override
     public PersistedFlowState getPersistedFlowState() {
@@ -303,9 +359,10 @@ public class DataFlowManagementServiceImpl implements DataFlowManagementService
                             final ClusterDataFlow existingClusterDataFlow = flowDao.loadDataFlow();
                             final ClusterDataFlow currentClusterDataFlow;
                             if (existingClusterDataFlow == null) {
-                                currentClusterDataFlow = new ClusterDataFlow(dataFlow, null);
+                                currentClusterDataFlow = new ClusterDataFlow(dataFlow, null, new byte[0], new byte[0]);
                             } else {
-                                currentClusterDataFlow = new ClusterDataFlow(dataFlow, existingClusterDataFlow.getPrimaryNodeId());
+                                currentClusterDataFlow = new ClusterDataFlow(dataFlow, existingClusterDataFlow.getPrimaryNodeId(), 
+                                		existingClusterDataFlow.getControllerServices(), existingClusterDataFlow.getReportingTasks());
                             }
                             flowDao.saveDataFlow(currentClusterDataFlow);
                             flowDao.setPersistedFlowState(PersistedFlowState.CURRENT);

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/NodeResponse.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/NodeResponse.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/NodeResponse.java
index 3f966e5..8bc73ab 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/NodeResponse.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/NodeResponse.java
@@ -192,6 +192,20 @@ public class NodeResponse {
     }
 
     /**
+     * If this node response has been merged returns the updated entity, 
+     * otherwise null. Also returns null if hasThrowable() is true. The
+     * intent of this method is to support getting the response entity
+     * when it was already consumed during the merge operation. In this
+     * case the client response rom getClientResponse() will not support 
+     * a getEntity(...) or getEntityInputStream()  call.
+     * 
+     * @return 
+     */
+    public Entity getUpdatedEntity() {
+        return updatedEntity;
+    }
+    
+    /**
      * Creates a Response by mapping the ClientResponse values to it. Since the
      * ClientResponse's input stream can only be read once, this method should
      * only be called once. Furthermore, the caller should not have already read


[24/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnAdded.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnAdded.java b/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnAdded.java
index acb7a4d..a1286ea 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnAdded.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnAdded.java
@@ -24,16 +24,25 @@ import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
 /**
+ * <p>
  * Marker annotation a {@link org.apache.nifi.processor.Processor Processor}, 
  * {@link org.apache.nifi.controller.ControllerService ControllerService}, or
  * {@link org.apache.nifi.reporting.ReportingTask ReportingTask} 
  * implementation can use to indicate a method
  * should be called whenever the component is added to the flow. This method
  * will be called once for the entire life of a component instance.
- *
+ * </p>
+ * 
+ * <p>
+ * Methods with this annotation are called without any arguments, as all settings
+ * and properties can be assumed to be the defaults.
+ * </p>
+ * 
+ * <p>
  * If any method annotated with this annotation throws a Throwable, the component
  * will not be added to the flow.
- *
+ * </p>
+ * 
  * @author none
  */
 @Documented

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnDisabled.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnDisabled.java b/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnDisabled.java
index 0f78010..b227968 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnDisabled.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnDisabled.java
@@ -23,19 +23,32 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+import org.apache.nifi.controller.ConfigurationContext;
+
 /**
- * Marker annotation a {@link org.apache.nifi.processor.Processor Processor},
- * {@link org.apache.nifi.controller.ControllerService ControllerService} or 
- * {@link org.apache.nifi.reporting.ReportingTask ReportingTask}  
- * can use to indicate a method should be called whenever the component is disabled. 
+ * <p>
+ * Marker annotation a {@link org.apache.nifi.controller.ControllerService ControllerService} 
+ * can use to indicate a method should be called whenever the service is disabled. 
+ *</p>
  *
  * <p>
- * Methods using this annotation must take no arguments. If a method with this annotation
- * throws a Throwable, a log message and bulletin will be issued for the component, but
- * the component will still be disabled.
+ * Methods using this annotation are permitted to take zero arguments or to take a single
+ * argument of type {@link ConfigurationContext}. If a method with this annotation
+ * throws a Throwable, a log message and bulletin will be issued for the service, and the
+ * service will remain in a 'DISABLING' state. When this occurs, the method with this annotation
+ * will be called again after some period of time. This will continue until the method returns
+ * without throwing any Throwable. Until that time, the service will remain in a 'DISABLING' state
+ * and cannot be enabled again.
+ * </p>
+ * 
+ * <p>
+ * Note that this annotation will be ignored if applied to a ReportingTask or Processor. For a Controller
+ * Service, enabling and disabling are considered lifecycle events, as the action makes them usable or
+ * unusable by other components. However, for a Processor and a Reporting
+ * Task, these are not lifecycle events but rather a mechanism to allow a component to be excluded when
+ * starting or stopping a group of components.
  * </p>
  *
- * @author none
  */
 @Documented
 @Target({ElementType.METHOD})

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnEnabled.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnEnabled.java b/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnEnabled.java
index 1536dec..32aeec6 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnEnabled.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnEnabled.java
@@ -25,35 +25,35 @@ import java.lang.annotation.Target;
 
 /**
  * <p>
- * Marker annotation a {@link org.apache.nifi.processor.Processor Processor},
- * {@link org.apache.nifi.controller.ControllerService ControllerService} or 
- * {@link org.apache.nifi.reporting.ReportingTask ReportingTask}  
- * can use to indicate a method should be called whenever the component is enabled.
- * Any method that has this annotation will be called every time a user enables the component.
+ * Marker annotation a {@link org.apache.nifi.controller.ControllerService ControllerService}
+ * can use to indicate a method should be called whenever the service is enabled.
+ * Any method that has this annotation will be called every time a user enables the service.
  * Additionally, each time that NiFi is restarted, if NiFi is configured to "auto-resume state"
- * and the component is enabled (whether stopped or running), the method will be invoked.
+ * and the service is enabled, the method will be invoked.
  * </p>
  *
  * <p>
- * Methods using this annotation must take either 0 arguments or a single argument.
+ * Methods using this annotation must take either 0 arguments or a single argument of type 
+ * {@link org.apache.nifi.controller.ConfigurationContext ConfigurationContext}.
  * </p>
  * 
  * <p>
- * If using 1 argument and the component using the annotation is a Processor, that argument must
- * be of type {@link org.apache.nifi.processor.ProcessContext ProcessContext}.
- * </p>
- * 
- * <p>
- * If using 1 argument and the component using the annotation is a Reporting Task or Controller Service, 
- * that argument must be of type {@link org.apache.nifi.controller.ConfigurationContext ConfigurationContext}.
+ * If a method with this annotation throws a Throwable, a log message and bulletin will be issued 
+ * for the component. In this event, the service will remain in an 'ENABLING' state and will not be
+ * usable. All methods with this annotation will then be called again after a delay. The service will
+ * not be made available for use until all methods with this annotation have returned without throwing
+ * anything.
  * </p>
  * 
  * <p>
- * If a method with this annotation throws a Throwable, a log message and bulletin will be issued 
- * for the component, but the component will still be enabled.
+ * Note that this annotation will be ignored if applied to a ReportingTask or Processor. For a Controller
+ * Service, enabling and disabling are considered lifecycle events, as the action makes them usable or
+ * unusable by other components. However, for a Processor and a Reporting
+ * Task, these are not lifecycle events but rather a mechanism to allow a component to be excluded when
+ * starting or stopping a group of components.
  * </p>
  *
- * @author none
+ * 
  */
 @Documented
 @Target({ElementType.METHOD})

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnRemoved.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnRemoved.java b/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnRemoved.java
index 696159f..71202b4 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnRemoved.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnRemoved.java
@@ -23,7 +23,11 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.processor.ProcessContext;
+
 /**
+ * <p>
  * Marker annotation a {@link org.apache.nifi.processor.Processor Processor}, 
  * {@link org.apache.nifi.controller.ControllerService ControllerService}, or
  * {@link org.apache.nifi.reporting.ReportingTask ReportingTask} implementation 
@@ -32,7 +36,15 @@ import java.lang.annotation.Target;
  * component instance. If the method throw any Throwable, that Throwable will be
  * caught and logged but will not prevent subsequent methods with this annotation
  * or removal of the component from the flow.
- *
+ * </p>
+ * 
+ * <p>
+ * Methods with this annotation are permitted to take no arguments or to take a single
+ * argument. If using a single argument, that argument must be of type {@link ConfigurationContext}
+ * if the component is a ReportingTask or a ControllerService. If the component is a Processor,
+ * then the argument must be of type {@link ProcessContext}.
+ * </p>
+ * 
  * @author none
  */
 @Documented

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnShutdown.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnShutdown.java b/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnShutdown.java
index a4129e1..3d1ce6c 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnShutdown.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnShutdown.java
@@ -23,7 +23,11 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.processor.ProcessContext;
+
 /**
+ * <p>
  * Marker annotation a {@link org.apache.nifi.processor.Processor Processor}, 
  * {@link org.apache.nifi.controller.ControllerService ControllerService}, or
  * {@link org.apache.nifi.reporting.ReportingTask ReportingTask} implementation 
@@ -31,7 +35,14 @@ import java.lang.annotation.Target;
  * This will be called at most once for each component in a JVM lifetime.
  * It is not, however, guaranteed that this method will be called on shutdown, as 
  * the service may be killed suddenly.
- *
+ * </p>
+ * 
+ * <p>
+ * Methods with this annotation are permitted to take either 0 or 1 argument. If an argument
+ * is used, it must be of type {@link ConfigurationContext} if the component is a ReportingTask
+ * or Controller Service, or of type {@link ProcessContext} if the component is a Processor.
+ * </p>
+ *  
  * @author none
  */
 @Documented

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnStopped.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnStopped.java b/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnStopped.java
index 4715253..fdc4fd8 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnStopped.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnStopped.java
@@ -23,6 +23,9 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.processor.ProcessContext;
+
 /**
  * <p>
  * Marker annotation a {@link org.apache.nifi.processor.Processor Processor} or
@@ -47,6 +50,12 @@ import java.lang.annotation.Target;
  * longer scheduled to run (as opposed to after all threads have returned from the
  * <code>onTrigger</code> method), see the {@link OnUnscheduled} annotation.
  * </p>
+ * 
+ * <p>
+ * Methods with this annotation are permitted to take either 0 or 1 argument. If an argument
+ * is used, it must be of type {@link ConfigurationContext} if the component is a ReportingTask
+ * or of type {@link ProcessContext} if the component is a Processor.
+ * </p>
  *
  * @author none
  */

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnUnscheduled.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnUnscheduled.java b/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnUnscheduled.java
index b1dbde1..5c7e13d 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnUnscheduled.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/annotation/lifecycle/OnUnscheduled.java
@@ -47,8 +47,6 @@ import java.lang.annotation.Target;
  * If using 1 argument and the component using the annotation is a Reporting Task, that argument must
  * be of type {@link org.apache.nifi.controller.ConfigurationContext ConfigurationContext}.
  * </p>
- *
- * @author none
  */
 @Documented
 @Target({ElementType.METHOD})

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/components/PropertyDescriptor.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/components/PropertyDescriptor.java b/nifi/nifi-api/src/main/java/org/apache/nifi/components/PropertyDescriptor.java
index 82372af..e62ff79 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/components/PropertyDescriptor.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/components/PropertyDescriptor.java
@@ -142,9 +142,19 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
             final Set<String> validIdentifiers = context.getControllerServiceLookup().getControllerServiceIdentifiers(controllerServiceDefinition);
             if (validIdentifiers != null && validIdentifiers.contains(input)) {
                 final ControllerService controllerService = context.getControllerServiceLookup().getControllerService(input);
-                if (!context.getControllerServiceLookup().isControllerServiceEnabled(controllerService)) {
+                if ( !context.isValidationRequired(controllerService) ) {
                     return new ValidationResult.Builder()
-                            .input(input)
+                        .input(input)
+                        .subject(getName())
+                        .valid(true)
+                        .build();
+                }
+                
+                final String serviceId = controllerService.getIdentifier();
+                if (!context.getControllerServiceLookup().isControllerServiceEnabled(serviceId) && 
+                    !context.getControllerServiceLookup().isControllerServiceEnabling(serviceId)) {
+                    return new ValidationResult.Builder()
+                            .input(context.getControllerServiceLookup().getControllerServiceName(serviceId))
                             .subject(getName())
                             .valid(false)
                             .explanation("Controller Service " + controllerService + " is disabled")

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/components/ValidationContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/components/ValidationContext.java b/nifi/nifi-api/src/main/java/org/apache/nifi/components/ValidationContext.java
index b7b72c5..61b68a2 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/components/ValidationContext.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/components/ValidationContext.java
@@ -81,6 +81,15 @@ public interface ValidationContext {
     String getAnnotationData();
     
     /**
+     * There are times when the framework needs to consider a component valid, even if it
+     * references an invalid ControllerService. This method will return <code>false</code>
+     * if the component is to be considered valid even if the given Controller Service is referenced
+     * and is invalid.
+     * @param service
+     */
+    boolean isValidationRequired(ControllerService service);
+    
+    /**
      * Returns <code>true</code> if the given value contains a NiFi Expression Language expression,
      * <code>false</code> if it does not
      * 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/controller/AbstractControllerService.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/controller/AbstractControllerService.java b/nifi/nifi-api/src/main/java/org/apache/nifi/controller/AbstractControllerService.java
index c12f2f8..71cdd23 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/controller/AbstractControllerService.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/controller/AbstractControllerService.java
@@ -22,6 +22,7 @@ import org.apache.nifi.components.AbstractConfigurableComponent;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.PropertyValue;
 import org.apache.nifi.controller.annotation.OnConfigured;
+import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.processor.ProcessorInitializationContext;
 import org.apache.nifi.reporting.InitializationException;
 
@@ -30,11 +31,13 @@ public abstract class AbstractControllerService extends AbstractConfigurableComp
     private String identifier;
     private ControllerServiceLookup serviceLookup;
     private volatile ConfigurationContext configContext;
-
+    private ComponentLog logger;
+    
     @Override
     public final void initialize(final ControllerServiceInitializationContext context) throws InitializationException {
         this.identifier = context.getIdentifier();
         serviceLookup = context.getControllerServiceLookup();
+        logger = context.getLogger();
         init(context);
     }
 
@@ -88,4 +91,12 @@ public abstract class AbstractControllerService extends AbstractConfigurableComp
      */
     protected void init(final ControllerServiceInitializationContext config) throws InitializationException {
     }
+    
+    /**
+     * Returns the logger that has been provided to the component by the framework in its initialize method.
+     * @return
+     */
+    protected ComponentLog getLogger() {
+        return logger;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/controller/ControllerServiceInitializationContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/controller/ControllerServiceInitializationContext.java b/nifi/nifi-api/src/main/java/org/apache/nifi/controller/ControllerServiceInitializationContext.java
index b5b0412..d34c635 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/controller/ControllerServiceInitializationContext.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/controller/ControllerServiceInitializationContext.java
@@ -16,6 +16,8 @@
  */
 package org.apache.nifi.controller;
 
+import org.apache.nifi.logging.ComponentLog;
+
 public interface ControllerServiceInitializationContext {
 
     /**
@@ -33,4 +35,12 @@ public interface ControllerServiceInitializationContext {
      * @return
      */
     ControllerServiceLookup getControllerServiceLookup();
+    
+    /**
+     * Returns a logger that can be used to log important events in a standard way and generate
+     * bulletins when appropriate
+     * 
+     * @return
+     */
+    ComponentLog getLogger();
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/controller/ControllerServiceLookup.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/controller/ControllerServiceLookup.java b/nifi/nifi-api/src/main/java/org/apache/nifi/controller/ControllerServiceLookup.java
index 77b8e62..4b96f62 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/controller/ControllerServiceLookup.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/controller/ControllerServiceLookup.java
@@ -42,6 +42,18 @@ public interface ControllerServiceLookup {
     boolean isControllerServiceEnabled(String serviceIdentifier);
 
     /**
+     * Returns <code>true</code> if the Controller Service with the given
+     * identifier has been enabled but is still in the transitioning state,
+     * otherwise returns <code>false</code>.
+     * If the given identifier is not known by this ControllerServiceLookup,
+     * returns <code>false</code>.
+     * 
+     * @param serviceIdentifier
+     * @return
+     */
+    boolean isControllerServiceEnabling(String serviceIdentifier);
+    
+    /**
      * Returns <code>true</code> if the given Controller Service is enabled,
      * <code>false</code> otherwise. If the given Controller Service is not
      * known by this ControllerServiceLookup, returns <code>false</code>
@@ -63,4 +75,11 @@ public interface ControllerServiceLookup {
      */
     Set<String> getControllerServiceIdentifiers(Class<? extends ControllerService> serviceType) throws IllegalArgumentException;
 
+    /**
+     * Returns the name of the Controller service with the given identifier. If no service can be
+     * found with this identifier, returns {@code null}.
+     * @param serviceIdentifier
+     * @return
+     */
+    String getControllerServiceName(String serviceIdentifier);
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/logging/ComponentLog.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/logging/ComponentLog.java b/nifi/nifi-api/src/main/java/org/apache/nifi/logging/ComponentLog.java
new file mode 100644
index 0000000..c070e23
--- /dev/null
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/logging/ComponentLog.java
@@ -0,0 +1,100 @@
+/*
+ * 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.logging;
+
+
+/**
+ * <p>
+ * The ComponentLog provides a mechanism to ensure that all NiFi components are logging and reporting
+ * information in a consistent way. When messages are logged to the ComponentLog, each message has the
+ * following characteristics:
+ * </p>
+ * 
+ * <ul>
+ *  <li>
+ *      The <code>toString()</code> of the component is automatically prepended to the message so that it is clear
+ *      which component is providing the information. This is important, since a single component may have many
+ *      different instances within the same NiFi instance.
+ *  </li>
+ *  <li>
+ *      If the last value in an Object[] argument that is passed to the logger is a Throwable, then the logged message
+ *      will include a <code>toString()</code> of the Throwable; in addition, if the component's logger is set to
+ *      DEBUG level via the logback configuration, the Stacktrace will also be logged. This provides a mechanism to easily
+ *      enable stacktraces in the logs when they are desired without filling the logs with unneeded stack traces for messages
+ *      that end up occurring often.
+ *  </li>
+ *  <li>
+ *      Any message that is logged with a Severity level that meets or exceeds the configured Bulletin Level for that component
+ *      will also cause a Bulletin to be generated, so that the message is visible in the UI, allowing Dataflow Managers
+ *      to understand that a problem exists and what the issue is.
+ *  </li>
+ * </ul>
+ * 
+ */
+public interface ComponentLog {
+    void warn(String msg, Throwable t);
+
+    void warn(String msg, Object[] os);
+
+    void warn(String msg, Object[] os, Throwable t);
+
+    void warn(String msg);
+
+    void trace(String msg, Throwable t);
+
+    void trace(String msg, Object[] os);
+
+    void trace(String msg);
+
+    void trace(String msg, Object[] os, Throwable t);
+
+    boolean isWarnEnabled();
+
+    boolean isTraceEnabled();
+
+    boolean isInfoEnabled();
+
+    boolean isErrorEnabled();
+
+    boolean isDebugEnabled();
+
+    void info(String msg, Throwable t);
+
+    void info(String msg, Object[] os);
+
+    void info(String msg);
+
+    void info(String msg, Object[] os, Throwable t);
+
+    String getName();
+
+    void error(String msg, Throwable t);
+
+    void error(String msg, Object[] os);
+
+    void error(String msg);
+
+    void error(String msg, Object[] os, Throwable t);
+
+    void debug(String msg, Throwable t);
+
+    void debug(String msg, Object[] os);
+
+    void debug(String msg, Object[] os, Throwable t);
+
+    void debug(String msg);
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/logging/ProcessorLog.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/logging/ProcessorLog.java b/nifi/nifi-api/src/main/java/org/apache/nifi/logging/ProcessorLog.java
index c5fa7b1..0d66d85 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/logging/ProcessorLog.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/logging/ProcessorLog.java
@@ -16,58 +16,15 @@
  */
 package org.apache.nifi.logging;
 
-public interface ProcessorLog {
 
-    void warn(String msg, Throwable t);
-
-    void warn(String msg, Object[] os);
-
-    void warn(String msg, Object[] os, Throwable t);
-
-    void warn(String msg);
-
-    void trace(String msg, Throwable t);
-
-    void trace(String msg, Object[] os);
-
-    void trace(String msg);
-
-    void trace(String msg, Object[] os, Throwable t);
-
-    boolean isWarnEnabled();
-
-    boolean isTraceEnabled();
-
-    boolean isInfoEnabled();
-
-    boolean isErrorEnabled();
-
-    boolean isDebugEnabled();
-
-    void info(String msg, Throwable t);
-
-    void info(String msg, Object[] os);
-
-    void info(String msg);
-
-    void info(String msg, Object[] os, Throwable t);
-
-    String getName();
-
-    void error(String msg, Throwable t);
-
-    void error(String msg, Object[] os);
-
-    void error(String msg);
-
-    void error(String msg, Object[] os, Throwable t);
-
-    void debug(String msg, Throwable t);
-
-    void debug(String msg, Object[] os);
-
-    void debug(String msg, Object[] os, Throwable t);
-
-    void debug(String msg);
+/**
+ * The ProcessorLog is an extension of ComponentLog but provides no additional functionality.
+ * It exists because ProcessorLog was created first,
+ * but when Controller Services and Reporting Tasks began to be used more heavily loggers
+ * were needed for them as well. We did not want to return a ProcessorLog to a ControllerService
+ * or a ReportingTask, so all of the methods were moved to a higher interface named ComponentLog.
+ * However, we kept the ProcessorLog interface around in order to maintain backward compatibility.
+ */
+public interface ProcessorLog extends ComponentLog {
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/reporting/AbstractReportingTask.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/reporting/AbstractReportingTask.java b/nifi/nifi-api/src/main/java/org/apache/nifi/reporting/AbstractReportingTask.java
index 5ed8f24..efcf2a3 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/reporting/AbstractReportingTask.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/reporting/AbstractReportingTask.java
@@ -20,6 +20,7 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.nifi.components.AbstractConfigurableComponent;
 import org.apache.nifi.controller.ControllerServiceLookup;
+import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.processor.ProcessorInitializationContext;
 
 public abstract class AbstractReportingTask extends AbstractConfigurableComponent implements ReportingTask {
@@ -28,10 +29,12 @@ public abstract class AbstractReportingTask extends AbstractConfigurableComponen
     private String name;
     private long schedulingNanos;
     private ControllerServiceLookup serviceLookup;
+    private ComponentLog logger;
 
     @Override
     public final void initialize(final ReportingInitializationContext config) throws InitializationException {
         identifier = config.getIdentifier();
+        logger = config.getLogger();
         name = config.getName();
         schedulingNanos = config.getSchedulingPeriod(TimeUnit.NANOSECONDS);
         serviceLookup = config.getControllerServiceLookup();
@@ -91,4 +94,11 @@ public abstract class AbstractReportingTask extends AbstractConfigurableComponen
     protected void init(final ReportingInitializationContext config) throws InitializationException {
     }
 
+    /**
+     * Returns the logger that has been provided to the component by the framework in its initialize method.
+     * @return
+     */
+    protected ComponentLog getLogger() {
+        return logger;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/reporting/ReportingInitializationContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/reporting/ReportingInitializationContext.java b/nifi/nifi-api/src/main/java/org/apache/nifi/reporting/ReportingInitializationContext.java
index a0ae88e..6b84589 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/reporting/ReportingInitializationContext.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/reporting/ReportingInitializationContext.java
@@ -19,6 +19,7 @@ package org.apache.nifi.reporting;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.nifi.controller.ControllerServiceLookup;
+import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.scheduling.SchedulingStrategy;
 
 /**
@@ -77,4 +78,13 @@ public interface ReportingInitializationContext {
      * @return
      */
     SchedulingStrategy getSchedulingStrategy();
+    
+    
+    /**
+     * Returns a logger that can be used to log important events in a standard way and generate
+     * bulletins when appropriate
+     * 
+     * @return
+     */
+    ComponentLog getLogger();
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/web/ClusterRequestException.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ClusterRequestException.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ClusterRequestException.java
index 0ecea3b..ee5f417 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ClusterRequestException.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ClusterRequestException.java
@@ -17,6 +17,7 @@
 package org.apache.nifi.web;
 
 /**
+ * An general error occurred when attempting to communicate with the cluster.
  */
 public class ClusterRequestException extends RuntimeException {
 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/web/ComponentDetails.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ComponentDetails.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ComponentDetails.java
new file mode 100644
index 0000000..0b68ed9
--- /dev/null
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ComponentDetails.java
@@ -0,0 +1,157 @@
+/*
+ * 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.web;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Details about a given component. Contains configuration and current validation errors.
+ */
+public class ComponentDetails {
+
+    private final String id;
+    private final String name;
+    private final String type;
+    private final String state;
+    private final String annotationData;
+    private final Map<String, String> properties;
+    private final Collection<String> validationErrors;
+
+    private ComponentDetails(final Builder builder) {
+        this.id = builder.id;
+        this.name = builder.name;
+        this.type = builder.type;
+        this.state = builder.state;
+        this.annotationData = builder.annotationData;
+        this.properties = builder.properties;
+        this.validationErrors = builder.validationErrors;
+    }
+
+    /**
+     * The component id.
+     * 
+     * @return 
+     */
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * The component name.
+     * 
+     * @return 
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * The component type.
+     * 
+     * @return 
+     */
+    public String getType() {
+        return type;
+    }
+    
+    /**
+     * The component state.
+     * 
+     * @return 
+     */
+    public String getState() {
+        return state;
+    }
+
+    /**
+     * The component's annotation data.
+     * 
+     * @return 
+     */
+    public String getAnnotationData() {
+        return annotationData;
+    }
+
+    /**
+     * Mapping of component properties.
+     * 
+     * @return 
+     */
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    /**
+     * Current validation errors for the component.
+     * 
+     * @return 
+     */
+    public Collection<String> getValidationErrors() {
+        return validationErrors;
+    }
+
+    public static final class Builder {
+
+        private String id;
+        private String name;
+        private String type;
+        private String state;
+        private String annotationData;
+        private Map<String, String> properties;
+        private Collection<String> validationErrors;
+
+        public Builder id(final String id) {
+            this.id = id;
+            return this;
+        }
+
+        public Builder name(final String name) {
+            this.name = name;
+            return this;
+        }
+        
+        public Builder type(final String type) {
+            this.type = type;
+            return this;
+        }
+
+        public Builder state(final String state) {
+            this.state = state;
+            return this;
+        }
+
+        public Builder annotationData(final String annotationData) {
+            this.annotationData = annotationData;
+            return this;
+        }
+
+        public Builder properties(final Map<String, String> properties) {
+            this.properties = properties;
+            return this;
+        }
+
+        public Builder validateErrors(final Collection<String> validationErrors) {
+            this.validationErrors = validationErrors;
+            return this;
+        }
+
+        public ComponentDetails build() {
+            return new ComponentDetails(this);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationAction.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationAction.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationAction.java
new file mode 100644
index 0000000..066e772
--- /dev/null
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationAction.java
@@ -0,0 +1,137 @@
+/*
+ * 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.web;
+
+/**
+ * An action that represents the configuration of a component.
+ */
+public class ConfigurationAction {
+
+    private final String id;
+    private final String name;
+    private final String type;
+    private final String field;
+    private final String previousValue;
+    private final String value;
+
+    private ConfigurationAction(final Builder builder) {
+        this.id = builder.id;
+        this.name = builder.name;
+        this.type = builder.type;
+        this.field = builder.field;
+        this.previousValue = builder.previousValue;
+        this.value = builder.value;
+    }
+
+    /**
+     * The id of the component being modified.
+     * 
+     * @return 
+     */
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * The name of the component being modified.
+     * 
+     * @return 
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * The type of the component being modified.
+     * 
+     * @return 
+     */
+    public String getType() {
+        return type;
+    }
+
+    /**
+     * Gets the name of the field, property, etc that has been modified.
+     *
+     * @return
+     */
+    public String getField() {
+        return field;
+    }
+
+    /**
+     * Gets the previous value.
+     *
+     * @return
+     */
+    public String getPreviousValue() {
+        return previousValue;
+    }
+
+    /**
+     * Gets the new value.
+     *
+     * @return
+     */
+    public String getValue() {
+        return value;
+    }
+
+    public static class Builder {
+
+        private String id;
+        private String name;
+        private String type;
+        private String field;
+        private String previousValue;
+        private String value;
+
+        public Builder id(final String id) {
+            this.id = id;
+            return this;
+        }
+        
+        public Builder name(final String name) {
+            this.name = name;
+            return this;
+        }
+        
+        public Builder type(final String type) {
+            this.type = type;
+            return this;
+        }
+        
+        public Builder field(final String field) {
+            this.field = field;
+            return this;
+        }
+
+        public Builder previousValue(final String previousValue) {
+            this.previousValue = previousValue;
+            return this;
+        }
+
+        public Builder value(final String value) {
+            this.value = value;
+            return this;
+        }
+
+        public ConfigurationAction build() {
+            return new ConfigurationAction(this);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebConfigurationContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebConfigurationContext.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebConfigurationContext.java
new file mode 100644
index 0000000..50f0ca3
--- /dev/null
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebConfigurationContext.java
@@ -0,0 +1,102 @@
+/*
+ * 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.web;
+
+import java.util.Collection;
+
+import org.apache.nifi.controller.ControllerService;
+
+/**
+ * NiFi web context providing limited access to dataflow configuration for
+ * component custom UIs.
+ */
+public interface NiFiWebConfigurationContext {
+    
+    /**
+     * Gets the ControllerService for the specified identifier. If a
+     * corresponding service cannot be found, null is returned. If this NiFi is
+     * clustered, the only services available will be those those
+     * availability is NCM only.
+     *
+     * @param serviceIdentifier
+     * @return
+     */
+    ControllerService getControllerService(String serviceIdentifier);
+
+    /**
+     * Provides a mechanism for custom UIs to save actions to appear in NiFi
+     * configuration history. Note all fields within each Action must be
+     * populated. Null values will result in a failure to insert the audit
+     * record. Since the saving to these actions is separate from the actual
+     * configuration change, a failure to insert here will just generate a
+     * warning log message. The recording of these actions typically happens
+     * after a configuration change is applied. Since those changes have already
+     * been applied to the flow, we cannot revert them because of a failure to
+     * insert an audit record.
+     *
+     * @param requestContext
+     * @param actions
+     * @throws IllegalArgumentException     When the requestContext isn't fully populated or 
+     * isn't appropriate for the given request
+     */
+    void saveActions(NiFiWebRequestContext requestContext, Collection<ConfigurationAction> actions);
+
+    /**
+     * Gets the current user dn. Returns null if no user is found.
+     *
+     * @return
+     */
+    String getCurrentUserDn();
+
+    /**
+     * Gets the current user name. Returns null if no user is found.
+     *
+     * @return
+     */
+    String getCurrentUserName();
+
+    /**
+     * Sets the annotation data for the underlying component.
+     * 
+     * @param configurationContext
+     * @param annotationData
+     * @return the configuration for the underlying component
+     * @throws ResourceNotFoundException if the underlying component does not exit
+     * @throws InvalidRevisionException if a revision other than the current
+     * revision is given
+     * @throws ClusterRequestException if the annotation data was unable to be
+     * set for the underlying component. This exception will only be thrown when operating
+     * in a cluster.
+     * @throws IllegalArgumentException     When the requestContext isn't fully populated or 
+     * isn't appropriate for the given request
+     */
+    ComponentDetails setAnnotationData(NiFiWebConfigurationRequestContext configurationContext, String annotationData) throws ResourceNotFoundException, InvalidRevisionException, ClusterRequestException;
+    
+    /**
+     * Gets the details for the underlying component (including configuration, validation errors, and annotation data).
+     * 
+     * @param requestContext
+     * @return the configuration for the underlying component
+     * @throws ResourceNotFoundException if the underlying component does not exit
+     * @throws ClusterRequestException if the underlying component was unable to be
+     * retrieved from the cluster. This exception will only be thrown when
+     * operating in a cluster.
+     * @throws IllegalArgumentException     When the requestContext isn't fully populated or 
+     * isn't appropriate for the given request
+     */
+    ComponentDetails getComponentDetails(NiFiWebRequestContext requestContext) throws ResourceNotFoundException, ClusterRequestException;
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebConfigurationRequestContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebConfigurationRequestContext.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebConfigurationRequestContext.java
new file mode 100644
index 0000000..7912241
--- /dev/null
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebConfigurationRequestContext.java
@@ -0,0 +1,31 @@
+/*
+ * 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.web;
+
+/**
+ * Contextual details required to make a configuration request from a UI extension.
+ */
+public interface NiFiWebConfigurationRequestContext extends NiFiWebRequestContext {
+
+    /**
+     * The revision to include in the request.
+     * 
+     * @return the revision
+     */
+    Revision getRevision();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContext.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContext.java
index 4c4f25d..01702ad 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContext.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContext.java
@@ -24,6 +24,7 @@ import org.apache.nifi.controller.ControllerService;
  * NiFi web context providing limited access to dataflow configuration for
  * processor custom UIs.
  */
+@Deprecated
 public interface NiFiWebContext {
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContextConfig.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContextConfig.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContextConfig.java
index 808b9d6..2df94e4 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContextConfig.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebContextConfig.java
@@ -19,6 +19,7 @@ package org.apache.nifi.web;
 /**
  * Context configuration for methods invoked from the NiFiWebContext.
  */
+@Deprecated
 public interface NiFiWebContextConfig {
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebRequestContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebRequestContext.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebRequestContext.java
new file mode 100644
index 0000000..ac38221
--- /dev/null
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/NiFiWebRequestContext.java
@@ -0,0 +1,58 @@
+/*
+ * 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.web;
+
+/**
+ * Contextual details required to make a request from a UI extension.
+ */
+public interface NiFiWebRequestContext {
+
+    /**
+     * Returns the type of UI extension is making the request.
+     * 
+     * @return 
+     */
+    UiExtensionType getExtensionType();
+    
+    /**
+     * The request protocol scheme (http or https). When scheme is https, the
+     * X509Certificate can be used for subsequent remote requests.
+     *
+     * @return the protocol scheme
+     */
+    String getScheme();
+
+    /**
+     * The id of the component.
+     * 
+     * @return the ID
+     */
+    String getId();
+
+    /**
+     * Returns the proxied entities chain. The format of the chain is as
+     * follows:
+     *
+     * <code>
+     * &lt;CN=original-proxied-entity&gt;&lt;CN=first-proxy&gt;&lt;CN=second-proxy&gt;...
+     * </code>
+     *
+     * @return the proxied entities chain or null if no chain
+     */
+    String getProxiedEntitiesChain();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorConfigurationAction.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorConfigurationAction.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorConfigurationAction.java
index 8385e4a..ce5e069 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorConfigurationAction.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorConfigurationAction.java
@@ -19,6 +19,7 @@ package org.apache.nifi.web;
 /**
  *
  */
+@Deprecated
 public class ProcessorConfigurationAction {
 
     private final String processorId;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorInfo.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorInfo.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorInfo.java
index 0481098..e87e73e 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorInfo.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ProcessorInfo.java
@@ -22,6 +22,7 @@ import java.util.Map;
 /**
  *
  */
+@Deprecated
 public class ProcessorInfo {
 
     private final String id;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/web/Revision.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/Revision.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/Revision.java
index 1881c2f..8a6275e 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/web/Revision.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/Revision.java
@@ -37,12 +37,12 @@ public class Revision implements Serializable {
      * the client ID
      */
     private final String clientId;
-
+    
     public Revision(Long revision, String clientId) {
         this.version = revision;
         this.clientId = clientId;
     }
-
+    
     public String getClientId() {
         return clientId;
     }
@@ -51,34 +51,6 @@ public class Revision implements Serializable {
         return version;
     }
 
-    /**
-     * A factory method for creating a new Revision instance whose version is
-     * this instance's version plus 1.
-     *
-     * @return an updated revision
-     */
-    public Revision increment() {
-        final long incrementedVersion;
-        if (version == null) {
-            incrementedVersion = 0;
-        } else {
-            incrementedVersion = version + 1;
-        }
-        return new Revision(incrementedVersion, clientId);
-    }
-
-    /**
-     * A factory method for creating a new Revision instance whose version is
-     * this instance's version plus 1 and whose client ID is the given client
-     * ID.
-     *
-     * @param clientId the client ID
-     * @return an updated revision
-     */
-    public Revision increment(String clientId) {
-        return new Revision(increment().getVersion(), clientId);
-    }
-
     @Override
     public boolean equals(final Object obj) {
 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-api/src/main/java/org/apache/nifi/web/UiExtensionType.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/UiExtensionType.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/UiExtensionType.java
new file mode 100644
index 0000000..0bbda16
--- /dev/null
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/UiExtensionType.java
@@ -0,0 +1,31 @@
+/*
+ * 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.web;
+
+/**
+ * Types of UI extensions. Since a UI extension could support multiple
+ * types of custom UIs it will need to include the type so the framework
+ * can appropriate understand and process the request (recording actions
+ * in the audit database, replicating a request throughout the cluster to
+ * the appropriate endpoints, etc).
+ */
+public enum UiExtensionType {
+    ContentViewer,
+    ProcessorConfiguration,
+    ControllerServiceConfiguration,
+    ReportingTaskConfiguration
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-assembly/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-assembly/pom.xml b/nifi/nifi-assembly/pom.xml
index cae0f00..a26f214 100644
--- a/nifi/nifi-assembly/pom.xml
+++ b/nifi/nifi-assembly/pom.xml
@@ -187,8 +187,6 @@
 
         <nifi.flow.configuration.file>./conf/flow.xml.gz</nifi.flow.configuration.file>
         <nifi.flow.configuration.archive.dir>./conf/archive/</nifi.flow.configuration.archive.dir>
-        <nifi.reporting.task.configuration.file>./conf/reporting-tasks.xml</nifi.reporting.task.configuration.file>
-        <nifi.controller.service.configuration.file>./conf/controller-services.xml</nifi.controller.service.configuration.file>
         <nifi.authority.provider.configuration.file>./conf/authority-providers.xml</nifi.authority.provider.configuration.file>
         <nifi.templates.directory>./conf/templates</nifi.templates.directory>
         <nifi.database.directory>./database_repository</nifi.database.directory>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-commons/nifi-processor-utilities/src/main/java/org/apache/nifi/processor/util/StandardValidators.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-processor-utilities/src/main/java/org/apache/nifi/processor/util/StandardValidators.java b/nifi/nifi-commons/nifi-processor-utilities/src/main/java/org/apache/nifi/processor/util/StandardValidators.java
index a22e7bb..d1621ed 100644
--- a/nifi/nifi-commons/nifi-processor-utilities/src/main/java/org/apache/nifi/processor/util/StandardValidators.java
+++ b/nifi/nifi-commons/nifi-processor-utilities/src/main/java/org/apache/nifi/processor/util/StandardValidators.java
@@ -236,7 +236,7 @@ public class StandardValidators {
         @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").build();
+                return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
             }
 
             try {
@@ -254,7 +254,7 @@ public class StandardValidators {
         @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").build();
+                return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
             }
 
             try {
@@ -271,7 +271,7 @@ public class StandardValidators {
         @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").build();
+                return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
             }
 
             if (input == null) {
@@ -289,7 +289,7 @@ public class StandardValidators {
         @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").build();
+                return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
             }
 
             if (input == null) {
@@ -319,7 +319,7 @@ public class StandardValidators {
             @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").build();
+                    return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
                 }
 
                 try {
@@ -347,7 +347,7 @@ public class StandardValidators {
             @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").build();
+                    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);
@@ -372,7 +372,7 @@ public class StandardValidators {
             @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").build();
+                    return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
                 }
 
                 final boolean matches = pattern.matcher(input).matches();
@@ -457,7 +457,7 @@ public class StandardValidators {
             @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").build();
+                    return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
                 }
 
                 String reason = null;
@@ -503,7 +503,7 @@ public class StandardValidators {
         @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").build();
+                return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
             }
 
             if (input == null) {
@@ -628,7 +628,7 @@ public class StandardValidators {
             @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").build();
+                    return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
                 }
 
                 final ControllerService svc = context.getControllerServiceLookup().getControllerService(input);

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java b/nifi/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
index 87a82d4..3b427a7 100644
--- a/nifi/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
+++ b/nifi/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
@@ -42,8 +42,6 @@ public class NiFiProperties extends Properties {
     public static final String PROPERTIES_FILE_PATH = "nifi.properties.file.path";
     public static final String FLOW_CONFIGURATION_FILE = "nifi.flow.configuration.file";
     public static final String FLOW_CONFIGURATION_ARCHIVE_FILE = "nifi.flow.configuration.archive.file";
-    public static final String TASK_CONFIGURATION_FILE = "nifi.reporting.task.configuration.file";
-    public static final String SERVICE_CONFIGURATION_FILE = "nifi.controller.service.configuration.file";
     public static final String AUTHORITY_PROVIDER_CONFIGURATION_FILE = "nifi.authority.provider.configuration.file";
     public static final String REPOSITORY_DATABASE_DIRECTORY = "nifi.database.directory";
     public static final String RESTORE_DIRECTORY = "nifi.restore.directory";

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java b/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java
index e80f328..daf52b4 100644
--- a/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java
+++ b/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java
@@ -700,7 +700,7 @@ public class EndpointConnectionPool {
             final int flowFileCount = nodeInfo.getTotalFlowFiles();
             // don't allow any node to get more than 80% of the data
             final double percentageOfFlowFiles = Math.min(0.8D, ((double) flowFileCount / (double) totalFlowFileCount));
-            final double relativeWeighting = (direction == TransferDirection.RECEIVE) ? (1 - percentageOfFlowFiles) : percentageOfFlowFiles;
+            final double relativeWeighting = (direction == TransferDirection.SEND) ? (1 - percentageOfFlowFiles) : percentageOfFlowFiles;
             final int entries = Math.max(1, (int) (numDestinations * relativeWeighting));
             
             entryCountMap.put(nodeInfo, Math.max(1, entries));

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/client/socket/TestEndpointConnectionStatePool.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/client/socket/TestEndpointConnectionStatePool.java b/nifi/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/client/socket/TestEndpointConnectionStatePool.java
index cb7af08..c5cca78 100644
--- a/nifi/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/client/socket/TestEndpointConnectionStatePool.java
+++ b/nifi/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/client/socket/TestEndpointConnectionStatePool.java
@@ -39,7 +39,7 @@ public class TestEndpointConnectionStatePool {
         collection.add(new NodeInformation("ShouldGetMedium", 5, 5555, true, 4096));
 
         clusterNodeInfo.setNodeInformation(collection);
-        final List<PeerStatus> destinations = EndpointConnectionPool.formulateDestinationList(clusterNodeInfo, TransferDirection.SEND);
+        final List<PeerStatus> destinations = EndpointConnectionPool.formulateDestinationList(clusterNodeInfo, TransferDirection.RECEIVE);
         for ( final PeerStatus peerStatus : destinations ) {
             System.out.println(peerStatus.getPeerDescription());
         }
@@ -53,7 +53,7 @@ public class TestEndpointConnectionStatePool {
         collection.add(new NodeInformation("ShouldGetLots", 2, 2222, true, 50000));
 
         clusterNodeInfo.setNodeInformation(collection);
-        final List<PeerStatus> destinations = EndpointConnectionPool.formulateDestinationList(clusterNodeInfo, TransferDirection.SEND);
+        final List<PeerStatus> destinations = EndpointConnectionPool.formulateDestinationList(clusterNodeInfo, TransferDirection.RECEIVE);
         for ( final PeerStatus peerStatus : destinations ) {
             System.out.println(peerStatus.getPeerDescription());
         }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockControllerServiceInitializationContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockControllerServiceInitializationContext.java b/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockControllerServiceInitializationContext.java
index 86624ae..fd3c2de 100644
--- a/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockControllerServiceInitializationContext.java
+++ b/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockControllerServiceInitializationContext.java
@@ -19,13 +19,20 @@ package org.apache.nifi.util;
 import org.apache.nifi.controller.ControllerService;
 import org.apache.nifi.controller.ControllerServiceInitializationContext;
 import org.apache.nifi.controller.ControllerServiceLookup;
+import org.apache.nifi.logging.ComponentLog;
 
 public class MockControllerServiceInitializationContext extends MockControllerServiceLookup implements ControllerServiceInitializationContext, ControllerServiceLookup {
 
     private final String identifier;
+    private final ComponentLog logger;
 
     public MockControllerServiceInitializationContext(final ControllerService controllerService, final String identifier) {
+        this(controllerService, identifier, new MockProcessorLog(identifier, controllerService));
+    }
+    
+    public MockControllerServiceInitializationContext(final ControllerService controllerService, final String identifier, final ComponentLog logger) {
         this.identifier = identifier;
+        this.logger = logger;
         addControllerService(controllerService, identifier);
     }
 
@@ -33,9 +40,19 @@ public class MockControllerServiceInitializationContext extends MockControllerSe
     public String getIdentifier() {
         return identifier;
     }
+    
+    @Override
+    public String getControllerServiceName(final String serviceIdentifier) {
+    	return null;
+    }
 
     @Override
     public ControllerServiceLookup getControllerServiceLookup() {
         return this;
     }
+    
+    @Override
+    public ComponentLog getLogger() {
+        return logger;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockControllerServiceLookup.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockControllerServiceLookup.java b/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockControllerServiceLookup.java
index 8298a39..2734440 100644
--- a/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockControllerServiceLookup.java
+++ b/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockControllerServiceLookup.java
@@ -77,6 +77,11 @@ public abstract class MockControllerServiceLookup implements ControllerServiceLo
     }
 
     @Override
+    public boolean isControllerServiceEnabling(final String serviceIdentifier) {
+        return false;
+    }
+    
+    @Override
     public Set<String> getControllerServiceIdentifiers(final Class<? extends ControllerService> serviceType) {
         final Set<String> ids = new HashSet<>();
         for (final Map.Entry<String, ControllerServiceConfiguration> entry : controllerServiceMap.entrySet()) {
@@ -86,4 +91,10 @@ public abstract class MockControllerServiceLookup implements ControllerServiceLo
         }
         return ids;
     }
+    
+    @Override
+    public String getControllerServiceName(String serviceIdentifier) {
+    	final ControllerServiceConfiguration status = controllerServiceMap.get(serviceIdentifier);
+    	return status == null ? null : serviceIdentifier;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessorInitializationContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessorInitializationContext.java b/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessorInitializationContext.java
index f49a6c5..0aa2749 100644
--- a/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessorInitializationContext.java
+++ b/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessorInitializationContext.java
@@ -63,6 +63,11 @@ public class MockProcessorInitializationContext implements ProcessorInitializati
     }
 
     @Override
+    public String getControllerServiceName(String serviceIdentifier) {
+    	return context.getControllerServiceName(serviceIdentifier);
+    }
+    
+    @Override
     public boolean isControllerServiceEnabled(String serviceIdentifier) {
         return context.isControllerServiceEnabled(serviceIdentifier);
     }
@@ -71,4 +76,9 @@ public class MockProcessorInitializationContext implements ProcessorInitializati
     public boolean isControllerServiceEnabled(ControllerService service) {
         return context.isControllerServiceEnabled(service);
     }
+
+    @Override
+    public boolean isControllerServiceEnabling(String serviceIdentifier) {
+        return context.isControllerServiceEnabling(serviceIdentifier);
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessorLog.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessorLog.java b/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessorLog.java
index f8489f8..5505e88 100644
--- a/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessorLog.java
+++ b/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockProcessorLog.java
@@ -17,28 +17,26 @@
 package org.apache.nifi.util;
 
 import org.apache.nifi.logging.ProcessorLog;
-import org.apache.nifi.processor.Processor;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class MockProcessorLog implements ProcessorLog {
 
     private final Logger logger;
-    private final Processor processor;
+    private final Object component;
 
-    public MockProcessorLog(final String processorId, final Processor processor) {
-        this.logger = LoggerFactory.getLogger(processor.getClass());
-        this.processor = processor;
+    public MockProcessorLog(final String componentId, final Object component) {
+        this.logger = LoggerFactory.getLogger(component.getClass());
+        this.component = component;
     }
 
     private Object[] addProcessor(final Object[] originalArgs) {
-        return prependToArgs(originalArgs, processor);
+        return prependToArgs(originalArgs, component);
     }
 
     private Object[] addProcessorAndThrowable(final Object[] os, final Throwable t) {
         final Object[] modifiedArgs = new Object[os.length + 2];
-        modifiedArgs[0] = processor.toString();
+        modifiedArgs[0] = component.toString();
         for (int i = 0; i < os.length; i++) {
             modifiedArgs[i + 1] = os[i];
         }
@@ -75,7 +73,7 @@ public class MockProcessorLog implements ProcessorLog {
      */
     @Override
     public void warn(final String msg, final Throwable t) {
-        warn("{} " + msg, new Object[]{processor}, t);
+        warn("{} " + msg, new Object[]{component}, t);
     }
 
     /**
@@ -118,7 +116,7 @@ public class MockProcessorLog implements ProcessorLog {
     @Override
     public void warn(String msg) {
         msg = "{} " + msg;
-        logger.warn(msg, processor);
+        logger.warn(msg, component);
     }
 
     /**
@@ -129,7 +127,7 @@ public class MockProcessorLog implements ProcessorLog {
     @Override
     public void trace(String msg, Throwable t) {
         msg = "{} " + msg;
-        final Object[] os = {processor};
+        final Object[] os = {component};
         logger.trace(msg, os, t);
     }
 
@@ -152,7 +150,7 @@ public class MockProcessorLog implements ProcessorLog {
     @Override
     public void trace(String msg) {
         msg = "{} " + msg;
-        final Object[] os = {processor};
+        final Object[] os = {component};
         logger.trace(msg, os);
     }
 
@@ -224,7 +222,7 @@ public class MockProcessorLog implements ProcessorLog {
     @Override
     public void info(String msg, Throwable t) {
         msg = "{} " + msg;
-        final Object[] os = {processor};
+        final Object[] os = {component};
 
         logger.info(msg, os);
         if (logger.isDebugEnabled()) {
@@ -252,7 +250,7 @@ public class MockProcessorLog implements ProcessorLog {
     @Override
     public void info(String msg) {
         msg = "{} " + msg;
-        final Object[] os = {processor};
+        final Object[] os = {component};
 
         logger.info(msg, os);
     }
@@ -291,7 +289,7 @@ public class MockProcessorLog implements ProcessorLog {
     @Override
     public void error(String msg, Throwable t) {
         msg = "{} " + msg;
-        final Object[] os = {processor};
+        final Object[] os = {component};
 
         logger.error(msg, os, t);
         if (logger.isDebugEnabled()) {
@@ -322,7 +320,7 @@ public class MockProcessorLog implements ProcessorLog {
     @Override
     public void error(String msg) {
         msg = "{} " + msg;
-        final Object[] os = {processor};
+        final Object[] os = {component};
 
         logger.error(msg, os);
     }
@@ -352,7 +350,7 @@ public class MockProcessorLog implements ProcessorLog {
     @Override
     public void debug(String msg, Throwable t) {
         msg = "{} " + msg;
-        final Object[] os = {processor};
+        final Object[] os = {component};
 
         logger.debug(msg, os, t);
     }
@@ -394,7 +392,7 @@ public class MockProcessorLog implements ProcessorLog {
     @Override
     public void debug(String msg) {
         msg = "{} " + msg;
-        final Object[] os = {processor};
+        final Object[] os = {component};
 
         logger.debug(msg, os);
     }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockReportingInitializationContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockReportingInitializationContext.java b/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockReportingInitializationContext.java
index a874536..7cabef2 100644
--- a/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockReportingInitializationContext.java
+++ b/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockReportingInitializationContext.java
@@ -22,6 +22,7 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.controller.ControllerServiceLookup;
+import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.reporting.ReportingInitializationContext;
 import org.apache.nifi.scheduling.SchedulingStrategy;
 
@@ -30,10 +31,12 @@ public class MockReportingInitializationContext extends MockControllerServiceLoo
     private final String identifier;
     private final String name;
     private final Map<PropertyDescriptor, String> properties = new HashMap<>();
+    private final ComponentLog logger;
 
-    public MockReportingInitializationContext(final String identifier, final String name) {
+    public MockReportingInitializationContext(final String identifier, final String name, final ComponentLog logger) {
         this.identifier = identifier;
         this.name = name;
+        this.logger = logger;
     }
 
     @Override
@@ -78,4 +81,9 @@ public class MockReportingInitializationContext extends MockControllerServiceLoo
     public SchedulingStrategy getSchedulingStrategy() {
         return SchedulingStrategy.TIMER_DRIVEN;
     }
+    
+    @Override
+    public ComponentLog getLogger() {
+        return logger;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockValidationContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockValidationContext.java b/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockValidationContext.java
index c00386e..c9b1cda 100644
--- a/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockValidationContext.java
+++ b/nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockValidationContext.java
@@ -103,6 +103,21 @@ public class MockValidationContext implements ValidationContext, ControllerServi
     }
     
     @Override
+    public String getControllerServiceName(final String serviceIdentifier) {
+    	final ControllerServiceConfiguration configuration = context.getConfiguration(serviceIdentifier);
+    	return configuration == null ? null : serviceIdentifier;
+    }
+    
+    @Override
+    public boolean isValidationRequired(final ControllerService service) {
+        return true;
+    }
+
+    @Override
+    public boolean isControllerServiceEnabling(String serviceIdentifier) {
+        return context.isControllerServiceEnabling(serviceIdentifier);
+    }
+    
     public boolean isExpressionLanguagePresent(final String value) {
         if ( value == null ) {
             return false;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-mock/src/main/java/org/apache/nifi/util/StandardProcessorTestRunner.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-mock/src/main/java/org/apache/nifi/util/StandardProcessorTestRunner.java b/nifi/nifi-mock/src/main/java/org/apache/nifi/util/StandardProcessorTestRunner.java
index 8d691dd..d66ed81 100644
--- a/nifi/nifi-mock/src/main/java/org/apache/nifi/util/StandardProcessorTestRunner.java
+++ b/nifi/nifi-mock/src/main/java/org/apache/nifi/util/StandardProcessorTestRunner.java
@@ -59,6 +59,7 @@ import org.apache.nifi.controller.ConfigurationContext;
 import org.apache.nifi.controller.ControllerService;
 import org.apache.nifi.flowfile.FlowFile;
 import org.apache.nifi.flowfile.attributes.CoreAttributes;
+import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.processor.ProcessSessionFactory;
 import org.apache.nifi.processor.Processor;
 import org.apache.nifi.processor.QueueSize;
@@ -512,13 +513,15 @@ public class StandardProcessorTestRunner implements TestRunner {
 
     @Override
     public void addControllerService(final String identifier, final ControllerService service, final Map<String, String> properties) throws InitializationException {
+        // hold off on failing due to deprecated annotation for now... will introduce later.
 //        for ( final Method method : service.getClass().getMethods() ) {
 //            if ( method.isAnnotationPresent(org.apache.nifi.controller.annotation.OnConfigured.class) ) {
 //                Assert.fail("Controller Service " + service + " is using deprecated Annotation " + org.apache.nifi.controller.annotation.OnConfigured.class + " for method " + method);
 //            }
 //        }
         
-        final MockControllerServiceInitializationContext initContext = new MockControllerServiceInitializationContext(requireNonNull(service), requireNonNull(identifier));
+        final ComponentLog logger = new MockProcessorLog(identifier, service);
+        final MockControllerServiceInitializationContext initContext = new MockControllerServiceInitializationContext(requireNonNull(service), requireNonNull(identifier), logger);
         service.initialize(initContext);
 
         final Map<PropertyDescriptor, String> resolvedProps = new HashMap<>();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/ActionDAO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/ActionDAO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/ActionDAO.java
index 5d6d222..5d5d498 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/ActionDAO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/dao/ActionDAO.java
@@ -48,12 +48,12 @@ public interface ActionDAO {
 
     /**
      * Finds the previous values for the specified property in the specified
-     * processor. Returns empty list if there are none.
+     * component. Returns empty list if there are none.
      *
-     * @param processorId
+     * @param componentId
      * @return
      */
-    Map<String, List<PreviousValue>> getPreviousValues(String processorId);
+    Map<String, List<PreviousValue>> getPreviousValues(String componentId);
 
     /**
      * Finds the specified action.


[19/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateManager.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateManager.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateManager.java
index aa095d1..30d4365 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateManager.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/TemplateManager.java
@@ -42,24 +42,25 @@ import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.persistence.TemplateDeserializer;
+import org.apache.nifi.persistence.TemplateSerializer;
 import org.apache.nifi.stream.io.ByteArrayInputStream;
 import org.apache.nifi.stream.io.ByteArrayOutputStream;
 import org.apache.nifi.stream.io.DataOutputStream;
 import org.apache.nifi.stream.io.StreamUtils;
-import org.apache.nifi.persistence.TemplateDeserializer;
-import org.apache.nifi.persistence.TemplateSerializer;
 import org.apache.nifi.web.api.dto.ConnectableDTO;
 import org.apache.nifi.web.api.dto.ConnectionDTO;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
 import org.apache.nifi.web.api.dto.FlowSnippetDTO;
 import org.apache.nifi.web.api.dto.ProcessGroupDTO;
 import org.apache.nifi.web.api.dto.ProcessorConfigDTO;
-import org.apache.nifi.web.api.dto.ProcessorConfigDTO.PropertyDescriptorDTO;
 import org.apache.nifi.web.api.dto.ProcessorDTO;
+import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupContentsDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupPortDTO;
 import org.apache.nifi.web.api.dto.TemplateDTO;
-import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -272,6 +273,11 @@ public class TemplateManager {
             if (snippet.getProcessGroups() != null) {
                 scrubProcessGroups(snippet.getProcessGroups());
             }
+            
+            // go through each controller service if specified
+            if (snippet.getControllerServices() != null) {
+                scrubControllerServices(snippet.getControllerServices());
+            }
         }
     }
 
@@ -315,7 +321,6 @@ public class TemplateManager {
                     }
                 }
 
-                processorConfig.setDescriptors(null);
                 processorConfig.setCustomUiUrl(null);
             }
 
@@ -323,6 +328,24 @@ public class TemplateManager {
             processorDTO.setValidationErrors(null);
         }
     }
+    
+    private void scrubControllerServices(final Set<ControllerServiceDTO> controllerServices) {
+        for ( final ControllerServiceDTO serviceDTO : controllerServices ) {
+            final Map<String, String> properties = serviceDTO.getProperties();
+            final Map<String, PropertyDescriptorDTO> descriptors = serviceDTO.getDescriptors();
+            
+            if ( properties != null && descriptors != null ) {
+                for ( final PropertyDescriptorDTO descriptor : descriptors.values() ) {
+                    if ( descriptor.isSensitive() ) {
+                        properties.put(descriptor.getName(), null);
+                    }
+                }
+            }
+            
+            serviceDTO.setCustomUiUrl(null);
+            serviceDTO.setValidationErrors(null);
+        }
+    }
 
     /**
      * Scrubs connections prior to saving. This includes removing available

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/AbstractReportingTaskNode.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/AbstractReportingTaskNode.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/AbstractReportingTaskNode.java
index 7c3734a..05b3a06 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/AbstractReportingTaskNode.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/AbstractReportingTaskNode.java
@@ -16,11 +16,14 @@
  */
 package org.apache.nifi.controller.reporting;
 
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.apache.nifi.components.ValidationResult;
 import org.apache.nifi.controller.AbstractConfiguredComponent;
-import org.apache.nifi.controller.Availability;
 import org.apache.nifi.controller.ConfigurationContext;
 import org.apache.nifi.controller.ControllerServiceLookup;
 import org.apache.nifi.controller.ProcessScheduler;
@@ -29,6 +32,7 @@ import org.apache.nifi.controller.ScheduledState;
 import org.apache.nifi.controller.ValidationContextFactory;
 import org.apache.nifi.controller.annotation.OnConfigured;
 import org.apache.nifi.controller.exception.ProcessorLifeCycleException;
+import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.controller.service.ControllerServiceProvider;
 import org.apache.nifi.controller.service.StandardConfigurationContext;
 import org.apache.nifi.nar.NarCloseable;
@@ -45,8 +49,8 @@ public abstract class AbstractReportingTaskNode extends AbstractConfiguredCompon
 
     private final AtomicReference<SchedulingStrategy> schedulingStrategy = new AtomicReference<>(SchedulingStrategy.TIMER_DRIVEN);
     private final AtomicReference<String> schedulingPeriod = new AtomicReference<>("5 mins");
-    private final AtomicReference<Availability> availability = new AtomicReference<>(Availability.NODE_ONLY);
-
+    
+    private volatile String comment;
     private volatile ScheduledState scheduledState = ScheduledState.STOPPED;
     
     public AbstractReportingTaskNode(final ReportingTask reportingTask, final String id,
@@ -59,16 +63,6 @@ public abstract class AbstractReportingTaskNode extends AbstractConfiguredCompon
     }
 
     @Override
-    public Availability getAvailability() {
-        return availability.get();
-    }
-
-    @Override
-    public void setAvailability(final Availability availability) {
-        this.availability.set(availability);
-    }
-
-    @Override
     public void setSchedulingStrategy(final SchedulingStrategy schedulingStrategy) {
         this.schedulingStrategy.set(schedulingStrategy);
     }
@@ -102,6 +96,11 @@ public abstract class AbstractReportingTaskNode extends AbstractConfiguredCompon
     public boolean isRunning() {
         return processScheduler.isScheduled(this) || processScheduler.getActiveThreadCount(this) > 0;
     }
+    
+    @Override
+    public int getActiveThreadCount() {
+        return processScheduler.getActiveThreadCount(this);
+    }
 
     @Override
     public ConfigurationContext getConfigurationContext() {
@@ -142,7 +141,8 @@ public abstract class AbstractReportingTaskNode extends AbstractConfiguredCompon
         return removed;
     }
     
-    private void onConfigured() {
+    @SuppressWarnings("deprecation")
+	private void onConfigured() {
         // We need to invoke any method annotation with the OnConfigured annotation in order to
         // maintain backward compatibility. This will be removed when we remove the old, deprecated annotations.
         try (final NarCloseable x = NarCloseable.withNarLoader()) {
@@ -158,6 +158,16 @@ public abstract class AbstractReportingTaskNode extends AbstractConfiguredCompon
     }
     
     @Override
+    public String getComments() {
+		return comment;
+	}
+
+    @Override
+	public void setComments(final String comment) {
+		this.comment = comment;
+	}
+
+	@Override
     public void verifyCanDelete() {
         if (isRunning()) {
             throw new IllegalStateException("Cannot delete " + reportingTask + " because it is currently running");
@@ -207,4 +217,38 @@ public abstract class AbstractReportingTaskNode extends AbstractConfiguredCompon
             throw new IllegalStateException("Cannot update " + reportingTask + " because it is currently running");
         }
     }
+    
+    @Override
+    public void verifyCanStart(final Set<ControllerServiceNode> ignoredReferences) {
+        switch (getScheduledState()) {
+            case DISABLED:
+                throw new IllegalStateException(this + " cannot be started because it is disabled");
+            case RUNNING:
+                throw new IllegalStateException(this + " cannot be started because it is already running");
+            case STOPPED:
+                break;
+        }
+        final int activeThreadCount = getActiveThreadCount();
+        if ( activeThreadCount > 0 ) {
+            throw new IllegalStateException(this + " cannot be started because it has " + activeThreadCount + " active threads already");
+        }
+        
+        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 + " cannot be started because it is not valid: " + result);
+            }
+        }
+    }
+    
+    
+    @Override
+    public String toString() {
+        return "ReportingTask[id=" + getIdentifier() + ", name=" + getName() + "]";
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingContext.java
index ed48e20..3d57533 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingContext.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingContext.java
@@ -124,9 +124,20 @@ public class StandardReportingContext implements ReportingContext, ControllerSer
     public boolean isControllerServiceEnabled(final String serviceIdentifier) {
         return serviceProvider.isControllerServiceEnabled(serviceIdentifier);
     }
+    
+    @Override
+    public boolean isControllerServiceEnabling(final String serviceIdentifier) {
+        return serviceProvider.isControllerServiceEnabling(serviceIdentifier);
+    }
 
     @Override
     public ControllerServiceLookup getControllerServiceLookup() {
         return this;
     }
+
+	@Override
+	public String getControllerServiceName(final String serviceIdentifier) {
+		return serviceProvider.getControllerServiceName(serviceIdentifier);
+	}
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingInitializationContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingInitializationContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingInitializationContext.java
index d576f9c..435dbd0 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingInitializationContext.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/StandardReportingInitializationContext.java
@@ -22,6 +22,7 @@ import java.util.concurrent.TimeUnit;
 import org.apache.nifi.controller.ControllerService;
 import org.apache.nifi.controller.ControllerServiceLookup;
 import org.apache.nifi.controller.service.ControllerServiceProvider;
+import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.reporting.ReportingInitializationContext;
 import org.apache.nifi.scheduling.SchedulingStrategy;
 import org.apache.nifi.util.FormatUtils;
@@ -33,13 +34,16 @@ public class StandardReportingInitializationContext implements ReportingInitiali
     private final String schedulingPeriod;
     private final SchedulingStrategy schedulingStrategy;
     private final ControllerServiceProvider serviceProvider;
-
-    public StandardReportingInitializationContext(final String id, final String name, final SchedulingStrategy schedulingStrategy, final String schedulingPeriod, final ControllerServiceProvider serviceProvider) {
+    private final ComponentLog logger;
+    
+    public StandardReportingInitializationContext(final String id, final String name, final SchedulingStrategy schedulingStrategy, 
+            final String schedulingPeriod, final ComponentLog logger, final ControllerServiceProvider serviceProvider) {
         this.id = id;
         this.name = name;
         this.schedulingPeriod = schedulingPeriod;
         this.serviceProvider = serviceProvider;
         this.schedulingStrategy = schedulingStrategy;
+        this.logger = logger;
     }
 
     @Override
@@ -90,7 +94,22 @@ public class StandardReportingInitializationContext implements ReportingInitiali
     }
 
     @Override
+    public boolean isControllerServiceEnabling(final String serviceIdentifier) {
+        return serviceProvider.isControllerServiceEnabling(serviceIdentifier);
+    }
+    
+    @Override
     public ControllerServiceLookup getControllerServiceLookup() {
         return this;
     }
+    
+    @Override
+    public String getControllerServiceName(final String serviceIdentifier) {
+    	return serviceProvider.getControllerServiceName(serviceIdentifier);
+    }
+    
+    @Override
+    public ComponentLog getLogger() {
+        return logger;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java
index 4407451..89850cc 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java
@@ -19,6 +19,8 @@ package org.apache.nifi.controller.scheduling;
 import static java.util.Objects.requireNonNull;
 
 import java.lang.reflect.InvocationTargetException;
+import java.util.HashSet;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ExecutorService;
@@ -32,20 +34,26 @@ import org.apache.nifi.annotation.lifecycle.OnEnabled;
 import org.apache.nifi.annotation.lifecycle.OnScheduled;
 import org.apache.nifi.annotation.lifecycle.OnStopped;
 import org.apache.nifi.annotation.lifecycle.OnUnscheduled;
+import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.connectable.Connectable;
 import org.apache.nifi.connectable.Funnel;
 import org.apache.nifi.connectable.Port;
 import org.apache.nifi.controller.AbstractPort;
 import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.controller.ControllerService;
 import org.apache.nifi.controller.Heartbeater;
 import org.apache.nifi.controller.ProcessScheduler;
 import org.apache.nifi.controller.ProcessorNode;
 import org.apache.nifi.controller.ReportingTaskNode;
 import org.apache.nifi.controller.ScheduledState;
 import org.apache.nifi.controller.annotation.OnConfigured;
+import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.controller.service.ControllerServiceProvider;
+import org.apache.nifi.controller.service.ControllerServiceState;
+import org.apache.nifi.controller.service.StandardConfigurationContext;
 import org.apache.nifi.encrypt.StringEncryptor;
 import org.apache.nifi.engine.FlowEngine;
+import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.logging.ProcessorLog;
 import org.apache.nifi.nar.NarCloseable;
 import org.apache.nifi.processor.SchedulingContext;
@@ -144,6 +152,8 @@ public final class StandardProcessScheduler implements ProcessScheduler {
         componentLifeCycleThreadPool.shutdown();
     }
 
+    
+    @Override
     public void schedule(final ReportingTaskNode taskNode) {
         final ScheduleState scheduleState = getScheduleState(requireNonNull(taskNode));
         if (scheduleState.isScheduled()) {
@@ -176,16 +186,11 @@ public final class StandardProcessScheduler implements ProcessScheduler {
                         }
                         
                         break;
-                    } catch (final InvocationTargetException ite) {
-                        LOG.error("Failed to invoke the On-Scheduled Lifecycle methods of {} due to {}; administratively yielding this ReportingTask and will attempt to schedule it again after {}",
-                                new Object[]{reportingTask, ite.getTargetException(), administrativeYieldDuration});
-                        LOG.error("", ite.getTargetException());
-
-                        try {
-                            Thread.sleep(administrativeYieldMillis);
-                        } catch (final InterruptedException ie) {
-                        }
                     } catch (final Exception e) {
+                        final Throwable cause = (e instanceof InvocationTargetException) ? e.getCause() : e;
+                        final ComponentLog componentLog = new SimpleProcessLogger(reportingTask.getIdentifier(), reportingTask);
+                        componentLog.error("Failed to invoke @OnEnabled method due to {}", cause);
+                        
                         LOG.error("Failed to invoke the On-Scheduled Lifecycle methods of {} due to {}; administratively yielding this ReportingTask and will attempt to schedule it again after {}",
                                 new Object[]{reportingTask, e.toString(), administrativeYieldDuration}, e);
                         try {
@@ -200,18 +205,23 @@ public final class StandardProcessScheduler implements ProcessScheduler {
         };
 
         componentLifeCycleThreadPool.execute(startReportingTaskRunnable);
+        taskNode.setScheduledState(ScheduledState.RUNNING);
     }
 
+    
+    @Override
     public void unschedule(final ReportingTaskNode taskNode) {
         final ScheduleState scheduleState = getScheduleState(requireNonNull(taskNode));
         if (!scheduleState.isScheduled()) {
             return;
         }
-
+        
+        taskNode.verifyCanStop();
         final SchedulingAgent agent = getSchedulingAgent(taskNode.getSchedulingStrategy());
         final ReportingTask reportingTask = taskNode.getReportingTask();
         scheduleState.setScheduled(false);
-
+        taskNode.setScheduledState(ScheduledState.STOPPED);
+        
         final Runnable unscheduleReportingTaskRunnable = new Runnable() {
             @SuppressWarnings("deprecation")
             @Override
@@ -222,18 +232,15 @@ public final class StandardProcessScheduler implements ProcessScheduler {
                     try (final NarCloseable x = NarCloseable.withNarLoader()) {
                         ReflectionUtils.invokeMethodsWithAnnotation(OnUnscheduled.class, org.apache.nifi.processor.annotation.OnUnscheduled.class, reportingTask, configurationContext);
                     }
-                } catch (final InvocationTargetException ite) {
-                    LOG.error("Failed to invoke the @OnConfigured methods of {} due to {}; administratively yielding this ReportingTask and will attempt to schedule it again after {}",
-                            new Object[]{reportingTask, ite.getTargetException(), administrativeYieldDuration});
-                    LOG.error("", ite.getTargetException());
-
-                    try {
-                        Thread.sleep(administrativeYieldMillis);
-                    } catch (final InterruptedException ie) {
-                    }
                 } catch (final Exception e) {
-                    LOG.error("Failed to invoke the @OnConfigured methods of {} due to {}; administratively yielding this ReportingTask and will attempt to schedule it again after {}",
-                            new Object[]{reportingTask, e.toString(), administrativeYieldDuration}, e);
+                    final Throwable cause = (e instanceof InvocationTargetException) ? e.getCause() : e;
+                    final ComponentLog componentLog = new SimpleProcessLogger(reportingTask.getIdentifier(), reportingTask);
+                    componentLog.error("Failed to invoke @OnUnscheduled method due to {}", cause);
+
+                    LOG.error("Failed to invoke the @OnUnscheduled methods of {} due to {}; administratively yielding this ReportingTask and will attempt to schedule it again after {}",
+                            reportingTask, cause.toString(), administrativeYieldDuration);
+                    LOG.error("", cause);
+                    
                     try {
                         Thread.sleep(administrativeYieldMillis);
                     } catch (final InterruptedException ie) {
@@ -274,20 +281,38 @@ public final class StandardProcessScheduler implements ProcessScheduler {
         }
 
         if (!procNode.isValid()) {
-            throw new IllegalStateException("Processor " + procNode.getName() + " is not in a valid state");
+            throw new IllegalStateException("Processor " + procNode.getName() + " is not in a valid state due to " + procNode.getValidationErrors());
         }
 
         final Runnable startProcRunnable = new Runnable() {
-            @SuppressWarnings("deprecation")
             @Override
+            @SuppressWarnings("deprecation")
             public void run() {
                 try (final NarCloseable x = NarCloseable.withNarLoader()) {
                     long lastStopTime = scheduleState.getLastStopTime();
                     final StandardProcessContext processContext = new StandardProcessContext(procNode, controllerServiceProvider, encryptor);
 
-                    while (true) {
+                    final Set<String> serviceIds = new HashSet<>();
+                    for ( final PropertyDescriptor descriptor : processContext.getProperties().keySet() ) {
+                        final Class<? extends ControllerService> serviceDefinition = descriptor.getControllerServiceDefinition();
+                        if ( serviceDefinition != null ) {
+                            final String serviceId = processContext.getProperty(descriptor).getValue();
+                            serviceIds.add(serviceId);
+                        }
+                    }
+                    
+                    attemptOnScheduled: while (true) {
                         try {
                             synchronized (scheduleState) {
+                                for ( final String serviceId : serviceIds ) {
+                                    final boolean enabled = processContext.isControllerServiceEnabled(serviceId);
+                                    if ( !enabled ) {
+                                        LOG.debug("Controller Service with ID {} is not yet enabled, so will not start {} yet", serviceId, procNode);
+                                        Thread.sleep(administrativeYieldMillis);
+                                        continue attemptOnScheduled;
+                                    }
+                                }
+                                
                                 // if no longer scheduled to run, then we're finished. This can happen, for example,
                                 // if the @OnScheduled method throws an Exception and the user stops the processor 
                                 // while we're administratively yielded.
@@ -308,11 +333,12 @@ public final class StandardProcessScheduler implements ProcessScheduler {
                                 return;
                             }
                         } catch (final Exception e) {
+                            final Throwable cause = (e instanceof InvocationTargetException) ? e.getCause() : e;
                             final ProcessorLog procLog = new SimpleProcessLogger(procNode.getIdentifier(), procNode.getProcessor());
 
                             procLog.error("{} failed to invoke @OnScheduled method due to {}; processor will not be scheduled to run for {}",
-                                    new Object[]{procNode.getProcessor(), e.getCause(), administrativeYieldDuration}, e.getCause());
-                            LOG.error("Failed to invoke @OnScheduled method due to {}", e.getCause().toString(), e.getCause());
+                                    new Object[]{procNode.getProcessor(), cause.getCause(), administrativeYieldDuration}, cause.getCause());
+                            LOG.error("Failed to invoke @OnScheduled method due to {}", cause.getCause().toString(), cause.getCause());
 
                             ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnUnscheduled.class, procNode.getProcessor(), processContext);
                             ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnStopped.class, procNode.getProcessor(), processContext);
@@ -535,11 +561,6 @@ public final class StandardProcessScheduler implements ProcessScheduler {
         }
         
         procNode.setScheduledState(ScheduledState.STOPPED);
-        
-        try (final NarCloseable x = NarCloseable.withNarLoader()) {
-            final ProcessorLog processorLog = new SimpleProcessLogger(procNode.getIdentifier(), procNode.getProcessor());
-            ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnEnabled.class, procNode.getProcessor(), processorLog);
-        }
     }
 
     @Override
@@ -549,11 +570,6 @@ public final class StandardProcessScheduler implements ProcessScheduler {
         }
         
         procNode.setScheduledState(ScheduledState.DISABLED);
-        
-        try (final NarCloseable x = NarCloseable.withNarLoader()) {
-            final ProcessorLog processorLog = new SimpleProcessLogger(procNode.getIdentifier(), procNode.getProcessor());
-            ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnDisabled.class, procNode.getProcessor(), processorLog);
-        }
     }
 
     public synchronized void enableReportingTask(final ReportingTaskNode taskNode) {
@@ -562,10 +578,6 @@ public final class StandardProcessScheduler implements ProcessScheduler {
         }
 
         taskNode.setScheduledState(ScheduledState.STOPPED);
-        
-        try (final NarCloseable x = NarCloseable.withNarLoader()) {
-            ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnEnabled.class, taskNode.getReportingTask());
-        }
     }
     
     public synchronized void disableReportingTask(final ReportingTaskNode taskNode) {
@@ -574,10 +586,6 @@ public final class StandardProcessScheduler implements ProcessScheduler {
         }
 
         taskNode.setScheduledState(ScheduledState.DISABLED);
-        
-        try (final NarCloseable x = NarCloseable.withNarLoader()) {
-            ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnDisabled.class, taskNode.getReportingTask());
-        }
     }
 
     @Override
@@ -605,4 +613,114 @@ public final class StandardProcessScheduler implements ProcessScheduler {
         }
         return scheduleState;
     }
+
+    @Override
+    public void enableControllerService(final ControllerServiceNode service) {
+        service.setState(ControllerServiceState.ENABLING);
+        final ScheduleState scheduleState = getScheduleState(service);
+        
+        final Runnable enableRunnable = new Runnable() {
+            @Override
+            public void run() {
+                try (final NarCloseable x = NarCloseable.withNarLoader()) {
+                    long lastStopTime = scheduleState.getLastStopTime();
+                    final ConfigurationContext configContext = new StandardConfigurationContext(service, controllerServiceProvider);
+                    
+                    while (true) {
+                        try {
+                            synchronized (scheduleState) {
+                                // if no longer enabled, then we're finished. This can happen, for example,
+                                // if the @OnEnabled method throws an Exception and the user disables the service
+                                // while we're administratively yielded.
+                                // 
+                                // we also check if the schedule state's last stop time is equal to what it was before.
+                                // if not, then means that the service has been disabled and enabled again, so we should just
+                                // bail; another thread will be responsible for invoking the @OnEnabled methods.
+                                if (!scheduleState.isScheduled() || scheduleState.getLastStopTime() != lastStopTime) {
+                                    return;
+                                }
+
+                                ReflectionUtils.invokeMethodsWithAnnotation(OnEnabled.class, service.getControllerServiceImplementation(), configContext);
+                                heartbeater.heartbeat();
+                                service.setState(ControllerServiceState.ENABLED);
+                                return;
+                            }
+                        } catch (final Exception e) {
+                            final Throwable cause = (e instanceof InvocationTargetException) ? e.getCause() : e;
+                            
+                            final ComponentLog componentLog = new SimpleProcessLogger(service.getIdentifier(), service);
+                            componentLog.error("Failed to invoke @OnEnabled method due to {}", cause);
+                            LOG.error("Failed to invoke @OnEnabled method of {} due to {}", service.getControllerServiceImplementation(), cause.toString());
+                            if ( LOG.isDebugEnabled() ) {
+                                LOG.error("", cause);
+                            }
+
+                            ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnDisabled.class, service.getControllerServiceImplementation(), configContext);
+                            Thread.sleep(administrativeYieldMillis);
+                            continue;
+                        }
+                    }
+                } catch (final Throwable t) {
+                    final Throwable cause = (t instanceof InvocationTargetException) ? t.getCause() : t;
+                    final ComponentLog componentLog = new SimpleProcessLogger(service.getIdentifier(), service);
+                    componentLog.error("Failed to invoke @OnEnabled method due to {}", cause);
+                    
+                    LOG.error("Failed to invoke @OnEnabled method on {} due to {}", service.getControllerServiceImplementation(), cause.toString());
+                    if ( LOG.isDebugEnabled() ) {
+                        LOG.error("", cause);
+                    }
+                }
+            }
+        };
+        
+        scheduleState.setScheduled(true);
+        componentLifeCycleThreadPool.execute(enableRunnable);
+    }
+
+    @Override
+    public void disableControllerService(final ControllerServiceNode service) {
+        service.verifyCanDisable();
+        
+        final ScheduleState state = getScheduleState(requireNonNull(service));
+        final Runnable disableRunnable = new Runnable() {
+            @Override
+            public void run() {
+                synchronized (state) {
+                    state.setScheduled(false);
+                }
+
+                try (final NarCloseable x = NarCloseable.withNarLoader()) {
+                    final ConfigurationContext configContext = new StandardConfigurationContext(service, controllerServiceProvider);
+                    
+                    while(true) {
+                        try {
+                            ReflectionUtils.invokeMethodsWithAnnotation(OnDisabled.class, service.getControllerServiceImplementation(), configContext);
+                            heartbeater.heartbeat();
+                            service.setState(ControllerServiceState.DISABLED);
+                            return;
+                        } catch (final Exception e) {
+                            final Throwable cause = (e instanceof InvocationTargetException) ? e.getCause() : e;
+                            final ComponentLog componentLog = new SimpleProcessLogger(service.getIdentifier(), service);
+                            componentLog.error("Failed to invoke @OnDisabled method due to {}", cause);
+                            
+                            LOG.error("Failed to invoke @OnDisabled method of {} due to {}", service.getControllerServiceImplementation(), cause.toString());
+                            if ( LOG.isDebugEnabled() ) {
+                                LOG.error("", cause);
+                            }
+        
+                            ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnDisabled.class, service.getControllerServiceImplementation(), configContext);
+                            try {
+                                Thread.sleep(administrativeYieldMillis);
+                            } catch (final InterruptedException ie) {}
+                            
+                            continue;
+                        }
+                    }
+                }
+            }
+        };
+
+        service.setState(ControllerServiceState.DISABLING);
+        componentLifeCycleThreadPool.execute(disableRunnable);        
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/ControllerServiceLoader.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/ControllerServiceLoader.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/ControllerServiceLoader.java
index db44b5f..1fde670 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/ControllerServiceLoader.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/ControllerServiceLoader.java
@@ -17,30 +17,29 @@
 package org.apache.nifi.controller.service;
 
 import java.io.BufferedInputStream;
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
-import javax.xml.XMLConstants;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.validation.Schema;
-import javax.xml.validation.SchemaFactory;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import org.apache.nifi.controller.FlowFromDOMFactory;
+import org.apache.nifi.encrypt.StringEncryptor;
+import org.apache.nifi.reporting.BulletinRepository;
 import org.apache.nifi.util.DomUtils;
-import org.apache.nifi.util.file.FileUtils;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
 import org.xml.sax.SAXParseException;
 
@@ -49,35 +48,14 @@ import org.xml.sax.SAXParseException;
  */
 public class ControllerServiceLoader {
 
-    private static final Log logger = LogFactory.getLog(ControllerServiceLoader.class);
+    private static final Logger logger = LoggerFactory.getLogger(ControllerServiceLoader.class);
 
-    private final Path serviceConfigXmlPath;
 
-    public ControllerServiceLoader(final Path serviceConfigXmlPath) throws IOException {
-        final File serviceConfigXmlFile = serviceConfigXmlPath.toFile();
-        if (!serviceConfigXmlFile.exists() || !serviceConfigXmlFile.canRead()) {
-            throw new IOException(serviceConfigXmlPath + " does not appear to exist or cannot be read. Cannot load configuration.");
-        }
-
-        this.serviceConfigXmlPath = serviceConfigXmlPath;
-    }
-
-    public List<ControllerServiceNode> loadControllerServices(final ControllerServiceProvider provider) throws IOException {
-        final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+    public static List<ControllerServiceNode> loadControllerServices(final ControllerServiceProvider provider, final InputStream serializedStream, final StringEncryptor encryptor, final BulletinRepository bulletinRepo, final boolean autoResumeState) throws IOException {
         final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
-        InputStream fis = null;
-        BufferedInputStream bis = null;
         documentBuilderFactory.setNamespaceAware(true);
 
-        final List<ControllerServiceNode> services = new ArrayList<>();
-
-        try {
-            final URL configurationResource = this.getClass().getResource("/ControllerServiceConfiguration.xsd");
-            if (configurationResource == null) {
-                throw new NullPointerException("Unable to load XML Schema for ControllerServiceConfiguration");
-            }
-            final Schema schema = schemaFactory.newSchema(configurationResource);
-            documentBuilderFactory.setSchema(schema);
+        try (final InputStream in = new BufferedInputStream(serializedStream)) {
             final DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
 
             builder.setErrorHandler(new org.xml.sax.ErrorHandler() {
@@ -109,43 +87,72 @@ public class ControllerServiceLoader {
                     throw err;
                 }
             });
-
-            //if controllerService.xml does not exist, create an empty file...
-            fis = Files.newInputStream(this.serviceConfigXmlPath, StandardOpenOption.READ);
-            bis = new BufferedInputStream(fis);
-            if (Files.size(this.serviceConfigXmlPath) > 0) {
-                final Document document = builder.parse(bis);
-                final NodeList servicesNodes = document.getElementsByTagName("services");
-                final Element servicesElement = (Element) servicesNodes.item(0);
-
-                final List<Element> serviceNodes = DomUtils.getChildElementsByTagName(servicesElement, "service");
-                for (final Element serviceElement : serviceNodes) {
-                    //get properties for the specific controller task - id, name, class,
-                    //and schedulingPeriod must be set
-                    final String serviceId = DomUtils.getChild(serviceElement, "identifier").getTextContent().trim();
-                    final String serviceClass = DomUtils.getChild(serviceElement, "class").getTextContent().trim();
-
-                    //set the class to be used for the configured controller task
-                    final ControllerServiceNode serviceNode = provider.createControllerService(serviceClass, serviceId, false);
-
-                    //optional task-specific properties
-                    for (final Element optionalProperty : DomUtils.getChildElementsByTagName(serviceElement, "property")) {
-                        final String name = optionalProperty.getAttribute("name").trim();
-                        final String value = optionalProperty.getTextContent().trim();
-                        serviceNode.setProperty(name, value);
-                    }
-
-                    services.add(serviceNode);
-                    provider.enableControllerService(serviceNode);
-                }
-            }
+            
+            final Document document = builder.parse(in);
+            final Element controllerServices = document.getDocumentElement();
+            final List<Element> serviceElements = DomUtils.getChildElementsByTagName(controllerServices, "controllerService");
+            return new ArrayList<ControllerServiceNode>(loadControllerServices(serviceElements, provider, encryptor, bulletinRepo, autoResumeState));
         } catch (SAXException | ParserConfigurationException sxe) {
             throw new IOException(sxe);
-        } finally {
-            FileUtils.closeQuietly(fis);
-            FileUtils.closeQuietly(bis);
         }
-
-        return services;
+    }
+    
+    public static Collection<ControllerServiceNode> loadControllerServices(final List<Element> serviceElements, final ControllerServiceProvider provider, final StringEncryptor encryptor, final BulletinRepository bulletinRepo, final boolean autoResumeState) {
+        final Map<ControllerServiceNode, Element> nodeMap = new HashMap<>();
+        for ( final Element serviceElement : serviceElements ) {
+            final ControllerServiceNode serviceNode = createControllerService(provider, serviceElement, encryptor);
+            // We need to clone the node because it will be used in a separate thread below, and 
+            // Element is not thread-safe.
+            nodeMap.put(serviceNode, (Element) serviceElement.cloneNode(true));
+        }
+        for ( final Map.Entry<ControllerServiceNode, Element> entry : nodeMap.entrySet() ) {
+            configureControllerService(entry.getKey(), entry.getValue(), encryptor);
+        }
+        
+        // Start services
+        if ( autoResumeState ) {
+            final Set<ControllerServiceNode> nodesToEnable = new HashSet<>();
+            
+            for ( final ControllerServiceNode node : nodeMap.keySet() ) {
+                final Element controllerServiceElement = nodeMap.get(node);
+
+                final ControllerServiceDTO dto;
+                synchronized (controllerServiceElement.getOwnerDocument()) {
+                    dto = FlowFromDOMFactory.getControllerService(controllerServiceElement, encryptor);
+                }
+                
+                final ControllerServiceState state = ControllerServiceState.valueOf(dto.getState());
+                if (state == ControllerServiceState.ENABLED) {
+                    nodesToEnable.add(node);
+                }
+            }
+            
+            provider.enableControllerServices(nodesToEnable);
+        }
+        
+        return nodeMap.keySet();
+    }
+    
+    
+    private static ControllerServiceNode createControllerService(final ControllerServiceProvider provider, final Element controllerServiceElement, final StringEncryptor encryptor) {
+        final ControllerServiceDTO dto = FlowFromDOMFactory.getControllerService(controllerServiceElement, encryptor);
+        
+        final ControllerServiceNode node = provider.createControllerService(dto.getType(), dto.getId(), false);
+        node.setName(dto.getName());
+        node.setComments(dto.getComments());
+        return node;
+    }
+    
+    private static void configureControllerService(final ControllerServiceNode node, final Element controllerServiceElement, final StringEncryptor encryptor) {
+        final ControllerServiceDTO dto = FlowFromDOMFactory.getControllerService(controllerServiceElement, encryptor);
+        node.setAnnotationData(dto.getAnnotationData());
+        
+        for (final Map.Entry<String, String> entry : dto.getProperties().entrySet()) {
+            if (entry.getValue() == null) {
+                node.removeProperty(entry.getKey());
+            } else {
+                node.setProperty(entry.getKey(), entry.getValue());
+            }
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceInitializationContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceInitializationContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceInitializationContext.java
index 8b5f27f..8d46b05 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceInitializationContext.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceInitializationContext.java
@@ -21,14 +21,17 @@ import java.util.Set;
 import org.apache.nifi.controller.ControllerService;
 import org.apache.nifi.controller.ControllerServiceInitializationContext;
 import org.apache.nifi.controller.ControllerServiceLookup;
+import org.apache.nifi.logging.ComponentLog;
 
 public class StandardControllerServiceInitializationContext implements ControllerServiceInitializationContext, ControllerServiceLookup {
 
     private final String id;
     private final ControllerServiceProvider serviceProvider;
+    private final ComponentLog logger;
 
-    public StandardControllerServiceInitializationContext(final String identifier, final ControllerServiceProvider serviceProvider) {
+    public StandardControllerServiceInitializationContext(final String identifier, final ComponentLog logger, final ControllerServiceProvider serviceProvider) {
         this.id = identifier;
+        this.logger = logger;
         this.serviceProvider = serviceProvider;
     }
 
@@ -61,4 +64,19 @@ public class StandardControllerServiceInitializationContext implements Controlle
     public boolean isControllerServiceEnabled(final ControllerService service) {
         return serviceProvider.isControllerServiceEnabled(service);
     }
+    
+    @Override
+    public boolean isControllerServiceEnabling(String serviceIdentifier) {
+        return serviceProvider.isControllerServiceEnabling(serviceIdentifier);
+    }
+    
+    @Override
+    public String getControllerServiceName(final String serviceIdentifier) {
+    	return serviceProvider.getControllerServiceName(serviceIdentifier);
+    }
+
+    @Override
+    public ComponentLog getLogger() {
+        return logger;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java
index 741caec..c8c7ec9 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java
@@ -16,16 +16,17 @@
  */
 package org.apache.nifi.controller.service;
 
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import org.apache.nifi.components.ValidationResult;
 import org.apache.nifi.controller.AbstractConfiguredComponent;
-import org.apache.nifi.controller.Availability;
 import org.apache.nifi.controller.ConfigurationContext;
 import org.apache.nifi.controller.ConfiguredComponent;
 import org.apache.nifi.controller.ControllerService;
@@ -41,14 +42,14 @@ public class StandardControllerServiceNode extends AbstractConfiguredComponent i
     private final ControllerService implementation;
     private final ControllerServiceProvider serviceProvider;
 
-    private final AtomicReference<Availability> availability = new AtomicReference<>(Availability.NODE_ONLY);
-    private final AtomicBoolean disabled = new AtomicBoolean(true);
+    private final AtomicReference<ControllerServiceState> stateRef = new AtomicReference<>(ControllerServiceState.DISABLED);
 
     private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
     private final Lock readLock = rwLock.readLock();
     private final Lock writeLock = rwLock.writeLock();
 
     private final Set<ConfiguredComponent> referencingComponents = new HashSet<>();
+    private String comment;
 
     public StandardControllerServiceNode(final ControllerService proxiedControllerService, final ControllerService implementation, final String id,
             final ValidationContextFactory validationContextFactory, final ControllerServiceProvider serviceProvider) {
@@ -58,38 +59,7 @@ public class StandardControllerServiceNode extends AbstractConfiguredComponent i
         this.serviceProvider = serviceProvider;
     }
 
-    @Override
-    public boolean isDisabled() {
-        return disabled.get();
-    }
-
-    @Override
-    public void setDisabled(final boolean disabled) {
-        if (!disabled && !isValid()) {
-            throw new IllegalStateException("Cannot enable Controller Service " + implementation + " because it is not valid");
-        }
-
-        if (disabled) {
-            // do not allow a Controller Service to be disabled if it's currently being used.
-            final Set<ConfiguredComponent> runningRefs = getReferences().getRunningReferences();
-            if (!runningRefs.isEmpty()) {
-                throw new IllegalStateException("Cannot disable Controller Service because it is referenced (either directly or indirectly) by " + runningRefs.size() + " different components that are currently running");
-            }
-        }
-
-        this.disabled.set(disabled);
-    }
-
-    @Override
-    public Availability getAvailability() {
-        return availability.get();
-    }
-
-    @Override
-    public void setAvailability(final Availability availability) {
-        this.availability.set(availability);
-    }
-
+    
     @Override
     public ControllerService getProxiedControllerService() {
         return proxedControllerService;
@@ -132,7 +102,7 @@ public class StandardControllerServiceNode extends AbstractConfiguredComponent i
 
     @Override
     public void verifyModifiable() throws IllegalStateException {
-        if (!isDisabled()) {
+        if (getState() != ControllerServiceState.DISABLED) {
             throw new IllegalStateException("Cannot modify Controller Service configuration because it is currently enabled. Please disable the Controller Service first.");
         }
     }
@@ -140,7 +110,6 @@ public class StandardControllerServiceNode extends AbstractConfiguredComponent i
     @Override
     public void setProperty(final String name, final String value) {
         super.setProperty(name, value);
-        
         onConfigured();
     }
     
@@ -166,31 +135,96 @@ public class StandardControllerServiceNode extends AbstractConfiguredComponent i
     
     @Override
     public void verifyCanDelete() {
-        if ( !isDisabled() ) {
+        if ( getState() != ControllerServiceState.DISABLED ) {
             throw new IllegalStateException(implementation + " cannot be deleted because it is not disabled");
         }
     }
     
     @Override
     public void verifyCanDisable() {
+        verifyCanDisable(Collections.<ControllerServiceNode>emptySet());
+    }
+    
+    @Override
+    public void verifyCanDisable(final Set<ControllerServiceNode> ignoreReferences) {
+        final ControllerServiceState state = getState();
+        if ( state != ControllerServiceState.ENABLED && state != ControllerServiceState.ENABLING ) {
+            throw new IllegalStateException("Cannot disable " + getControllerServiceImplementation() + " because it is not enabled");
+        }
+        
         final ControllerServiceReference references = getReferences();
-        final int numRunning = references.getRunningReferences().size();
-        if ( numRunning > 0 ) {
-            throw new IllegalStateException(implementation + " cannot be disabled because it is referenced by " + numRunning + " components that are currently running");
+        
+        for ( final ConfiguredComponent activeReference : references.getActiveReferences() ) {
+            if ( !ignoreReferences.contains(activeReference) ) {
+                throw new IllegalStateException(implementation + " cannot be disabled because it is referenced by at least one component that is currently running");
+            }
         }
     }
     
     @Override
     public void verifyCanEnable() {
-        if ( !isDisabled() ) {
+        if ( getState() != ControllerServiceState.DISABLED ) {
             throw new IllegalStateException(implementation + " cannot be enabled because it is not disabled");
         }
+        
+        if ( !isValid() ) {
+            throw new IllegalStateException(implementation + " cannot be enabled because it is not valid: " + getValidationErrors());
+        }
+    }
+    
+    @Override
+    public void verifyCanEnable(final Set<ControllerServiceNode> ignoredReferences) {
+        if (getState() != ControllerServiceState.DISABLED) {
+            throw new IllegalStateException(implementation + " 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 + " cannot be enabled because it is not valid: " + result);
+            }
+        }
     }
     
     @Override
     public void verifyCanUpdate() {
-        if ( !isDisabled() ) {
+        if ( getState() != ControllerServiceState.DISABLED ) {
             throw new IllegalStateException(implementation + " cannot be updated because it is not disabled");
         }
     }
+    
+    @Override
+    public String getComments() {
+    	readLock.lock();
+    	try {
+    		return comment;
+    	} finally {
+    		readLock.unlock();
+    	}
+    }
+    
+    @Override
+    public void setComments(final String comment) {
+    	writeLock.lock();
+    	try {
+    		this.comment = comment;
+    	} finally {
+    		writeLock.unlock();
+    	}
+    }
+    
+    @Override
+    public ControllerServiceState getState() {
+        return stateRef.get();
+    }
+    
+    @Override
+    public void setState(final ControllerServiceState state) {
+        this.stateRef.set(state);
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceProvider.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceProvider.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceProvider.java
index 7a8e22f..dfbfca5 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceProvider.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceProvider.java
@@ -23,26 +23,39 @@ import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 import org.apache.nifi.annotation.lifecycle.OnAdded;
-import org.apache.nifi.annotation.lifecycle.OnEnabled;
-import org.apache.nifi.annotation.lifecycle.OnDisabled;
 import org.apache.nifi.annotation.lifecycle.OnRemoved;
+import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.controller.ConfiguredComponent;
 import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.controller.ProcessScheduler;
+import org.apache.nifi.controller.ProcessorNode;
+import org.apache.nifi.controller.ReportingTaskNode;
+import org.apache.nifi.controller.ScheduledState;
 import org.apache.nifi.controller.ValidationContextFactory;
-import org.apache.nifi.controller.exception.ControllerServiceAlreadyExistsException;
-import org.apache.nifi.controller.exception.ControllerServiceNotFoundException;
+import org.apache.nifi.controller.exception.ControllerServiceInstantiationException;
 import org.apache.nifi.controller.exception.ProcessorLifeCycleException;
+import org.apache.nifi.events.BulletinFactory;
+import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.nar.ExtensionManager;
 import org.apache.nifi.nar.NarCloseable;
+import org.apache.nifi.processor.SimpleProcessLogger;
 import org.apache.nifi.processor.StandardValidationContextFactory;
+import org.apache.nifi.reporting.BulletinRepository;
+import org.apache.nifi.reporting.Severity;
 import org.apache.nifi.util.ObjectHolder;
 import org.apache.nifi.util.ReflectionUtils;
 import org.slf4j.Logger;
@@ -55,8 +68,10 @@ public class StandardControllerServiceProvider implements ControllerServiceProvi
 
     private static final Logger logger = LoggerFactory.getLogger(StandardControllerServiceProvider.class);
 
-    private final Map<String, ControllerServiceNode> controllerServices;
+    private final ProcessScheduler processScheduler;
+    private final ConcurrentMap<String, ControllerServiceNode> controllerServices;
     private static final Set<Method> validDisabledMethods;
+    private final BulletinRepository bulletinRepo;
 
     static {
         // methods that are okay to be called when the service is disabled.
@@ -70,10 +85,12 @@ public class StandardControllerServiceProvider implements ControllerServiceProvi
         validDisabledMethods = Collections.unmodifiableSet(validMethods);
     }
 
-    public StandardControllerServiceProvider() {
+    public StandardControllerServiceProvider(final ProcessScheduler scheduler, final BulletinRepository bulletinRepo) {
         // the following 2 maps must be updated atomically, but we do not lock around them because they are modified
         // only in the createControllerService method, and both are modified before the method returns
         this.controllerServices = new ConcurrentHashMap<>();
+        this.processScheduler = scheduler;
+        this.bulletinRepo = bulletinRepo;
     }
 
     private Class<?>[] getInterfaces(final Class<?> cls) {
@@ -95,21 +112,24 @@ public class StandardControllerServiceProvider implements ControllerServiceProvi
             populateInterfaces(superClass, interfacesDefinedThusFar);
         }
     }
-
+    
     @Override
     public ControllerServiceNode createControllerService(final String type, final String id, final boolean firstTimeAdded) {
         if (type == null || id == null) {
             throw new NullPointerException();
         }
-        if (controllerServices.containsKey(id)) {
-            throw new ControllerServiceAlreadyExistsException(id);
-        }
-
+        
         final ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader();
         try {
             final ClassLoader cl = ExtensionManager.getClassLoader(type);
-            Thread.currentThread().setContextClassLoader(cl);
-            final Class<?> rawClass = Class.forName(type, false, cl);
+            final Class<?> rawClass;
+            if ( cl == null ) {
+                rawClass = Class.forName(type);
+            } else {
+                Thread.currentThread().setContextClassLoader(cl);
+                rawClass = Class.forName(type, false, cl);
+            }
+            
             final Class<? extends ControllerService> controllerServiceClass = rawClass.asSubclass(ControllerService.class);
 
             final ControllerService originalService = controllerServiceClass.newInstance();
@@ -124,7 +144,9 @@ public class StandardControllerServiceProvider implements ControllerServiceProvi
                 	}
                 	
                     final ControllerServiceNode node = serviceNodeHolder.get();
-                    if (node.isDisabled() && !validDisabledMethods.contains(method)) {
+                    final ControllerServiceState state = node.getState();
+                    final boolean disabled = (state != ControllerServiceState.ENABLED); // only allow method call if service state is ENABLED.
+                    if (disabled && !validDisabledMethods.contains(method)) {
                         // Use nar class loader here because we are implicitly calling toString() on the original implementation.
                         try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
                             throw new IllegalStateException("Cannot invoke method " + method + " on Controller Service " + originalService + " because the Controller Service is disabled");
@@ -143,17 +165,22 @@ public class StandardControllerServiceProvider implements ControllerServiceProvi
                 }
             };
 
-            final ControllerService proxiedService = (ControllerService) Proxy.newProxyInstance(cl, getInterfaces(controllerServiceClass), invocationHandler);
-            logger.info("Loaded service {} as configured.", type);
+            final ControllerService proxiedService;
+            if ( cl == null ) {
+                proxiedService = (ControllerService) Proxy.newProxyInstance(getClass().getClassLoader(), getInterfaces(controllerServiceClass), invocationHandler);
+            } else {
+                proxiedService = (ControllerService) Proxy.newProxyInstance(cl, getInterfaces(controllerServiceClass), invocationHandler);
+            }
+            logger.info("Created Controller Service of type {} with identifier {}", type, id);
 
-            originalService.initialize(new StandardControllerServiceInitializationContext(id, this));
+            final ComponentLog serviceLogger = new SimpleProcessLogger(id, originalService);
+            originalService.initialize(new StandardControllerServiceInitializationContext(id, serviceLogger, this));
 
             final ValidationContextFactory validationContextFactory = new StandardValidationContextFactory(this);
 
             final ControllerServiceNode serviceNode = new StandardControllerServiceNode(proxiedService, originalService, id, validationContextFactory, this);
             serviceNodeHolder.set(serviceNode);
-            serviceNode.setAnnotationData(null);
-            serviceNode.setName(id);
+            serviceNode.setName(rawClass.getSimpleName());
             
             if ( firstTimeAdded ) {
                 try (final NarCloseable x = NarCloseable.withNarLoader()) {
@@ -166,7 +193,7 @@ public class StandardControllerServiceProvider implements ControllerServiceProvi
             this.controllerServices.put(id, serviceNode);
             return serviceNode;
         } catch (final Throwable t) {
-            throw new ControllerServiceNotFoundException(t);
+            throw new ControllerServiceInstantiationException(t);
         } finally {
             if (currentContextClassLoader != null) {
                 Thread.currentThread().setContextClassLoader(currentContextClassLoader);
@@ -174,29 +201,242 @@ public class StandardControllerServiceProvider implements ControllerServiceProvi
         }
     }
     
+    
+    
+    @Override
+    public void disableReferencingServices(final ControllerServiceNode serviceNode) {
+        // Get a list of all Controller Services that need to be disabled, in the order that they need to be
+        // disabled.
+        final List<ControllerServiceNode> toDisable = findRecursiveReferences(serviceNode, ControllerServiceNode.class);
+        final Set<ControllerServiceNode> serviceSet = new HashSet<>(toDisable);
+        
+        for ( final ControllerServiceNode nodeToDisable : toDisable ) {
+            final ControllerServiceState state = nodeToDisable.getState();
+            
+            if ( state != ControllerServiceState.DISABLED && state != ControllerServiceState.DISABLING ) {
+                nodeToDisable.verifyCanDisable(serviceSet);
+            }
+        }
+        
+        Collections.reverse(toDisable);
+        for ( final ControllerServiceNode nodeToDisable : toDisable ) {
+            final ControllerServiceState state = nodeToDisable.getState();
+            
+            if ( state != ControllerServiceState.DISABLED && state != ControllerServiceState.DISABLING ) {
+                disableControllerService(nodeToDisable);
+            }
+        }
+    }
+    
+    
+    @Override
+    public void scheduleReferencingComponents(final ControllerServiceNode serviceNode) {
+        // find all of the schedulable components (processors, reporting tasks) that refer to this Controller Service,
+        // or a service that references this controller service, etc.
+        final List<ProcessorNode> processors = findRecursiveReferences(serviceNode, ProcessorNode.class);
+        final List<ReportingTaskNode> reportingTasks = findRecursiveReferences(serviceNode, ReportingTaskNode.class);
+        
+        // verify that  we can start all components (that are not disabled) before doing anything
+        for ( final ProcessorNode node : processors ) {
+            if ( node.getScheduledState() != ScheduledState.DISABLED ) {
+                node.verifyCanStart();
+            }
+        }
+        for ( final ReportingTaskNode node : reportingTasks ) {
+            if ( node.getScheduledState() != ScheduledState.DISABLED ) {
+                node.verifyCanStart();
+            }
+        }
+        
+        // start all of the components that are not disabled
+        for ( final ProcessorNode node : processors ) {
+            if ( node.getScheduledState() != ScheduledState.DISABLED ) {
+                node.getProcessGroup().startProcessor(node);
+            }
+        }
+        for ( final ReportingTaskNode node : reportingTasks ) {
+            if ( node.getScheduledState() != ScheduledState.DISABLED ) {
+                processScheduler.schedule(node);
+            }
+        }
+    }
+    
+    @Override
+    public void unscheduleReferencingComponents(final ControllerServiceNode serviceNode) {
+        // find all of the schedulable components (processors, reporting tasks) that refer to this Controller Service,
+        // or a service that references this controller service, etc.
+        final List<ProcessorNode> processors = findRecursiveReferences(serviceNode, ProcessorNode.class);
+        final List<ReportingTaskNode> reportingTasks = findRecursiveReferences(serviceNode, ReportingTaskNode.class);
+        
+        // verify that  we can stop all components (that are running) before doing anything
+        for ( final ProcessorNode node : processors ) {
+            if ( node.getScheduledState() == ScheduledState.RUNNING ) {
+                node.verifyCanStop();
+            }
+        }
+        for ( final ReportingTaskNode node : reportingTasks ) {
+            if ( node.getScheduledState() == ScheduledState.RUNNING ) {
+                node.verifyCanStop();
+            }
+        }
+        
+        // stop all of the components that are running
+        for ( final ProcessorNode node : processors ) {
+            if ( node.getScheduledState() == ScheduledState.RUNNING ) {
+                node.getProcessGroup().stopProcessor(node);
+            }
+        }
+        for ( final ReportingTaskNode node : reportingTasks ) {
+            if ( node.getScheduledState() == ScheduledState.RUNNING ) {
+                processScheduler.unschedule(node);
+            }
+        }
+    }
+    
     @Override
     public void enableControllerService(final ControllerServiceNode serviceNode) {
         serviceNode.verifyCanEnable();
+        processScheduler.enableControllerService(serviceNode);
+    }
+    
+    @Override
+    public void enableControllerServices(final Collection<ControllerServiceNode> serviceNodes) {
+        final Set<ControllerServiceNode> servicesToEnable = new HashSet<>();
+        // Ensure that all nodes are already disabled
+        for ( final ControllerServiceNode serviceNode : serviceNodes ) {
+            final ControllerServiceState curState = serviceNode.getState();
+            if ( ControllerServiceState.DISABLED.equals(curState) ) {
+                servicesToEnable.add(serviceNode);
+            } else {
+                logger.warn("Cannot enable {} because it is not disabled; current state is {}", serviceNode, curState);
+            }
+        }
         
-        try (final NarCloseable x = NarCloseable.withNarLoader()) {
-            final ConfigurationContext configContext = new StandardConfigurationContext(serviceNode, this);
-            ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnEnabled.class, serviceNode.getControllerServiceImplementation(), configContext);
+        // determine the order to load the services. We have to ensure that if service A references service B, then B
+        // is enabled first, and so on.
+        final Map<String, ControllerServiceNode> idToNodeMap = new HashMap<>();
+        for ( final ControllerServiceNode node : servicesToEnable ) {
+            idToNodeMap.put(node.getIdentifier(), node);
+        }
+        
+        // We can have many Controller Services dependent on one another. We can have many of these
+        // disparate lists of Controller Services that are dependent on one another. We refer to each
+        // of these as a branch.
+        final List<List<ControllerServiceNode>> branches = determineEnablingOrder(idToNodeMap);
+
+        if ( branches.isEmpty() ) {
+            logger.info("No Controller Services to enable");
+            return;
+        } else {
+            logger.info("Will enable {} Controller Services", servicesToEnable.size());
+        }
+        
+        // Mark all services that are configured to be enabled as 'ENABLING'. This allows Processors, reporting tasks
+        // to be valid so that they can be scheduled.
+        for ( final List<ControllerServiceNode> branch : branches ) {
+            for ( final ControllerServiceNode nodeToEnable : branch ) {
+                nodeToEnable.setState(ControllerServiceState.ENABLING);
+            }
+        }
+        
+        final Set<ControllerServiceNode> enabledNodes = Collections.synchronizedSet(new HashSet<ControllerServiceNode>());
+        final ExecutorService executor = Executors.newFixedThreadPool(Math.min(10, branches.size()));
+        for ( final List<ControllerServiceNode> branch : branches ) {
+            final Runnable enableBranchRunnable = new Runnable() {
+                @Override
+                public void run() {
+                    logger.debug("Enabling Controller Service Branch {}", branch);
+                    
+                    for ( final ControllerServiceNode serviceNode : branch ) {
+                        try {
+                            if ( !enabledNodes.contains(serviceNode) ) {
+                                enabledNodes.add(serviceNode);
+                                
+                                logger.info("Enabling {}", serviceNode);
+                                try {
+                                    processScheduler.enableControllerService(serviceNode);
+                                } catch (final Exception e) {
+                                    logger.error("Failed to enable " + serviceNode + " due to " + e);
+                                    if ( logger.isDebugEnabled() ) {
+                                        logger.error("", e);
+                                    }
+                                    
+                                    if ( bulletinRepo != null ) {
+                                        bulletinRepo.addBulletin(BulletinFactory.createBulletin(
+                                            "Controller Service", Severity.ERROR.name(), "Could not start " + serviceNode + " due to " + e));
+                                    }
+                                }
+                            }
+                            
+                            // wait for service to finish enabling.
+                            while ( ControllerServiceState.ENABLING.equals(serviceNode.getState()) ) {
+                                try {
+                                    Thread.sleep(100L);
+                                } catch (final InterruptedException ie) {}
+                            }
+                            
+                            logger.info("State for {} is now {}", serviceNode, serviceNode.getState());
+                        } catch (final Exception e) {
+                            logger.error("Failed to enable {} due to {}", serviceNode, e.toString());
+                            if ( logger.isDebugEnabled() ) {
+                                logger.error("", e);
+                            }
+                        }
+                    }
+                }
+            };
+            
+            executor.submit(enableBranchRunnable);
+        }
+        
+        executor.shutdown();
+    }
+    
+    static List<List<ControllerServiceNode>> determineEnablingOrder(final Map<String, ControllerServiceNode> serviceNodeMap) {
+        final List<List<ControllerServiceNode>> orderedNodeLists = new ArrayList<>();
+        
+        for ( final ControllerServiceNode node : serviceNodeMap.values() ) {
+            if ( orderedNodeLists.contains(node) ) {
+                continue;   // this node is already in the list.
+            }
+            
+            final List<ControllerServiceNode> branch = new ArrayList<>();
+            determineEnablingOrder(serviceNodeMap, node, branch, new HashSet<ControllerServiceNode>());
+            orderedNodeLists.add(branch);
+        }
+        
+        return orderedNodeLists;
+    }
+    
+    
+    private static void determineEnablingOrder(final Map<String, ControllerServiceNode> serviceNodeMap, final ControllerServiceNode contextNode, final List<ControllerServiceNode> orderedNodes, final Set<ControllerServiceNode> visited) {
+        if ( visited.contains(contextNode) ) {
+            return;
         }
         
-        serviceNode.setDisabled(false);
+        for ( final Map.Entry<PropertyDescriptor, String> entry : contextNode.getProperties().entrySet() ) {
+            if ( entry.getKey().getControllerServiceDefinition() != null ) {
+                final String referencedServiceId = entry.getValue();
+                if ( referencedServiceId != null ) {
+                    final ControllerServiceNode referencedNode = serviceNodeMap.get(referencedServiceId);
+                    if ( !orderedNodes.contains(referencedNode) ) {
+                        visited.add(contextNode);
+                        determineEnablingOrder(serviceNodeMap, referencedNode, orderedNodes, visited);
+                    }
+                }
+            }
+        }
+
+        if ( !orderedNodes.contains(contextNode) ) {
+            orderedNodes.add(contextNode);
+        }
     }
     
+    
     @Override
     public void disableControllerService(final ControllerServiceNode serviceNode) {
         serviceNode.verifyCanDisable();
-
-        // We must set the service to disabled before we invoke the OnDisabled methods because the service node
-        // can throw Exceptions if we attempt to disable the service while it's known to be in use.
-        serviceNode.setDisabled(true);
-        
-        try (final NarCloseable x = NarCloseable.withNarLoader()) {
-            ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnDisabled.class, serviceNode.getControllerServiceImplementation());
-        }
+        processScheduler.disableControllerService(serviceNode);
     }
 
     @Override
@@ -213,10 +453,16 @@ public class StandardControllerServiceProvider implements ControllerServiceProvi
     @Override
     public boolean isControllerServiceEnabled(final String serviceIdentifier) {
         final ControllerServiceNode node = controllerServices.get(serviceIdentifier);
-        return (node == null) ? false : !node.isDisabled();
+        return (node == null) ? false : (ControllerServiceState.ENABLED == node.getState());
     }
 
     @Override
+    public boolean isControllerServiceEnabling(final String serviceIdentifier) {
+        final ControllerServiceNode node = controllerServices.get(serviceIdentifier);
+        return (node == null) ? false : (ControllerServiceState.ENABLING == node.getState());
+    }
+    
+    @Override
     public ControllerServiceNode getControllerServiceNode(final String serviceIdentifier) {
         return controllerServices.get(serviceIdentifier);
     }
@@ -234,6 +480,11 @@ public class StandardControllerServiceProvider implements ControllerServiceProvi
     }
     
     @Override
+    public String getControllerServiceName(final String serviceIdentifier) {
+    	final ControllerServiceNode node = getControllerServiceNode(serviceIdentifier);
+    	return node == null ? null : node.getName();
+    }
+    
     public void removeControllerService(final ControllerServiceNode serviceNode) {
         final ControllerServiceNode existing = controllerServices.get(serviceNode.getIdentifier());
         if ( existing == null || existing != serviceNode ) {
@@ -247,6 +498,139 @@ public class StandardControllerServiceProvider implements ControllerServiceProvi
             ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnRemoved.class, serviceNode.getControllerServiceImplementation(), configurationContext);
         }
         
+        for ( final Map.Entry<PropertyDescriptor, String> entry : serviceNode.getProperties().entrySet() ) {
+            final PropertyDescriptor descriptor = entry.getKey();
+            if (descriptor.getControllerServiceDefinition() != null ) {
+                final String value = entry.getValue() == null ? descriptor.getDefaultValue() : entry.getValue();
+                if ( value != null ) {
+                    final ControllerServiceNode referencedNode = getControllerServiceNode(value);
+                    if ( referencedNode != null ) {
+                        referencedNode.removeReference(serviceNode);
+                    }
+                }
+            }
+        }
+        
         controllerServices.remove(serviceNode.getIdentifier());
     }
+    
+    @Override
+    public Set<ControllerServiceNode> getAllControllerServices() {
+    	return new HashSet<>(controllerServices.values());
+    }
+    
+    
+    /**
+     * Returns a List of all components that reference the given referencedNode (either directly or indirectly through
+     * another service) that are also of the given componentType. The list that is returned is in the order in which they will
+     * need to be 'activated' (enabled/started).
+     * @param referencedNode
+     * @param componentType
+     * @return
+     */
+    private <T> List<T> findRecursiveReferences(final ControllerServiceNode referencedNode, final Class<T> componentType) {
+        final List<T> references = new ArrayList<>();
+        
+        for ( final ConfiguredComponent referencingComponent : referencedNode.getReferences().getReferencingComponents() ) {
+            if ( componentType.isAssignableFrom(referencingComponent.getClass()) ) {
+                references.add(componentType.cast(referencingComponent));
+            }
+            
+            if ( referencingComponent instanceof ControllerServiceNode ) {
+                final ControllerServiceNode referencingNode = (ControllerServiceNode) referencingComponent;
+                
+                // find components recursively that depend on referencingNode.
+                final List<T> recursive = findRecursiveReferences(referencingNode, componentType);
+                
+                // For anything that depends on referencing node, we want to add it to the list, but we know
+                // that it must come after the referencing node, so we first remove any existing occurrence.
+                references.removeAll(recursive);
+                references.addAll(recursive);
+            }
+        }
+        
+        return references;
+    }
+
+    
+    @Override
+    public void enableReferencingServices(final ControllerServiceNode serviceNode) {
+        final List<ControllerServiceNode> recursiveReferences = findRecursiveReferences(serviceNode, ControllerServiceNode.class);
+        enableReferencingServices(serviceNode, recursiveReferences);
+    }
+    
+    private void enableReferencingServices(final ControllerServiceNode serviceNode, final List<ControllerServiceNode> recursiveReferences) {
+        if ( serviceNode.getState() != ControllerServiceState.ENABLED && serviceNode.getState() != ControllerServiceState.ENABLING ) {
+            serviceNode.verifyCanEnable(new HashSet<>(recursiveReferences));
+        }
+        
+        final Set<ControllerServiceNode> ifEnabled = new HashSet<>();
+        final List<ControllerServiceNode> toEnable = findRecursiveReferences(serviceNode, ControllerServiceNode.class);
+        for ( final ControllerServiceNode nodeToEnable : toEnable ) {
+            final ControllerServiceState state = nodeToEnable.getState();
+            if ( state != ControllerServiceState.ENABLED && state != ControllerServiceState.ENABLING ) {
+                nodeToEnable.verifyCanEnable(ifEnabled);
+                ifEnabled.add(nodeToEnable);
+            }
+        }
+        
+        for ( final ControllerServiceNode nodeToEnable : toEnable ) {
+            final ControllerServiceState state = nodeToEnable.getState();
+            if ( state != ControllerServiceState.ENABLED && state != ControllerServiceState.ENABLING ) {
+                enableControllerService(nodeToEnable);
+            }
+        }
+    }
+    
+    @Override
+    public void verifyCanEnableReferencingServices(final ControllerServiceNode serviceNode) {
+        final List<ControllerServiceNode> referencingServices = findRecursiveReferences(serviceNode, ControllerServiceNode.class);
+        final Set<ControllerServiceNode> referencingServiceSet = new HashSet<>(referencingServices);
+        
+        for ( final ControllerServiceNode referencingService : referencingServices ) {
+            referencingService.verifyCanEnable(referencingServiceSet);
+        }
+    }
+    
+    @Override
+    public void verifyCanScheduleReferencingComponents(final ControllerServiceNode serviceNode) {
+        final List<ControllerServiceNode> referencingServices = findRecursiveReferences(serviceNode, ControllerServiceNode.class);
+        final List<ReportingTaskNode> referencingReportingTasks = findRecursiveReferences(serviceNode, ReportingTaskNode.class);
+        final List<ProcessorNode> referencingProcessors = findRecursiveReferences(serviceNode, ProcessorNode.class);
+        
+        final Set<ControllerServiceNode> referencingServiceSet = new HashSet<>(referencingServices);
+        
+        for ( final ReportingTaskNode taskNode : referencingReportingTasks ) {
+            if ( taskNode.getScheduledState() != ScheduledState.DISABLED ) {
+                taskNode.verifyCanStart(referencingServiceSet);
+            }
+        }
+        
+        for ( final ProcessorNode procNode : referencingProcessors ) {
+            if ( procNode.getScheduledState() != ScheduledState.DISABLED ) {
+                procNode.verifyCanStart(referencingServiceSet);
+            }
+        }
+    }
+    
+    @Override
+    public void verifyCanDisableReferencingServices(final ControllerServiceNode serviceNode) {
+        // Get a list of all Controller Services that need to be disabled, in the order that they need to be
+        // disabled.
+        final List<ControllerServiceNode> toDisable = findRecursiveReferences(serviceNode, ControllerServiceNode.class);
+        final Set<ControllerServiceNode> serviceSet = new HashSet<>(toDisable);
+        
+        for ( final ControllerServiceNode nodeToDisable : toDisable ) {
+            final ControllerServiceState state = nodeToDisable.getState();
+            
+            if ( state != ControllerServiceState.DISABLED && state != ControllerServiceState.DISABLING ) {
+                nodeToDisable.verifyCanDisable(serviceSet);
+            }
+        }
+    }
+    
+    @Override
+    public void verifyCanStopReferencingComponents(final ControllerServiceNode serviceNode) {
+        // we can always stop referencing components
+    }
 }


[15/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
index 14640d8..086c46b 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
@@ -106,7 +106,7 @@ import org.apache.nifi.web.api.dto.PreviousValueDTO;
 import org.apache.nifi.web.api.dto.ProcessGroupDTO;
 import org.apache.nifi.web.api.dto.ProcessorConfigDTO;
 import org.apache.nifi.web.api.dto.ProcessorDTO;
-import org.apache.nifi.web.api.dto.ProcessorHistoryDTO;
+import org.apache.nifi.web.api.dto.ComponentHistoryDTO;
 import org.apache.nifi.web.api.dto.PropertyHistoryDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupPortDTO;
@@ -152,6 +152,19 @@ import org.apache.nifi.web.util.SnippetUtils;
 
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.Validator;
+import org.apache.nifi.controller.ReportingTaskNode;
+import org.apache.nifi.controller.ScheduledState;
+import org.apache.nifi.controller.service.ControllerServiceNode;
+import org.apache.nifi.controller.service.ControllerServiceReference;
+import org.apache.nifi.controller.service.ControllerServiceState;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
+import org.apache.nifi.web.api.dto.ControllerServiceReferencingComponentDTO;
+import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
+import org.apache.nifi.web.api.dto.ReportingTaskDTO;
+import org.apache.nifi.web.dao.ControllerServiceDAO;
+import org.apache.nifi.web.dao.ReportingTaskDAO;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.access.AccessDeniedException;
@@ -162,8 +175,6 @@ import org.springframework.security.access.AccessDeniedException;
 public class StandardNiFiServiceFacade implements NiFiServiceFacade {
 
     private static final Logger logger = LoggerFactory.getLogger(StandardNiFiServiceFacade.class);
-    private static final String INVALID_REVISION_ERROR = "Given revision %s does not match current revision %s.";
-    private static final String SYNC_ERROR = "This NiFi instance has been updated by '%s'. Please refresh to synchronize the view.";
 
     // nifi core components
     private ControllerFacade controllerFacade;
@@ -182,6 +193,8 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     private PortDAO inputPortDAO;
     private PortDAO outputPortDAO;
     private ConnectionDAO connectionDAO;
+    private ControllerServiceDAO controllerServiceDAO;
+    private ReportingTaskDAO reportingTaskDAO;
     private TemplateDAO templateDAO;
 
     // administrative services
@@ -195,54 +208,10 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     private NiFiProperties properties;
     private DtoFactory dtoFactory;
 
-    /**
-     * Checks the specified revision against the current revision.
-     *
-     * @param revision The revision to check
-     * @param clientId The client id
-     * @return Whether or not the request should proceed
-     * @throws NiFiCoreException If the specified revision is not current
-     */
-    private void checkRevision(Revision revision) {
-
-        boolean approved = optimisticLockingManager.isCurrent(revision);
-
-        if (!approved) {
-            Revision currentRevision = optimisticLockingManager.getRevision();
-            logger.debug("Revision check failed because current revision is " + currentRevision + " but supplied revision is " + revision);
-
-            if (StringUtils.isBlank(currentRevision.getClientId()) || currentRevision.getVersion() == null) {
-                throw new InvalidRevisionException(String.format(INVALID_REVISION_ERROR, revision, currentRevision));
-            } else {
-                throw new InvalidRevisionException(String.format(SYNC_ERROR, optimisticLockingManager.getLastModifier()));
-            }
-        }
-    }
-
-    /**
-     * Increments the revision and updates the last modifier.
-     *
-     * @param revision
-     * @return
-     */
-    private Revision updateRevision(Revision revision) {
-        // update the client id and modifier
-        final Revision updatedRevision = optimisticLockingManager.incrementRevision(revision.getClientId());
-
-        // get the nifi user to extract the username
-        NiFiUser user = NiFiUserUtils.getNiFiUser();
-        if (user == null) {
-            optimisticLockingManager.setLastModifier("unknown");
-        } else {
-            optimisticLockingManager.setLastModifier(user.getUserName());
-        }
-
-        return updatedRevision;
-    }
-
     // -----------------------------------------
     // Verification Operations
     // -----------------------------------------
+    
     @Override
     public void verifyCreateConnection(String groupId, ConnectionDTO connectionDTO) {
         connectionDAO.verifyCreate(groupId, connectionDTO);
@@ -360,100 +329,123 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
         remoteProcessGroupDAO.verifyDelete(groupId, remoteProcessGroupId);
     }
 
+    @Override
+    public void verifyUpdateControllerService(ControllerServiceDTO controllerServiceDTO) {
+        // if service does not exist, then the update request is likely creating it
+        // so we don't verify since it will fail
+        if (controllerServiceDAO.hasControllerService(controllerServiceDTO.getId())) {
+            controllerServiceDAO.verifyUpdate(controllerServiceDTO);
+        }
+    }
+
+    @Override
+    public void verifyUpdateControllerServiceReferencingComponents(String controllerServiceId, ScheduledState scheduledState, ControllerServiceState controllerServiceState) {
+        controllerServiceDAO.verifyUpdateReferencingComponents(controllerServiceId, scheduledState, controllerServiceState);
+    }
+
+    @Override
+    public void verifyDeleteControllerService(String controllerServiceId) {
+        controllerServiceDAO.verifyDelete(controllerServiceId);
+    }
+
+    @Override
+    public void verifyUpdateReportingTask(ReportingTaskDTO reportingTaskDTO) {
+        // if tasks does not exist, then the update request is likely creating it
+        // so we don't verify since it will fail
+        if (reportingTaskDAO.hasReportingTask(reportingTaskDTO.getId())) {
+            reportingTaskDAO.verifyUpdate(reportingTaskDTO);
+        }
+    }
+
+    @Override
+    public void verifyDeleteReportingTask(String reportingTaskId) {
+        reportingTaskDAO.verifyDelete(reportingTaskId);
+    }
+
     // -----------------------------------------
     // Write Operations
     // -----------------------------------------
+    
     @Override
-    public ConfigurationSnapshot<ConnectionDTO> updateConnection(Revision revision, String groupId, ConnectionDTO connectionDTO) {
-
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
+    public ConfigurationSnapshot<ConnectionDTO> updateConnection(final Revision revision, final String groupId, final ConnectionDTO connectionDTO) {
         // if connection does not exist, then create new connection
         if (connectionDAO.hasConnection(groupId, connectionDTO.getId()) == false) {
             return createConnection(revision, groupId, connectionDTO);
         }
+        
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<ConnectionDTO>() {
+            @Override
+            public ConnectionDTO execute() {
+                final Connection connection = connectionDAO.updateConnection(groupId, connectionDTO);
 
-        final Connection connection = connectionDAO.updateConnection(groupId, connectionDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<ConnectionDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createConnectionDto(connection));
-
-        // save the flow
-        controllerFacade.save();
-
-        return response;
+                controllerFacade.save();
+                
+                return dtoFactory.createConnectionDto(connection);
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<ProcessorDTO> updateProcessor(Revision revision, String groupId, ProcessorDTO processorDTO) {
-
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
+    public ConfigurationSnapshot<ProcessorDTO> updateProcessor(final Revision revision, final String groupId, final ProcessorDTO processorDTO) {
         // if processor does not exist, then create new processor
         if (processorDAO.hasProcessor(groupId, processorDTO.getId()) == false) {
             return createProcessor(revision, groupId, processorDTO);
         }
+        
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<ProcessorDTO>() {
+            @Override
+            public ProcessorDTO execute() {
+                // update the processor
+                ProcessorNode processor = processorDAO.updateProcessor(groupId, processorDTO);
 
-        // update the processor
-        ProcessorNode processor = processorDAO.updateProcessor(groupId, processorDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<ProcessorDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createProcessorDto(processor));
-
-        // save the flow
-        controllerFacade.save();
-
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return dtoFactory.createProcessorDto(processor);
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<LabelDTO> updateLabel(Revision revision, String groupId, LabelDTO labelDTO) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
+    public ConfigurationSnapshot<LabelDTO> updateLabel(final Revision revision, final String groupId, final LabelDTO labelDTO) {
         // if label does not exist, then create new label
         if (labelDAO.hasLabel(groupId, labelDTO.getId()) == false) {
             return createLabel(revision, groupId, labelDTO);
         }
+        
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<LabelDTO>() {
+            @Override
+            public LabelDTO execute() {
+                // update the existing label
+                final Label label = labelDAO.updateLabel(groupId, labelDTO);
 
-        // update the existing label
-        final Label label = labelDAO.updateLabel(groupId, labelDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<LabelDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createLabelDto(label));
-
-        // save updated controller
-        controllerFacade.save();
-
-        return response;
+                // save updated controller
+                controllerFacade.save();
+                
+                return dtoFactory.createLabelDto(label);
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<FunnelDTO> updateFunnel(Revision revision, String groupId, FunnelDTO funnelDTO) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
+    public ConfigurationSnapshot<FunnelDTO> updateFunnel(final Revision revision, final String groupId, final FunnelDTO funnelDTO) {
         // if label does not exist, then create new label
         if (funnelDAO.hasFunnel(groupId, funnelDTO.getId()) == false) {
             return createFunnel(revision, groupId, funnelDTO);
         }
+        
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<FunnelDTO>() {
+            @Override
+            public FunnelDTO execute() {
+                // update the existing label
+                final Funnel funnel = funnelDAO.updateFunnel(groupId, funnelDTO);
 
-        // update the existing label
-        final Funnel funnel = funnelDAO.updateFunnel(groupId, funnelDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<FunnelDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createFunnelDto(funnel));
-
-        // save updated controller
-        controllerFacade.save();
-
-        return response;
+                // save updated controller
+                controllerFacade.save();
+                
+                return dtoFactory.createFunnelDto(funnel);
+            }
+        });
     }
 
     @Override
@@ -466,141 +458,126 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     }
 
     @Override
-    public ConfigurationSnapshot<SnippetDTO> updateSnippet(Revision revision, SnippetDTO snippetDto) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
+    public ConfigurationSnapshot<SnippetDTO> updateSnippet(final Revision revision, final SnippetDTO snippetDto) {
         // if label does not exist, then create new label
         if (snippetDAO.hasSnippet(snippetDto.getId()) == false) {
             return createSnippet(revision, snippetDto);
         }
+        
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<SnippetDTO>() {
+            @Override
+            public SnippetDTO execute() {
+                // update the snippet
+                final Snippet snippet = snippetDAO.updateSnippet(snippetDto);
 
-        // update the snippet
-        final Snippet snippet = snippetDAO.updateSnippet(snippetDto);
+                // build the snippet dto
+                final SnippetDTO responseSnippetDto = dtoFactory.createSnippetDto(snippet);
+                responseSnippetDto.setContents(snippetUtils.populateFlowSnippet(snippet, false));
 
-        // build the snippet dto
-        final SnippetDTO responseSnippetDto = dtoFactory.createSnippetDto(snippet);
-        responseSnippetDto.setContents(snippetUtils.populateFlowSnippet(snippet, false));
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<SnippetDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), responseSnippetDto);
-
-        // save updated controller if applicable
-        if (snippetDto.getParentGroupId() != null && snippet.isLinked()) {
-            controllerFacade.save();
-        }
-
-        return response;
+                // save updated controller if applicable
+                if (snippetDto.getParentGroupId() != null && snippet.isLinked()) {
+                    controllerFacade.save();
+                }
+                
+                return responseSnippetDto;
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<PortDTO> updateInputPort(Revision revision, String groupId, PortDTO inputPortDTO) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
+    public ConfigurationSnapshot<PortDTO> updateInputPort(final Revision revision, final String groupId, final PortDTO inputPortDTO) {
         // if input port does not exist, then create new input port
         if (inputPortDAO.hasPort(groupId, inputPortDTO.getId()) == false) {
             return createInputPort(revision, groupId, inputPortDTO);
         }
+        
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<PortDTO>() {
+            @Override
+            public PortDTO execute() {
+                final Port inputPort = inputPortDAO.updatePort(groupId, inputPortDTO);
 
-        final Port inputPort = inputPortDAO.updatePort(groupId, inputPortDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<PortDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createPortDto(inputPort));
-
-        // save updated controller
-        controllerFacade.save();
-
-        return response;
+                // save updated controller
+                controllerFacade.save();
+                
+                return  dtoFactory.createPortDto(inputPort);
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<PortDTO> updateOutputPort(Revision revision, String groupId, PortDTO outputPortDTO) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
+    public ConfigurationSnapshot<PortDTO> updateOutputPort(final Revision revision, final String groupId, final PortDTO outputPortDTO) {
         // if output port does not exist, then create new output port
         if (outputPortDAO.hasPort(groupId, outputPortDTO.getId()) == false) {
             return createOutputPort(revision, groupId, outputPortDTO);
         }
+        
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<PortDTO>() {
+            @Override
+            public PortDTO execute() {
+                final Port outputPort = outputPortDAO.updatePort(groupId, outputPortDTO);
 
-        final Port outputPort = outputPortDAO.updatePort(groupId, outputPortDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<PortDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createPortDto(outputPort));
-
-        // save updated controller
-        controllerFacade.save();
-
-        return response;
+                // save updated controller
+                controllerFacade.save();
+                
+                return dtoFactory.createPortDto(outputPort);
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<RemoteProcessGroupDTO> updateRemoteProcessGroup(Revision revision, String groupId, RemoteProcessGroupDTO remoteProcessGroupDTO) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
+    public ConfigurationSnapshot<RemoteProcessGroupDTO> updateRemoteProcessGroup(final Revision revision, final String groupId, final RemoteProcessGroupDTO remoteProcessGroupDTO) {
         // if controller reference does not exist, then create new controller reference
         if (remoteProcessGroupDAO.hasRemoteProcessGroup(groupId, remoteProcessGroupDTO.getId()) == false) {
             return createRemoteProcessGroup(revision, groupId, remoteProcessGroupDTO);
         }
+        
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<RemoteProcessGroupDTO>() {
+            @Override
+            public RemoteProcessGroupDTO execute() {
+                RemoteProcessGroup remoteProcessGroup = remoteProcessGroupDAO.updateRemoteProcessGroup(groupId, remoteProcessGroupDTO);
 
-        RemoteProcessGroup remoteProcessGroup = remoteProcessGroupDAO.updateRemoteProcessGroup(groupId, remoteProcessGroupDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<RemoteProcessGroupDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createRemoteProcessGroupDto(remoteProcessGroup));
-
-        // save updated controller
-        controllerFacade.save();
-
-        return response;
+                // save updated controller
+                controllerFacade.save();
+                
+                return dtoFactory.createRemoteProcessGroupDto(remoteProcessGroup);
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<RemoteProcessGroupPortDTO> updateRemoteProcessGroupInputPort(Revision revision, String groupId, String remoteProcessGroupId, RemoteProcessGroupPortDTO remoteProcessGroupPortDTO) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        // update the remote port
-        RemoteGroupPort remoteGroupPort = remoteProcessGroupDAO.updateRemoteProcessGroupInputPort(groupId, remoteProcessGroupId, remoteProcessGroupPortDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<RemoteProcessGroupPortDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createRemoteProcessGroupPortDto(remoteGroupPort));
+    public ConfigurationSnapshot<RemoteProcessGroupPortDTO> updateRemoteProcessGroupInputPort(final Revision revision, final String groupId, final String remoteProcessGroupId, final RemoteProcessGroupPortDTO remoteProcessGroupPortDTO) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<RemoteProcessGroupPortDTO>() {
+            @Override
+            public RemoteProcessGroupPortDTO execute() {
+                // update the remote port
+                RemoteGroupPort remoteGroupPort = remoteProcessGroupDAO.updateRemoteProcessGroupInputPort(groupId, remoteProcessGroupId, remoteProcessGroupPortDTO);
 
-        // save updated controller
-        controllerFacade.save();
-
-        return response;
+                // save updated controller
+                controllerFacade.save();
+                
+                return dtoFactory.createRemoteProcessGroupPortDto(remoteGroupPort);
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<RemoteProcessGroupPortDTO> updateRemoteProcessGroupOutputPort(Revision revision, String groupId, String remoteProcessGroupId, RemoteProcessGroupPortDTO remoteProcessGroupPortDTO) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        // update the remote port
-        RemoteGroupPort remoteGroupPort = remoteProcessGroupDAO.updateRemoteProcessGroupOutputPort(groupId, remoteProcessGroupId, remoteProcessGroupPortDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<RemoteProcessGroupPortDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createRemoteProcessGroupPortDto(remoteGroupPort));
+    public ConfigurationSnapshot<RemoteProcessGroupPortDTO> updateRemoteProcessGroupOutputPort(final Revision revision, final String groupId, final String remoteProcessGroupId, final RemoteProcessGroupPortDTO remoteProcessGroupPortDTO) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<RemoteProcessGroupPortDTO>() {
+            @Override
+            public RemoteProcessGroupPortDTO execute() {
+                // update the remote port
+                RemoteGroupPort remoteGroupPort = remoteProcessGroupDAO.updateRemoteProcessGroupOutputPort(groupId, remoteProcessGroupId, remoteProcessGroupPortDTO);
 
-        // save updated controller
-        controllerFacade.save();
-
-        return response;
+                // save updated controller
+                controllerFacade.save();
+                
+                return dtoFactory.createRemoteProcessGroupPortDto(remoteGroupPort);
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<ProcessGroupDTO> updateProcessGroup(Revision revision, String parentGroupId, ProcessGroupDTO processGroupDTO) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
+    public ConfigurationSnapshot<ProcessGroupDTO> updateProcessGroup(final Revision revision, final String parentGroupId, final ProcessGroupDTO processGroupDTO) {
         // if process group does not exist, then create new process group
         if (processGroupDAO.hasProcessGroup(processGroupDTO.getId()) == false) {
             if (parentGroupId == null) {
@@ -609,50 +586,49 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
                 return createProcessGroup(parentGroupId, revision, processGroupDTO);
             }
         }
+        
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<ProcessGroupDTO>() {
+            @Override
+            public ProcessGroupDTO execute() {
+                // update the process group
+                ProcessGroup processGroup = processGroupDAO.updateProcessGroup(processGroupDTO);
 
-        // update the process group
-        ProcessGroup processGroup = processGroupDAO.updateProcessGroup(processGroupDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<ProcessGroupDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createProcessGroupDto(processGroup));
-
-        // save updated controller
-        controllerFacade.save();
-
-        return response;
+                // save updated controller
+                controllerFacade.save();
+                
+                return dtoFactory.createProcessGroupDto(processGroup);
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<ControllerConfigurationDTO> updateControllerConfiguration(Revision revision, ControllerConfigurationDTO controllerConfigurationDTO) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        // update the controller configuration through the proxy
-        if (controllerConfigurationDTO.getName() != null) {
-            controllerFacade.setName(controllerConfigurationDTO.getName());
-        }
-        if (controllerConfigurationDTO.getComments() != null) {
-            controllerFacade.setComments(controllerConfigurationDTO.getComments());
-        }
-        if (controllerConfigurationDTO.getMaxTimerDrivenThreadCount() != null) {
-            controllerFacade.setMaxTimerDrivenThreadCount(controllerConfigurationDTO.getMaxTimerDrivenThreadCount());
-        }
-        if (controllerConfigurationDTO.getMaxEventDrivenThreadCount() != null) {
-            controllerFacade.setMaxEventDrivenThreadCount(controllerConfigurationDTO.getMaxEventDrivenThreadCount());
-        }
-
-        // create the controller configuration dto
-        ControllerConfigurationDTO controllerConfig = getControllerConfiguration();
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<ControllerConfigurationDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), controllerConfig);
+    public ConfigurationSnapshot<ControllerConfigurationDTO> updateControllerConfiguration(final Revision revision, final ControllerConfigurationDTO controllerConfigurationDTO) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<ControllerConfigurationDTO>() {
+            @Override
+            public ControllerConfigurationDTO execute() {
+                // update the controller configuration through the proxy
+                if (controllerConfigurationDTO.getName() != null) {
+                    controllerFacade.setName(controllerConfigurationDTO.getName());
+                }
+                if (controllerConfigurationDTO.getComments() != null) {
+                    controllerFacade.setComments(controllerConfigurationDTO.getComments());
+                }
+                if (controllerConfigurationDTO.getMaxTimerDrivenThreadCount() != null) {
+                    controllerFacade.setMaxTimerDrivenThreadCount(controllerConfigurationDTO.getMaxTimerDrivenThreadCount());
+                }
+                if (controllerConfigurationDTO.getMaxEventDrivenThreadCount() != null) {
+                    controllerFacade.setMaxEventDrivenThreadCount(controllerConfigurationDTO.getMaxEventDrivenThreadCount());
+                }
 
-        // save the flow
-        controllerFacade.save();
+                // create the controller configuration dto
+                ControllerConfigurationDTO controllerConfig = getControllerConfiguration();
 
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return controllerConfig;
+            }
+        });
     }
 
     @Override
@@ -685,74 +661,66 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     }
 
     @Override
-    public ConfigurationSnapshot<Void> deleteConnection(Revision revision, String groupId, String connectionId) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        connectionDAO.deleteConnection(groupId, connectionId);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<Void> response = new ConfigurationSnapshot<>(updatedRevision.getVersion());
+    public ConfigurationSnapshot<Void> deleteConnection(final Revision revision, final String groupId, final String connectionId) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<Void>(){
+            @Override
+            public Void execute() {
+                connectionDAO.deleteConnection(groupId, connectionId);
 
-        // save the flow
-        controllerFacade.save();
-
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return null;
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<Void> deleteProcessor(Revision revision, String groupId, String processorId) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        // delete the processor and synchronize the connection state
-        processorDAO.deleteProcessor(groupId, processorId);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<Void> response = new ConfigurationSnapshot<>(updatedRevision.getVersion());
+    public ConfigurationSnapshot<Void> deleteProcessor(final Revision revision, final String groupId, final String processorId) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<Void>() {
+            @Override
+            public Void execute() {
+                // delete the processor and synchronize the connection state
+                processorDAO.deleteProcessor(groupId, processorId);
 
-        // save the flow
-        controllerFacade.save();
-
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return null;
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<Void> deleteLabel(Revision revision, String groupId, String labelId) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        // delete the label
-        labelDAO.deleteLabel(groupId, labelId);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<Void> response = new ConfigurationSnapshot<>(updatedRevision.getVersion());
-
-        // save the flow
-        controllerFacade.save();
+    public ConfigurationSnapshot<Void> deleteLabel(final Revision revision, final String groupId, final String labelId) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<Void>() {
+            @Override
+            public Void execute() {
+                // delete the label
+                labelDAO.deleteLabel(groupId, labelId);
 
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return null;
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<Void> deleteFunnel(Revision revision, String groupId, String funnelId) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        // delete the label
-        funnelDAO.deleteFunnel(groupId, funnelId);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<Void> response = new ConfigurationSnapshot<>(updatedRevision.getVersion());
-
-        // save the flow
-        controllerFacade.save();
+    public ConfigurationSnapshot<Void> deleteFunnel(final Revision revision, final String groupId, final String funnelId) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<Void>() {
+            @Override
+            public Void execute() {
+                // delete the label
+                funnelDAO.deleteFunnel(groupId, funnelId);
 
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return null;
+            }
+        });
     }
 
     @Override
@@ -761,95 +729,85 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     }
 
     @Override
-    public ConfigurationSnapshot<Void> deleteSnippet(Revision revision, String snippetId) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        // determine if this snippet was linked to the data flow
-        Snippet snippet = snippetDAO.getSnippet(snippetId);
-        boolean linked = snippet.isLinked();
-
-        // delete the snippet
-        snippetDAO.deleteSnippet(snippetId);
+    public ConfigurationSnapshot<Void> deleteSnippet(final Revision revision, final String snippetId) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<Void>() {
+            @Override
+            public Void execute() {
+                // determine if this snippet was linked to the data flow
+                Snippet snippet = snippetDAO.getSnippet(snippetId);
+                boolean linked = snippet.isLinked();
 
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<Void> response = new ConfigurationSnapshot<>(updatedRevision.getVersion());
+                // delete the snippet
+                snippetDAO.deleteSnippet(snippetId);
 
-        // save the flow if necessary
-        if (linked) {
-            controllerFacade.save();
-        }
-
-        return response;
+                // save the flow if necessary
+                if (linked) {
+                    controllerFacade.save();
+                }
+                
+                return null;
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<Void> deleteInputPort(Revision revision, String groupId, String inputPortId) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        inputPortDAO.deletePort(groupId, inputPortId);
+    public ConfigurationSnapshot<Void> deleteInputPort(final Revision revision, final String groupId, final String inputPortId) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<Void>() {
+            @Override
+            public Void execute() {
+                inputPortDAO.deletePort(groupId, inputPortId);
 
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<Void> response = new ConfigurationSnapshot<>(updatedRevision.getVersion());
-
-        // save the flow
-        controllerFacade.save();
-
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return null;
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<Void> deleteOutputPort(Revision revision, String groupId, String outputPortId) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        outputPortDAO.deletePort(groupId, outputPortId);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<Void> response = new ConfigurationSnapshot<>(updatedRevision.getVersion());
+    public ConfigurationSnapshot<Void> deleteOutputPort(final Revision revision, final String groupId, final String outputPortId) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<Void>() {
+            @Override
+            public Void execute() {
+                outputPortDAO.deletePort(groupId, outputPortId);
 
-        // save the flow
-        controllerFacade.save();
-
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return null;
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<Void> deleteProcessGroup(Revision revision, String groupId) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        processGroupDAO.deleteProcessGroup(groupId);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<Void> response = new ConfigurationSnapshot<>(updatedRevision.getVersion());
+    public ConfigurationSnapshot<Void> deleteProcessGroup(final Revision revision, final String groupId) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<Void>() {
+            @Override
+            public Void execute() {
+                processGroupDAO.deleteProcessGroup(groupId);
 
-        // save the flow
-        controllerFacade.save();
-
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return null;
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<Void> deleteRemoteProcessGroup(Revision revision, String groupId, String remoteProcessGroupId) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        remoteProcessGroupDAO.deleteRemoteProcessGroup(groupId, remoteProcessGroupId);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<Void> response = new ConfigurationSnapshot<>(updatedRevision.getVersion());
+    public ConfigurationSnapshot<Void> deleteRemoteProcessGroup(final Revision revision, final String groupId, final String remoteProcessGroupId) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<Void>() {
+            @Override
+            public Void execute() {
+                remoteProcessGroupDAO.deleteRemoteProcessGroup(groupId, remoteProcessGroupId);
 
-        // save the flow
-        controllerFacade.save();
-
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return null;
+            }
+        });
     }
 
     @Override
@@ -859,97 +817,86 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     }
 
     @Override
-    public ConfigurationSnapshot<ConnectionDTO> createConnection(Revision revision, String groupId, ConnectionDTO connectionDTO) {
-
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        // ensure id is set
-        if (StringUtils.isBlank(connectionDTO.getId())) {
-            connectionDTO.setId(UUID.randomUUID().toString());
-        }
-
-        final Connection connection = connectionDAO.createConnection(groupId, connectionDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<ConnectionDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createConnectionDto(connection));
+    public ConfigurationSnapshot<ConnectionDTO> createConnection(final Revision revision, final String groupId, final ConnectionDTO connectionDTO) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<ConnectionDTO>() {
+            @Override
+            public ConnectionDTO execute() {
+                // ensure id is set
+                if (StringUtils.isBlank(connectionDTO.getId())) {
+                    connectionDTO.setId(UUID.randomUUID().toString());
+                }
 
-        // save the flow
-        controllerFacade.save();
+                final Connection connection = connectionDAO.createConnection(groupId, connectionDTO);
 
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return dtoFactory.createConnectionDto(connection);
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<ProcessorDTO> createProcessor(Revision revision, String groupId, ProcessorDTO processorDTO) {
-
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        // ensure id is set
-        if (StringUtils.isBlank(processorDTO.getId())) {
-            processorDTO.setId(UUID.randomUUID().toString());
-        }
-
-        // create the processor
-        final ProcessorNode processor = processorDAO.createProcessor(groupId, processorDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<ProcessorDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createProcessorDto(processor));
+    public ConfigurationSnapshot<ProcessorDTO> createProcessor(final Revision revision, final String groupId, final ProcessorDTO processorDTO) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<ProcessorDTO>() {
+            @Override
+            public ProcessorDTO execute() {
+                // ensure id is set
+                if (StringUtils.isBlank(processorDTO.getId())) {
+                    processorDTO.setId(UUID.randomUUID().toString());
+                }
 
-        // save the flow
-        controllerFacade.save();
+                // create the processor
+                final ProcessorNode processor = processorDAO.createProcessor(groupId, processorDTO);
 
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return dtoFactory.createProcessorDto(processor);
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<LabelDTO> createLabel(Revision revision, String groupId, LabelDTO labelDTO) {
-
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        // ensure id is set
-        if (StringUtils.isBlank(labelDTO.getId())) {
-            labelDTO.setId(UUID.randomUUID().toString());
-        }
-
-        // add the label
-        final Label label = labelDAO.createLabel(groupId, labelDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<LabelDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createLabelDto(label));
+    public ConfigurationSnapshot<LabelDTO> createLabel(final Revision revision, final String groupId, final LabelDTO labelDTO) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<LabelDTO>() {
+            @Override
+            public LabelDTO execute() {
+                // ensure id is set
+                if (StringUtils.isBlank(labelDTO.getId())) {
+                    labelDTO.setId(UUID.randomUUID().toString());
+                }
 
-        // save the flow
-        controllerFacade.save();
+                // add the label
+                final Label label = labelDAO.createLabel(groupId, labelDTO);
 
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return dtoFactory.createLabelDto(label);
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<FunnelDTO> createFunnel(Revision revision, String groupId, FunnelDTO funnelDTO) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        // ensure id is set
-        if (StringUtils.isBlank(funnelDTO.getId())) {
-            funnelDTO.setId(UUID.randomUUID().toString());
-        }
-
-        // add the label
-        final Funnel funnel = funnelDAO.createFunnel(groupId, funnelDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<FunnelDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createFunnelDto(funnel));
+    public ConfigurationSnapshot<FunnelDTO> createFunnel(final Revision revision, final String groupId, final FunnelDTO funnelDTO) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<FunnelDTO>() {
+            @Override
+            public FunnelDTO execute() {
+                // ensure id is set
+                if (StringUtils.isBlank(funnelDTO.getId())) {
+                    funnelDTO.setId(UUID.randomUUID().toString());
+                }
 
-        // save the flow
-        controllerFacade.save();
+                // add the label
+                final Funnel funnel = funnelDAO.createFunnel(groupId, funnelDTO);
 
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return dtoFactory.createFunnelDto(funnel);
+            }
+        });
     }
 
     private void validateSnippetContents(final FlowSnippetDTO flowSnippet, final String groupId) {
@@ -1008,139 +955,129 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     }
 
     @Override
-    public ConfigurationSnapshot<FlowSnippetDTO> copySnippet(Revision revision, String groupId, String snippetId, Double originX, Double originY) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        // ensure id is set
-        if (StringUtils.isBlank(snippetId)) {
-            snippetId = UUID.randomUUID().toString();
-        }
-
-        // create the new snippet
-        FlowSnippetDTO flowSnippet = snippetDAO.copySnippet(groupId, snippetId, originX, originY);
-
-        // validate the new snippet
-        validateSnippetContents(flowSnippet, groupId);
+    public ConfigurationSnapshot<FlowSnippetDTO> copySnippet(final Revision revision, final String groupId, final String snippetId, final Double originX, final Double originY) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<FlowSnippetDTO>() {
+            @Override
+            public FlowSnippetDTO execute() {
+                String id = snippetId;
+                
+                // ensure id is set
+                if (StringUtils.isBlank(id)) {
+                    id = UUID.randomUUID().toString();
+                }
 
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<FlowSnippetDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), flowSnippet);
+                // create the new snippet
+                FlowSnippetDTO flowSnippet = snippetDAO.copySnippet(groupId, id, originX, originY);
 
-        // save the flow
-        controllerFacade.save();
+                // validate the new snippet
+                validateSnippetContents(flowSnippet, groupId);
 
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return flowSnippet;
+            }
+        });
     }
 
     @Override
     public ConfigurationSnapshot<SnippetDTO> createSnippet(final Revision revision, final SnippetDTO snippetDTO) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        // ensure id is set
-        if (StringUtils.isBlank(snippetDTO.getId())) {
-            snippetDTO.setId(UUID.randomUUID().toString());
-        }
-
-        // add the snippet
-        final Snippet snippet = snippetDAO.createSnippet(snippetDTO);
-        final SnippetDTO responseSnippetDTO = dtoFactory.createSnippetDto(snippet);
-        responseSnippetDTO.setContents(snippetUtils.populateFlowSnippet(snippet, false));
-
-        // create the response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<SnippetDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), responseSnippetDTO);
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<SnippetDTO>() {
+            @Override
+            public SnippetDTO execute() {
+                // ensure id is set
+                if (StringUtils.isBlank(snippetDTO.getId())) {
+                    snippetDTO.setId(UUID.randomUUID().toString());
+                }
 
-        return response;
+                // add the snippet
+                final Snippet snippet = snippetDAO.createSnippet(snippetDTO);
+                final SnippetDTO responseSnippetDTO = dtoFactory.createSnippetDto(snippet);
+                responseSnippetDTO.setContents(snippetUtils.populateFlowSnippet(snippet, false));
+                
+                return responseSnippetDTO;
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<PortDTO> createInputPort(Revision revision, String groupId, PortDTO inputPortDTO) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        // ensure id is set
-        if (StringUtils.isBlank(inputPortDTO.getId())) {
-            inputPortDTO.setId(UUID.randomUUID().toString());
-        }
-
-        final Port inputPort = inputPortDAO.createPort(groupId, inputPortDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<PortDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createPortDto(inputPort));
+    public ConfigurationSnapshot<PortDTO> createInputPort(final Revision revision, final String groupId, final PortDTO inputPortDTO) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<PortDTO>() {
+            @Override
+            public PortDTO execute() {
+                // ensure id is set
+                if (StringUtils.isBlank(inputPortDTO.getId())) {
+                    inputPortDTO.setId(UUID.randomUUID().toString());
+                }
 
-        // save the flow
-        controllerFacade.save();
+                final Port inputPort = inputPortDAO.createPort(groupId, inputPortDTO);
 
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return dtoFactory.createPortDto(inputPort);
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<PortDTO> createOutputPort(Revision revision, String groupId, PortDTO outputPortDTO) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        // ensure id is set
-        if (StringUtils.isBlank(outputPortDTO.getId())) {
-            outputPortDTO.setId(UUID.randomUUID().toString());
-        }
-
-        final Port outputPort = outputPortDAO.createPort(groupId, outputPortDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<PortDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createPortDto(outputPort));
+    public ConfigurationSnapshot<PortDTO> createOutputPort(final Revision revision, final String groupId, final PortDTO outputPortDTO) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<PortDTO>() {
+            @Override
+            public PortDTO execute() {
+                // ensure id is set
+                if (StringUtils.isBlank(outputPortDTO.getId())) {
+                    outputPortDTO.setId(UUID.randomUUID().toString());
+                }
 
-        // save the flow
-        controllerFacade.save();
+                final Port outputPort = outputPortDAO.createPort(groupId, outputPortDTO);
 
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return dtoFactory.createPortDto(outputPort);
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<ProcessGroupDTO> createProcessGroup(String parentGroupId, Revision revision, ProcessGroupDTO processGroupDTO) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        // ensure id is set
-        if (StringUtils.isBlank(processGroupDTO.getId())) {
-            processGroupDTO.setId(UUID.randomUUID().toString());
-        }
-
-        final ProcessGroup processGroup = processGroupDAO.createProcessGroup(parentGroupId, processGroupDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<ProcessGroupDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createProcessGroupDto(processGroup));
+    public ConfigurationSnapshot<ProcessGroupDTO> createProcessGroup(final String parentGroupId, final Revision revision, final ProcessGroupDTO processGroupDTO) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<ProcessGroupDTO>() {
+            @Override
+            public ProcessGroupDTO execute() {
+                // ensure id is set
+                if (StringUtils.isBlank(processGroupDTO.getId())) {
+                    processGroupDTO.setId(UUID.randomUUID().toString());
+                }
 
-        // save the flow
-        controllerFacade.save();
+                final ProcessGroup processGroup = processGroupDAO.createProcessGroup(parentGroupId, processGroupDTO);
 
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return dtoFactory.createProcessGroupDto(processGroup);
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<RemoteProcessGroupDTO> createRemoteProcessGroup(Revision revision, String groupId, RemoteProcessGroupDTO remoteProcessGroupDTO) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
-
-        // ensure id is set
-        if (StringUtils.isBlank(remoteProcessGroupDTO.getId())) {
-            remoteProcessGroupDTO.setId(UUID.randomUUID().toString());
-        }
-
-        final RemoteProcessGroup remoteProcessGroup = remoteProcessGroupDAO.createRemoteProcessGroup(groupId, remoteProcessGroupDTO);
-
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<RemoteProcessGroupDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createRemoteProcessGroupDto(remoteProcessGroup));
+    public ConfigurationSnapshot<RemoteProcessGroupDTO> createRemoteProcessGroup(final Revision revision, final String groupId, final RemoteProcessGroupDTO remoteProcessGroupDTO) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<RemoteProcessGroupDTO>() {
+            @Override
+            public RemoteProcessGroupDTO execute() {
+                // ensure id is set
+                if (StringUtils.isBlank(remoteProcessGroupDTO.getId())) {
+                    remoteProcessGroupDTO.setId(UUID.randomUUID().toString());
+                }
 
-        // save the flow
-        controllerFacade.save();
+                final RemoteProcessGroup remoteProcessGroup = remoteProcessGroupDAO.createRemoteProcessGroup(groupId, remoteProcessGroupDTO);
 
-        return response;
+                // save the flow
+                controllerFacade.save();
+                
+                return dtoFactory.createRemoteProcessGroupDto(remoteProcessGroup);
+            }
+        });
     }
 
     @Override
@@ -1186,74 +1123,217 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     }
 
     @Override
-    public ConfigurationSnapshot<FlowSnippetDTO> createTemplateInstance(Revision revision, String groupId, Double originX, Double originY, String templateId) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
+    public ConfigurationSnapshot<FlowSnippetDTO> createTemplateInstance(final Revision revision, final String groupId, final Double originX, final Double originY, final String templateId) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<FlowSnippetDTO>() {
+            @Override
+            public FlowSnippetDTO execute() {
+                // instantiate the template - there is no need to make another copy of the flow snippet since the actual template
+                // was copied and this dto is only used to instantiate it's components (which as already completed)
+                FlowSnippetDTO flowSnippet = templateDAO.instantiateTemplate(groupId, originX, originY, templateId);
 
-        // instantiate the template - there is no need to make another copy of the flow snippet since the actual template
-        // was copied and this dto is only used to instantiate it's components (which as already completed)
-        FlowSnippetDTO flowSnippet = templateDAO.instantiateTemplate(groupId, originX, originY, templateId);
+                // validate the new snippet
+                validateSnippetContents(flowSnippet, groupId);
 
-        // validate the new snippet
-        validateSnippetContents(flowSnippet, groupId);
+                // save the flow
+                controllerFacade.save();
+                
+                return flowSnippet;
+            }
+        });
+    }
 
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<FlowSnippetDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), flowSnippet);
+    @Override
+    public ConfigurationSnapshot<Void> createArchive(final Revision revision) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<Void>() {
+            @Override
+            public Void execute() {
+                // create the archive
+                controllerFacade.createArchive();
+                return null;
+            }
+        });
+    }
 
-        // save the flow
-        controllerFacade.save();
+    @Override
+    public ConfigurationSnapshot<ProcessorDTO> setProcessorAnnotationData(final Revision revision, final String processorId, final String annotationData) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<ProcessorDTO>() {
+            @Override
+            public ProcessorDTO execute() {
+                // create the processor config
+                final ProcessorConfigDTO config = new ProcessorConfigDTO();
+                config.setAnnotationData(annotationData);
 
-        return response;
+                // create the processor dto
+                final ProcessorDTO processorDTO = new ProcessorDTO();
+                processorDTO.setId(processorId);
+                processorDTO.setConfig(config);
+
+                // get the parent group id for the specified processor
+                String groupId = controllerFacade.findProcessGroupIdForProcessor(processorId);
+
+                // ensure the parent group id was found
+                if (groupId == null) {
+                    throw new ResourceNotFoundException(String.format("Unable to locate Processor with id '%s'.", processorId));
+                }
+
+                // update the processor configuration
+                ProcessorNode processor = processorDAO.updateProcessor(groupId, processorDTO);
+
+                // save the flow
+                controllerFacade.save();
+
+                return dtoFactory.createProcessorDto(processor);
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<Void> createArchive(Revision revision) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
+    public ConfigurationSnapshot<ControllerServiceDTO> createControllerService(final Revision revision, final ControllerServiceDTO controllerServiceDTO) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<ControllerServiceDTO>() {
+            @Override
+            public ControllerServiceDTO execute() {
+                // ensure id is set
+                if (StringUtils.isBlank(controllerServiceDTO.getId())) {
+                    controllerServiceDTO.setId(UUID.randomUUID().toString());
+                }
 
-        // create the archive
-        controllerFacade.createArchive();
+                // create the controller service
+                final ControllerServiceNode controllerService = controllerServiceDAO.createControllerService(controllerServiceDTO);
 
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<Void> response = new ConfigurationSnapshot<>(updatedRevision.getVersion());
-        return response;
+                // save the update
+                if (properties.isClusterManager()) {
+                    clusterManager.saveControllerServices();
+                } else {
+                    controllerFacade.save();
+                }
+                
+                return dtoFactory.createControllerServiceDto(controllerService);
+            }
+        });
     }
 
     @Override
-    public ConfigurationSnapshot<ProcessorDTO> setProcessorAnnotationData(Revision revision, String processorId, String annotationData) {
-        // ensure the proper revision before performing the update
-        checkRevision(revision);
+    public ConfigurationSnapshot<ControllerServiceDTO> updateControllerService(final Revision revision, final ControllerServiceDTO controllerServiceDTO) {
+        // if controller service does not exist, then create new controller service
+        if (controllerServiceDAO.hasControllerService(controllerServiceDTO.getId()) == false) {
+            return createControllerService(revision, controllerServiceDTO);
+        }
+        
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<ControllerServiceDTO>() {
+            @Override
+            public ControllerServiceDTO execute() {
+                final ControllerServiceNode controllerService = controllerServiceDAO.updateControllerService(controllerServiceDTO);
 
-        // create the processor config
-        final ProcessorConfigDTO config = new ProcessorConfigDTO();
-        config.setAnnotationData(annotationData);
+                // save the update
+                if (properties.isClusterManager()) {
+                    clusterManager.saveControllerServices();
+                } else {
+                    controllerFacade.save();
+                }
 
-        // create the processor dto
-        final ProcessorDTO processorDTO = new ProcessorDTO();
-        processorDTO.setId(processorId);
-        processorDTO.setConfig(config);
+                return dtoFactory.createControllerServiceDto(controllerService);
+            }
+        });
+    }
 
-        // get the parent group id for the specified processor
-        String groupId = controllerFacade.findProcessGroupIdForProcessor(processorId);
+    @Override
+    public ConfigurationSnapshot<Set<ControllerServiceReferencingComponentDTO>> updateControllerServiceReferencingComponents(final Revision revision, final String controllerServiceId, final org.apache.nifi.controller.ScheduledState scheduledState, final org.apache.nifi.controller.service.ControllerServiceState controllerServiceState) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<Set<ControllerServiceReferencingComponentDTO>>() {
+            @Override
+            public Set<ControllerServiceReferencingComponentDTO> execute() {
+                final ControllerServiceReference reference = controllerServiceDAO.updateControllerServiceReferencingComponents(controllerServiceId, scheduledState, controllerServiceState);
+                return dtoFactory.createControllerServiceReferencingComponentsDto(reference);
+            }
+        });
+    }
 
-        // ensure the parent group id was found
-        if (groupId == null) {
-            throw new ResourceNotFoundException(String.format("Unable to locate Processor with id '%s'.", processorId));
+    @Override
+    public ConfigurationSnapshot<Void> deleteControllerService(final Revision revision, final String controllerServiceId) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<Void>() {
+            @Override
+            public Void execute() {
+                // delete the label
+                controllerServiceDAO.deleteControllerService(controllerServiceId);
+
+                // save the update
+                if (properties.isClusterManager()) {
+                    clusterManager.saveControllerServices();
+                } else {
+                    controllerFacade.save();
+                }
+                
+                return null;
+            }
+        });
+    }
+
+    @Override
+    public ConfigurationSnapshot<ReportingTaskDTO> createReportingTask(final Revision revision, final ReportingTaskDTO reportingTaskDTO) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<ReportingTaskDTO>() {
+            @Override
+            public ReportingTaskDTO execute() {
+                // ensure id is set
+                if (StringUtils.isBlank(reportingTaskDTO.getId())) {
+                    reportingTaskDTO.setId(UUID.randomUUID().toString());
+                }
+
+                // create the reporting
+                final ReportingTaskNode reportingTask = reportingTaskDAO.createReportingTask(reportingTaskDTO);
+
+                // save the update
+                if (properties.isClusterManager()) {
+                    clusterManager.saveReportingTasks();
+                } else {
+                    controllerFacade.save();
+                }
+                
+                return dtoFactory.createReportingTaskDto(reportingTask);
+            }
+        });
+    }
+
+    @Override
+    public ConfigurationSnapshot<ReportingTaskDTO> updateReportingTask(final Revision revision, final ReportingTaskDTO reportingTaskDTO) {
+        // if reporting task does not exist, then create new reporting task
+        if (reportingTaskDAO.hasReportingTask(reportingTaskDTO.getId()) == false) {
+            return createReportingTask(revision, reportingTaskDTO);
         }
+        
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<ReportingTaskDTO>() {
+            @Override
+            public ReportingTaskDTO execute() {
+                final ReportingTaskNode reportingTask = reportingTaskDAO.updateReportingTask(reportingTaskDTO);
 
-        // update the processor configuration
-        ProcessorNode processor = processorDAO.updateProcessor(groupId, processorDTO);
+                // save the update
+                if (properties.isClusterManager()) {
+                    clusterManager.saveReportingTasks();
+                } else {
+                    controllerFacade.save();
+                }
 
-        // update the revision and generate a response
-        final Revision updatedRevision = updateRevision(revision);
-        final ConfigurationSnapshot<ProcessorDTO> response = new ConfigurationSnapshot<>(updatedRevision.getVersion(), dtoFactory.createProcessorDto(processor));
+                return dtoFactory.createReportingTaskDto(reportingTask);
+            }
+        });
+    }
 
-        // save the flow
-        controllerFacade.save();
+    @Override
+    public ConfigurationSnapshot<Void> deleteReportingTask(final Revision revision, final String reportingTaskId) {
+        return optimisticLockingManager.configureFlow(revision, new ConfigurationRequest<Void>() {
+            @Override
+            public Void execute() {
+                // delete the label
+                reportingTaskDAO.deleteReportingTask(reportingTaskId);
 
-        return response;
+                // save the update
+                if (properties.isClusterManager()) {
+                    clusterManager.saveReportingTasks();
+                } else {
+                    controllerFacade.save();
+                }
+                
+                return null;
+            }
+        });
     }
 
     @Override
@@ -1408,9 +1488,10 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     // -----------------------------------------
     // Read Operations
     // -----------------------------------------
+    
     @Override
     public RevisionDTO getRevision() {
-        return dtoFactory.createRevisionDTO(optimisticLockingManager.getRevision());
+        return dtoFactory.createRevisionDTO(optimisticLockingManager.getLastModification());
     }
 
     @Override
@@ -1637,6 +1718,16 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     }
 
     @Override
+    public Set<DocumentedTypeDTO> getControllerServiceTypes() {
+        return controllerFacade.getControllerServiceTypes();
+    }
+
+    @Override
+    public Set<DocumentedTypeDTO> getReportingTaskTypes() {
+        return controllerFacade.getReportingTaskTypes();
+    }
+
+    @Override
     public ProcessorDTO getProcessor(String groupId, String id) {
         final ProcessorNode processor = processorDAO.getProcessor(groupId, id);
         final ProcessorDTO processorDto = dtoFactory.createProcessorDto(processor);
@@ -1644,6 +1735,19 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     }
 
     @Override
+    public PropertyDescriptorDTO getProcessorPropertyDescriptor(String groupId, String id, String property) {
+        final ProcessorNode processor = processorDAO.getProcessor(groupId, id);
+        PropertyDescriptor descriptor = processor.getPropertyDescriptor(property);
+        
+        // return an invalid descriptor if the processor doesn't suppor this property
+        if (descriptor == null) {
+            descriptor = new PropertyDescriptor.Builder().name(property).addValidator(Validator.INVALID).dynamic(true).build();
+        }
+        
+        return dtoFactory.createPropertyDescriptorDto(descriptor);
+    }
+
+    @Override
     public StatusHistoryDTO getProcessorStatusHistory(String groupId, String id) {
         return controllerFacade.getProcessorStatusHistory(groupId, id);
     }
@@ -1823,6 +1927,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
 
         final Date now = new Date();
         controllerConfig.setTimeOffset(TimeZone.getDefault().getOffset(now.getTime()));
+        controllerConfig.setCurrentTime(now);
 
         // determine the site to site configuration
         if (isClustered()) {
@@ -1929,12 +2034,72 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     @Override
     public ConfigurationSnapshot<ProcessGroupDTO> getProcessGroup(String groupId, final boolean recurse) {
         ProcessGroup processGroup = processGroupDAO.getProcessGroup(groupId);
-        Long version = optimisticLockingManager.getRevision().getVersion();
-        ConfigurationSnapshot<ProcessGroupDTO> response = new ConfigurationSnapshot<>(version, dtoFactory.createProcessGroupDto(processGroup, recurse));
+        Revision revision = optimisticLockingManager.getLastModification().getRevision();
+        ConfigurationSnapshot<ProcessGroupDTO> response = new ConfigurationSnapshot<>(revision.getVersion(), dtoFactory.createProcessGroupDto(processGroup, recurse));
         return response;
     }
 
     @Override
+    public Set<ControllerServiceDTO> getControllerServices() {
+        final Set<ControllerServiceDTO> controllerServiceDtos = new LinkedHashSet<>();
+        for (ControllerServiceNode controllerService : controllerServiceDAO.getControllerServices()) {
+            controllerServiceDtos.add(dtoFactory.createControllerServiceDto(controllerService));
+        }
+        return controllerServiceDtos;
+    }
+
+    @Override
+    public ControllerServiceDTO getControllerService(String controllerServiceId) {
+        return dtoFactory.createControllerServiceDto(controllerServiceDAO.getControllerService(controllerServiceId));
+    }
+
+    @Override
+    public PropertyDescriptorDTO getControllerServicePropertyDescriptor(String id, String property) {
+        final ControllerServiceNode controllerService = controllerServiceDAO.getControllerService(id);
+        PropertyDescriptor descriptor = controllerService.getControllerServiceImplementation().getPropertyDescriptor(property);
+        
+        // return an invalid descriptor if the controller service doesn't support this property
+        if (descriptor == null) {
+            descriptor = new PropertyDescriptor.Builder().name(property).addValidator(Validator.INVALID).dynamic(true).build();
+        }
+        
+        return dtoFactory.createPropertyDescriptorDto(descriptor);
+    }
+    
+    @Override
+    public Set<ControllerServiceReferencingComponentDTO> getControllerServiceReferencingComponents(String controllerServiceId) {
+        final ControllerServiceNode service = controllerServiceDAO.getControllerService(controllerServiceId);
+        return dtoFactory.createControllerServiceReferencingComponentsDto(service.getReferences());
+    }
+
+    @Override
+    public Set<ReportingTaskDTO> getReportingTasks() {
+        final Set<ReportingTaskDTO> reportingTaskDtos = new LinkedHashSet<>();
+        for (ReportingTaskNode reportingTask : reportingTaskDAO.getReportingTasks()) {
+            reportingTaskDtos.add(dtoFactory.createReportingTaskDto(reportingTask));
+        }
+        return reportingTaskDtos;
+    }
+
+    @Override
+    public ReportingTaskDTO getReportingTask(String reportingTaskId) {
+        return dtoFactory.createReportingTaskDto(reportingTaskDAO.getReportingTask(reportingTaskId));
+    }
+
+    @Override
+    public PropertyDescriptorDTO getReportingTaskPropertyDescriptor(String id, String property) {
+        final ReportingTaskNode reportingTask = reportingTaskDAO.getReportingTask(id);
+        PropertyDescriptor descriptor = reportingTask.getReportingTask().getPropertyDescriptor(property);
+        
+        // return an invalid descriptor if the reporting task doesn't support this property
+        if (descriptor == null) {
+            descriptor = new PropertyDescriptor.Builder().name(property).addValidator(Validator.INVALID).dynamic(true).build();
+        }
+        
+        return dtoFactory.createPropertyDescriptorDto(descriptor);
+    }
+    
+    @Override
     public StatusHistoryDTO getProcessGroupStatusHistory(String groupId) {
         return controllerFacade.getProcessGroupStatusHistory(groupId);
     }
@@ -1974,9 +2139,9 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     }
 
     @Override
-    public ProcessorHistoryDTO getProcessorHistory(String processorId) {
+    public ComponentHistoryDTO getComponentHistory(String componentId) {
         final Map<String, PropertyHistoryDTO> propertyHistoryDtos = new LinkedHashMap<>();
-        final Map<String, List<PreviousValue>> propertyHistory = auditService.getPreviousValues(processorId);
+        final Map<String, List<PreviousValue>> propertyHistory = auditService.getPreviousValues(componentId);
 
         for (final Map.Entry<String, List<PreviousValue>> entry : propertyHistory.entrySet()) {
             final List<PreviousValueDTO> previousValueDtos = new ArrayList<>();
@@ -1996,8 +2161,8 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
             }
         }
 
-        final ProcessorHistoryDTO history = new ProcessorHistoryDTO();
-        history.setProcessorId(processorId);
+        final ComponentHistoryDTO history = new ComponentHistoryDTO();
+        history.setComponentId(componentId);
         history.setPropertyHistory(propertyHistoryDtos);
 
         return history;
@@ -2718,6 +2883,14 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
         this.processGroupDAO = processGroupDAO;
     }
 
+    public void setControllerServiceDAO(ControllerServiceDAO controllerServiceDAO) {
+        this.controllerServiceDAO = controllerServiceDAO;
+    }
+
+    public void setReportingTaskDAO(ReportingTaskDAO reportingTaskDAO) {
+        this.reportingTaskDAO = reportingTaskDAO;
+    }
+
     public void setTemplateDAO(TemplateDAO templateDAO) {
         this.templateDAO = templateDAO;
     }


[31/62] [abbrv] incubator-nifi git commit: org.apache.nifi.controller.exception.ProcessorLifeCycleException should be renamed to ComponentLifeCycleException

Posted by ma...@apache.org.
org.apache.nifi.controller.exception.ProcessorLifeCycleException should be renamed to ComponentLifeCycleException

Signed-off-by: Mark Payne <ma...@hotmail.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/2b21b465
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/2b21b465
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/2b21b465

Branch: refs/heads/NIFI-25
Commit: 2b21b46501f03b7560e09c723117abffb0065da4
Parents: 2c35226
Author: Jon Anderson <an...@gmail.com>
Authored: Sat Apr 4 15:02:12 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Sun Apr 5 09:43:10 2015 -0400

----------------------------------------------------------------------
 .../cluster/manager/impl/WebClusterManager.java |  4 +--
 .../exception/ComponentLifeCycleException.java  | 30 ++++++++++++++++++++
 .../exception/ProcessorLifeCycleException.java  | 30 --------------------
 .../apache/nifi/controller/FlowController.java  |  6 ++--
 .../reporting/AbstractReportingTaskNode.java    |  4 +--
 .../service/StandardControllerServiceNode.java  |  4 +--
 .../StandardControllerServiceProvider.java      |  4 +--
 .../nifi/groups/StandardProcessGroup.java       |  4 +--
 .../nifi/web/dao/impl/StandardProcessorDAO.java |  8 +++---
 .../web/dao/impl/StandardReportingTaskDAO.java  |  4 +--
 10 files changed, 49 insertions(+), 49 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2b21b465/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/WebClusterManager.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/WebClusterManager.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/WebClusterManager.java
index eff523a..db6421e 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/WebClusterManager.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/WebClusterManager.java
@@ -130,7 +130,7 @@ import org.apache.nifi.controller.ReportingTaskNode;
 import org.apache.nifi.controller.ScheduledState;
 import org.apache.nifi.controller.StandardFlowSerializer;
 import org.apache.nifi.controller.ValidationContextFactory;
-import org.apache.nifi.controller.exception.ProcessorLifeCycleException;
+import org.apache.nifi.controller.exception.ComponentLifeCycleException;
 import org.apache.nifi.controller.reporting.ClusteredReportingTaskNode;
 import org.apache.nifi.controller.reporting.ReportingTaskInstantiationException;
 import org.apache.nifi.controller.reporting.ReportingTaskProvider;
@@ -1070,7 +1070,7 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
             try (final NarCloseable x = NarCloseable.withNarLoader()) {
                 ReflectionUtils.invokeMethodsWithAnnotation(OnAdded.class, task);
             } catch (final Exception e) {
-                throw new ProcessorLifeCycleException("Failed to invoke On-Added Lifecycle methods of " + task, e);
+                throw new ComponentLifeCycleException("Failed to invoke On-Added Lifecycle methods of " + task, e);
             }
         }
         

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2b21b465/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/exception/ComponentLifeCycleException.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/exception/ComponentLifeCycleException.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/exception/ComponentLifeCycleException.java
new file mode 100644
index 0000000..9e82b97
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/exception/ComponentLifeCycleException.java
@@ -0,0 +1,30 @@
+/*
+ * 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.controller.exception;
+
+public class ComponentLifeCycleException extends RuntimeException {
+
+    private static final long serialVersionUID = 8392341500511490941L;
+
+    public ComponentLifeCycleException(final String message, final Throwable t) {
+        super(message, t);
+    }
+
+    public ComponentLifeCycleException(final Throwable t) {
+        super(t);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2b21b465/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/exception/ProcessorLifeCycleException.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/exception/ProcessorLifeCycleException.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/exception/ProcessorLifeCycleException.java
deleted file mode 100644
index 5acca16..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/exception/ProcessorLifeCycleException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.controller.exception;
-
-public class ProcessorLifeCycleException extends RuntimeException {
-
-    private static final long serialVersionUID = 8392341500511490941L;
-
-    public ProcessorLifeCycleException(final String message, final Throwable t) {
-        super(message, t);
-    }
-
-    public ProcessorLifeCycleException(final Throwable t) {
-        super(t);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2b21b465/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
index f3fb67c..ec25ab1 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
@@ -75,7 +75,7 @@ import org.apache.nifi.connectable.Size;
 import org.apache.nifi.connectable.StandardConnection;
 import org.apache.nifi.controller.exception.CommunicationsException;
 import org.apache.nifi.controller.exception.ProcessorInstantiationException;
-import org.apache.nifi.controller.exception.ProcessorLifeCycleException;
+import org.apache.nifi.controller.exception.ComponentLifeCycleException;
 import org.apache.nifi.controller.label.Label;
 import org.apache.nifi.controller.label.StandardLabel;
 import org.apache.nifi.controller.reporting.ReportingTaskInstantiationException;
@@ -830,7 +830,7 @@ public class FlowController implements EventAccess, ControllerServiceProvider, R
                 ReflectionUtils.invokeMethodsWithAnnotation(OnAdded.class, org.apache.nifi.processor.annotation.OnAdded.class, processor);
             } catch (final Exception e) {
                 logRepository.removeObserver(StandardProcessorNode.BULLETIN_OBSERVER_ID);
-                throw new ProcessorLifeCycleException("Failed to invoke @OnAdded methods of " + procNode.getProcessor(), e);
+                throw new ComponentLifeCycleException("Failed to invoke @OnAdded methods of " + procNode.getProcessor(), e);
             }
         }
 
@@ -2600,7 +2600,7 @@ public class FlowController implements EventAccess, ControllerServiceProvider, R
             try (final NarCloseable x = NarCloseable.withNarLoader()) {
                 ReflectionUtils.invokeMethodsWithAnnotation(OnAdded.class, task);
             } catch (final Exception e) {
-                throw new ProcessorLifeCycleException("Failed to invoke On-Added Lifecycle methods of " + task, e);
+                throw new ComponentLifeCycleException("Failed to invoke On-Added Lifecycle methods of " + task, e);
             }
         }
         

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2b21b465/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/AbstractReportingTaskNode.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/AbstractReportingTaskNode.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/AbstractReportingTaskNode.java
index 05b3a06..18d2c5f 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/AbstractReportingTaskNode.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/reporting/AbstractReportingTaskNode.java
@@ -31,7 +31,7 @@ import org.apache.nifi.controller.ReportingTaskNode;
 import org.apache.nifi.controller.ScheduledState;
 import org.apache.nifi.controller.ValidationContextFactory;
 import org.apache.nifi.controller.annotation.OnConfigured;
-import org.apache.nifi.controller.exception.ProcessorLifeCycleException;
+import org.apache.nifi.controller.exception.ComponentLifeCycleException;
 import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.controller.service.ControllerServiceProvider;
 import org.apache.nifi.controller.service.StandardConfigurationContext;
@@ -149,7 +149,7 @@ public abstract class AbstractReportingTaskNode extends AbstractConfiguredCompon
             final ConfigurationContext configContext = new StandardConfigurationContext(this, serviceLookup);
             ReflectionUtils.invokeMethodsWithAnnotation(OnConfigured.class, reportingTask, configContext);
         } catch (final Exception e) {
-            throw new ProcessorLifeCycleException("Failed to invoke On-Configured Lifecycle methods of " + reportingTask, e);
+            throw new ComponentLifeCycleException("Failed to invoke On-Configured Lifecycle methods of " + reportingTask, e);
         }
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2b21b465/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java
index c8c7ec9..8ad0bf5 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java
@@ -32,7 +32,7 @@ import org.apache.nifi.controller.ConfiguredComponent;
 import org.apache.nifi.controller.ControllerService;
 import org.apache.nifi.controller.ValidationContextFactory;
 import org.apache.nifi.controller.annotation.OnConfigured;
-import org.apache.nifi.controller.exception.ProcessorLifeCycleException;
+import org.apache.nifi.controller.exception.ComponentLifeCycleException;
 import org.apache.nifi.nar.NarCloseable;
 import org.apache.nifi.util.ReflectionUtils;
 
@@ -129,7 +129,7 @@ public class StandardControllerServiceNode extends AbstractConfiguredComponent i
             final ConfigurationContext configContext = new StandardConfigurationContext(this, serviceProvider);
             ReflectionUtils.invokeMethodsWithAnnotation(OnConfigured.class, implementation, configContext);
         } catch (final Exception e) {
-            throw new ProcessorLifeCycleException("Failed to invoke On-Configured Lifecycle methods of " + implementation, e);
+            throw new ComponentLifeCycleException("Failed to invoke On-Configured Lifecycle methods of " + implementation, e);
         }
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2b21b465/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceProvider.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceProvider.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceProvider.java
index dfbfca5..d8f1338 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceProvider.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceProvider.java
@@ -47,7 +47,7 @@ import org.apache.nifi.controller.ReportingTaskNode;
 import org.apache.nifi.controller.ScheduledState;
 import org.apache.nifi.controller.ValidationContextFactory;
 import org.apache.nifi.controller.exception.ControllerServiceInstantiationException;
-import org.apache.nifi.controller.exception.ProcessorLifeCycleException;
+import org.apache.nifi.controller.exception.ComponentLifeCycleException;
 import org.apache.nifi.events.BulletinFactory;
 import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.nar.ExtensionManager;
@@ -186,7 +186,7 @@ public class StandardControllerServiceProvider implements ControllerServiceProvi
                 try (final NarCloseable x = NarCloseable.withNarLoader()) {
                     ReflectionUtils.invokeMethodsWithAnnotation(OnAdded.class, originalService);
                 } catch (final Exception e) {
-                    throw new ProcessorLifeCycleException("Failed to invoke On-Added Lifecycle methods of " + originalService, e);
+                    throw new ComponentLifeCycleException("Failed to invoke On-Added Lifecycle methods of " + originalService, e);
                 }
             }
 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2b21b465/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
index 216d015..6a26d09 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
@@ -48,7 +48,7 @@ import org.apache.nifi.controller.ProcessScheduler;
 import org.apache.nifi.controller.ProcessorNode;
 import org.apache.nifi.controller.ScheduledState;
 import org.apache.nifi.controller.Snippet;
-import org.apache.nifi.controller.exception.ProcessorLifeCycleException;
+import org.apache.nifi.controller.exception.ComponentLifeCycleException;
 import org.apache.nifi.controller.label.Label;
 import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.controller.service.ControllerServiceProvider;
@@ -674,7 +674,7 @@ public final class StandardProcessGroup implements ProcessGroup {
                 final StandardProcessContext processContext = new StandardProcessContext(processor, controllerServiceProvider, encryptor);
                 ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnRemoved.class, org.apache.nifi.processor.annotation.OnRemoved.class, processor.getProcessor(), processContext);
             } catch (final Exception e) {
-                throw new ProcessorLifeCycleException("Failed to invoke 'OnRemoved' methods of " + processor, e);
+                throw new ComponentLifeCycleException("Failed to invoke 'OnRemoved' methods of " + processor, e);
             }
 
             for ( final Map.Entry<PropertyDescriptor, String> entry : processor.getProperties().entrySet() ) {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2b21b465/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessorDAO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessorDAO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessorDAO.java
index 0c587fe..b291b4f 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessorDAO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessorDAO.java
@@ -32,7 +32,7 @@ import org.apache.nifi.controller.FlowController;
 import org.apache.nifi.controller.ProcessorNode;
 import org.apache.nifi.controller.ScheduledState;
 import org.apache.nifi.controller.exception.ProcessorInstantiationException;
-import org.apache.nifi.controller.exception.ProcessorLifeCycleException;
+import org.apache.nifi.controller.exception.ComponentLifeCycleException;
 import org.apache.nifi.controller.exception.ValidationException;
 import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.logging.LogLevel;
@@ -124,7 +124,7 @@ public class StandardProcessorDAO extends ComponentDAO implements ProcessorDAO {
             return processor;
         } catch (ProcessorInstantiationException pse) {
             throw new NiFiCoreException(String.format("Unable to create processor of type %s due to: %s", processorDTO.getType(), pse.getMessage()), pse);
-        } catch (IllegalStateException | ProcessorLifeCycleException ise) {
+        } catch (IllegalStateException | ComponentLifeCycleException ise) {
             throw new NiFiCoreException(ise.getMessage(), ise);
         }
     }
@@ -460,7 +460,7 @@ public class StandardProcessorDAO extends ComponentDAO implements ProcessorDAO {
                             parentGroup.disableProcessor(processor);
                             break;
                     }
-                } catch (IllegalStateException | ProcessorLifeCycleException ise) {
+                } catch (IllegalStateException | ComponentLifeCycleException ise) {
                     throw new NiFiCoreException(ise.getMessage(), ise);
                 } catch (RejectedExecutionException ree) {
                     throw new NiFiCoreException("Unable to schedule all tasks for the specified processor.", ree);
@@ -496,7 +496,7 @@ public class StandardProcessorDAO extends ComponentDAO implements ProcessorDAO {
         try {
             // attempt remove the processor
             group.removeProcessor(processor);
-        } catch (ProcessorLifeCycleException plce) {
+        } catch (ComponentLifeCycleException plce) {
             throw new NiFiCoreException(plce.getMessage(), plce);
         }
     }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2b21b465/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardReportingTaskDAO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardReportingTaskDAO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardReportingTaskDAO.java
index d9fd74c..46b7070 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardReportingTaskDAO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardReportingTaskDAO.java
@@ -26,7 +26,7 @@ import java.util.regex.Matcher;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.controller.ReportingTaskNode;
 import org.apache.nifi.controller.ScheduledState;
-import org.apache.nifi.controller.exception.ProcessorLifeCycleException;
+import org.apache.nifi.controller.exception.ComponentLifeCycleException;
 
 import org.apache.nifi.controller.exception.ValidationException;
 import org.apache.nifi.controller.reporting.ReportingTaskInstantiationException;
@@ -166,7 +166,7 @@ public class StandardReportingTaskDAO extends ComponentDAO implements ReportingT
                             reportingTaskProvider.disableReportingTask(reportingTask);
                             break;
                     }
-                } catch (IllegalStateException | ProcessorLifeCycleException ise) {
+                } catch (IllegalStateException | ComponentLifeCycleException ise) {
                     throw new NiFiCoreException(ise.getMessage(), ise);
                 } catch (RejectedExecutionException ree) {
                     throw new NiFiCoreException("Unable to schedule all tasks for the specified reporting task.", ree);


[42/62] [abbrv] incubator-nifi git commit: NIFI-493: - Fixing the number of documented components. - Fixing unordered list creation.

Posted by ma...@apache.org.
NIFI-493:
- Fixing the number of documented components.
- Fixing unordered list creation.

Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/d769b50e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/d769b50e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/d769b50e

Branch: refs/heads/NIFI-25
Commit: d769b50e39a7802dad55544f56759b976f162f70
Parents: 7369730
Author: Matt Gilman <ma...@gmail.com>
Authored: Wed Apr 8 08:39:05 2015 -0400
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Apr 8 08:39:05 2015 -0400

----------------------------------------------------------------------
 .../nifi/web/docs/DocumentationController.java    |  5 ++++-
 .../src/main/webapp/WEB-INF/jsp/documentation.jsp | 18 +++++++++---------
 2 files changed, 13 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d769b50e/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/java/org/apache/nifi/web/docs/DocumentationController.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/java/org/apache/nifi/web/docs/DocumentationController.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/java/org/apache/nifi/web/docs/DocumentationController.java
index a5428cd..7d8ec6c 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/java/org/apache/nifi/web/docs/DocumentationController.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/java/org/apache/nifi/web/docs/DocumentationController.java
@@ -39,6 +39,9 @@ import org.apache.commons.lang3.StringUtils;
 @WebServlet(name = "DocumenationController", urlPatterns = {"/*"})
 public class DocumentationController extends HttpServlet {
 
+    private static final int GENERAL_LINK_COUNT = 4;
+    private static final int DEVELOPER_LINK_COUNT = 2;
+    
     // context for accessing the extension mapping
     private ServletContext servletContext;
 
@@ -82,7 +85,7 @@ public class DocumentationController extends HttpServlet {
         request.setAttribute("processors", processors);
         request.setAttribute("controllerServices", controllerServices);
         request.setAttribute("reportingTasks", reportingTasks);
-        request.setAttribute("totalComponents", processors.size() + controllerServices.size() + reportingTasks.size());
+        request.setAttribute("totalComponents", GENERAL_LINK_COUNT + processors.size() + controllerServices.size() + reportingTasks.size() + DEVELOPER_LINK_COUNT);
 
         // forward appropriately
         request.getRequestDispatcher("/WEB-INF/jsp/documentation.jsp").forward(request, response);

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d769b50e/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/WEB-INF/jsp/documentation.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/WEB-INF/jsp/documentation.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/WEB-INF/jsp/documentation.jsp
index d2397de..aea08d0 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/WEB-INF/jsp/documentation.jsp
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/WEB-INF/jsp/documentation.jsp
@@ -55,11 +55,11 @@
                 <div id="processor-links" class="component-links">
                     <c:choose>
                         <c:when test="${not empty processors}">
+                            <ul>
                             <c:forEach var="entry" items="${processors}">
-                                <ul>
-                                    <li class="component-item"><a class="component-link" href="components/${entry.value}/index.html" target="component-usage">${entry.key}</a></li>
-                                </ul>
+                                <li class="component-item"><a class="component-link" href="components/${entry.value}/index.html" target="component-usage">${entry.key}</a></li>
                             </c:forEach>
+                            </ul>
                             <span class="no-matching no-components hidden">No matching processors</span>
                         </c:when>
                         <c:otherwise>
@@ -73,11 +73,11 @@
                 <div id="controller-service-links" class="component-links">
                     <c:choose>
                         <c:when test="${not empty controllerServices}">
+                            <ul>
                             <c:forEach var="entry" items="${controllerServices}">
-                                <ul>
-                                    <li class="component-item"><a class="component-link" href="components/${entry.value}/index.html" target="component-usage">${entry.key}</a></li>
-                                </ul>
+                                <li class="component-item"><a class="component-link" href="components/${entry.value}/index.html" target="component-usage">${entry.key}</a></li>
                             </c:forEach>
+                            </ul>
                             <span class="no-matching no-components hidden">No matching controller services</span>
                         </c:when>
                         <c:otherwise>
@@ -91,11 +91,11 @@
                 <div id="reporting-task-links" class="component-links">
                     <c:choose>
                         <c:when test="${not empty reportingTasks}">
+                            <ul>
                             <c:forEach var="entry" items="${reportingTasks}">
-                                <ul>
-                                    <li class="component-item"><a class="component-link" href="components/${entry.value}/index.html" target="component-usage">${entry.key}</a></li>
-                                </ul>
+                                <li class="component-item"><a class="component-link" href="components/${entry.value}/index.html" target="component-usage">${entry.key}</a></li>
                             </c:forEach>
+                            </ul>
                             <span class="no-matching no-components hidden">No matching reporting tasks</span>
                         </c:when>
                         <c:otherwise>


[03/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
index 3d573b3..0e60b2c 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
@@ -14,33 +14,45 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, Slick */
+
 nf.Settings = (function () {
 
     var config = {
+        node: 'node',
+        ncm: 'ncm',
+        filterText: 'Filter',
+        styles: {
+            filterList: 'filter-list'
+        },
         urls: {
             controllerConfig: '../nifi-api/controller/config',
-            controllerArchive: '../nifi-api/controller/archive'
+            controllerArchive: '../nifi-api/controller/archive',
+            controllerServiceTypes: '../nifi-api/controller/controller-service-types',
+            controllerServices: '../nifi-api/controller/controller-services',
+            reportingTaskTypes: '../nifi-api/controller/reporting-task-types',
+            reportingTasks: '../nifi-api/controller/reporting-tasks'
         }
     };
 
-    /**
-     * Marshals the details to include in the configuration request.
-     */
-    var marshalConfiguration = function () {
-        // create the configuration
-        var configuration = {};
-        configuration['name'] = $('#data-flow-title-field').val();
-        configuration['comments'] = $('#data-flow-comments-field').val();
-        configuration['maxTimerDrivenThreadCount'] = $('#maximum-timer-driven-thread-count-field').val();
-        configuration['maxEventDrivenThreadCount'] = $('#maximum-event-driven-thread-count-field').val();
-        return configuration;
+    var gridOptions = {
+        forceFitColumns: true,
+        enableTextSelectionOnCells: true,
+        enableCellNavigation: true,
+        enableColumnReorder: false,
+        autoEdit: false,
+        multiSelect: false
     };
 
-    return {
-        /**
-         * Initializes the status page.
-         */
-        init: function () {
+    /**
+     * Initializes the general tab.
+     */
+    var initGeneral = function () {
+        // update the visibility of the controls
+        if (nf.Common.isDFM()) {
+            $('#general-settings div.editable').show();
+            $('#general-settings div.read-only').hide();
 
             // register the click listener for the archive link
             $('#archive-flow-link').click(function () {
@@ -63,13 +75,7 @@ nf.Settings = (function () {
                         dialogContent: 'A new flow archive was successfully created.',
                         overlayBackground: false
                     });
-                }).fail(function (xhr, status, error) {
-                    // close the details panel
-                    $('#settings-cancel').click();
-
-                    // handle the error
-                    nf.Common.handleAjaxError(xhr, status, error);
-                });
+                }).fail(nf.Common.handleAjaxError);
             });
 
             // register the click listener for the save button
@@ -98,27 +104,1629 @@ nf.Settings = (function () {
                     $('#data-flow-title-container').children('span.link:first-child').text(response.config.name);
 
                     // close the settings dialog
+                    nf.Dialog.showOkDialog({
+                        dialogContent: 'Settings successfully applied.',
+                        overlayBackground: false
+                    });
+                }).fail(nf.Common.handleAjaxError);
+            });
+        } else {
+            $('#general-settings div.editable').hide();
+            $('#general-settings div.read-only').show();
+        }
+    };
+
+    /**
+     * Marshals the details to include in the configuration request.
+     */
+    var marshalConfiguration = function () {
+        // create the configuration
+        var configuration = {};
+        configuration['name'] = $('#data-flow-title-field').val();
+        configuration['comments'] = $('#data-flow-comments-field').val();
+        configuration['maxTimerDrivenThreadCount'] = $('#maximum-timer-driven-thread-count-field').val();
+        configuration['maxEventDrivenThreadCount'] = $('#maximum-event-driven-thread-count-field').val();
+        return configuration;
+    };
+
+    /**
+     * Get the text out of the filter field. If the filter field doesn't
+     * have any text it will contain the text 'filter list' so this method
+     * accounts for that.
+     */
+    var getControllerServiceTypeFilterText = function () {
+        var filterText = '';
+        var filterField = $('#controller-service-type-filter');
+        if (!filterField.hasClass(config.styles.filterList)) {
+            filterText = filterField.val();
+        }
+        return filterText;
+    };
+
+    /**
+     * Filters the processor type table.
+     */
+    var applyControllerServiceTypeFilter = function () {
+        // get the dataview
+        var controllerServiceTypesGrid = $('#controller-service-types-table').data('gridInstance');
+
+        // ensure the grid has been initialized
+        if (nf.Common.isDefinedAndNotNull(controllerServiceTypesGrid)) {
+            var controllerServiceTypesData = controllerServiceTypesGrid.getData();
+
+            // update the search criteria
+            controllerServiceTypesData.setFilterArgs({
+                searchString: getControllerServiceTypeFilterText(),
+                property: $('#controller-service-type-filter-options').combo('getSelectedOption').value
+            });
+
+            // need to invalidate the entire table since parent elements may need to be 
+            // rerendered due to changes in their children
+            controllerServiceTypesData.refresh();
+            controllerServiceTypesGrid.invalidate();
+        }
+    };
+
+    /**
+     * Determines if all of the ancestors of the specified item are expanded.
+     * 
+     * @param {type} item
+     * @returns {Boolean}
+     */
+    var areAncestorsExpanded = function (item) {
+        var documentedType = item;
+        while (documentedType.parent !== null) {
+            if (documentedType.parent.collapsed === true) {
+                return false;
+            }
+            documentedType = documentedType.parent;
+        }
+
+        return true;
+    };
+
+    /**
+     * Determines if the specified item is an ancestor.
+     * 
+     * @param {type} item
+     * @returns {Boolean}
+     */
+    var isAncestor = function (item) {
+        return item.children.length > 0;
+    };
+
+    /**
+     * Hides the selected controller service.
+     */
+    var clearSelectedControllerService = function () {
+        if (nf.Canvas.isClustered()) {
+            $('#controller-service-availability-combo').combo('setSelectedOption', {
+                value: config.node
+            });
+        }
+        
+        $('#controller-service-type-description').text('');
+        $('#controller-service-type-name').text('');
+        $('#selected-controller-service-name').text('');
+        $('#selected-controller-service-type').text('');
+        $('#controller-service-description-container').hide();
+    };
+
+    /**
+     * Clears the selected controller service type.
+     */
+    var clearControllerServiceSelection = function () {
+        // clear the selected row
+        clearSelectedControllerService();
+
+        // clear the active cell the it can be reselected when its included
+        var controllerServiceTypesGrid = $('#controller-service-types-table').data('gridInstance');
+        controllerServiceTypesGrid.resetActiveCell();
+    };
+
+    /**
+     * Performs the filtering.
+     * 
+     * @param {object} item     The item subject to filtering
+     * @param {object} args     Filter arguments
+     * @returns {Boolean}       Whether or not to include the item
+     */
+    var filterControllerServiceTypes = function (item, args) {
+        if (!areAncestorsExpanded(item)) {
+            // if this item is currently selected and its parent is not collapsed
+            if ($('#selected-controller-service-type').text() === item['type']) {
+                clearControllerServiceSelection();
+            }
+
+            // update visibility flag
+            item.visible = false;
+
+            return false;
+        }
+
+        // don't allow ancestors to be filtered out (unless any of their ancestors
+        // are collapsed)
+        if (isAncestor(item)) {
+            // update visibility flag
+            item.visible = false;
+
+            return true;
+        }
+
+        // determine if the item matches the filter
+        var matchesFilter = matchesRegex(item, args);
+
+        // determine if the row matches the selected tags
+        var matchesTags = true;
+        if (matchesFilter) {
+            var tagFilters = $('#controller-service-tag-cloud').tagcloud('getSelectedTags');
+            var hasSelectedTags = tagFilters.length > 0;
+            if (hasSelectedTags) {
+                matchesTags = matchesSelectedTags(tagFilters, item['tags']);
+            }
+        }
+
+        // determine if this row should be visible
+        var matches = matchesFilter && matchesTags;
+
+        // if this row is currently selected and its being filtered
+        if (matches === false && $('#selected-controller-service-type').text() === item['type']) {
+            clearControllerServiceSelection();
+        }
+
+        // update visibility flag
+        item.visible = matches;
+
+        return matches;
+    };
+
+    /**
+     * Determines if the item matches the filter.
+     * 
+     * @param {object} item     The item to filter
+     * @param {object} args     The filter criteria
+     * @returns {boolean}       Whether the item matches the filter
+     */
+    var matchesRegex = function (item, args) {
+        if (args.searchString === '') {
+            return true;
+        }
+
+        try {
+            // perform the row filtering
+            var filterExp = new RegExp(args.searchString, 'i');
+        } catch (e) {
+            // invalid regex
+            return false;
+        }
+
+        // determine if the item matches the filter
+        return item[args.property].search(filterExp) >= 0;
+    };
+
+    /**
+     * Determines if the specified tags match all the tags selected by the user.
+     * 
+     * @argument {string[]} tagFilters      The tag filters
+     * @argument {string} tags              The tags to test
+     */
+    var matchesSelectedTags = function (tagFilters, tags) {
+        var selectedTags = [];
+        $.each(tagFilters, function (_, filter) {
+            selectedTags.push(filter);
+        });
+
+        // normalize the tags
+        var normalizedTags = tags.toLowerCase();
+
+        var matches = true;
+        $.each(selectedTags, function (i, selectedTag) {
+            if (normalizedTags.indexOf(selectedTag) === -1) {
+                matches = false;
+                return false;
+            }
+        });
+
+        return matches;
+    };
+
+    /**
+     * Formats the type by introducing expand/collapse where appropriate.
+     * 
+     * @param {type} row
+     * @param {type} cell
+     * @param {type} value
+     * @param {type} columnDef
+     * @param {type} dataContext
+     */
+    var expandableTypeFormatter = function (row, cell, value, columnDef, dataContext) {
+        var markup = '';
+
+        var indent = 0;
+        var documentedType = dataContext;
+        while (documentedType.parent !== null) {
+            indent += 20;
+            documentedType = documentedType.parent;
+        }
+
+        var padding = 3;
+
+        // create the markup for the row
+        if (dataContext.children.length > 0) {
+            // determine how to render the expansion button
+            var expansionStyle = 'expanded';
+            if (dataContext.collapsed === true) {
+                expansionStyle = 'collapsed';
+            }
+
+            // calculate the number of visible/total children
+            var visibleChildren = 0;
+            var totalChildren = 0;
+            var countChildren = function (item) {
+                $.each(item.children, function (_, child) {
+                    if (child.children.length > 0) {
+                        countChildren(child);
+                    } else {
+                        if (child.visible) {
+                            visibleChildren++;
+                        }
+                        totalChildren++;
+                    }
+                });
+            };
+            countChildren(dataContext);
+
+            markup += ('<span style="margin-top: 5px; margin-left: ' + indent + 'px;" class="expansion-button ' + expansionStyle + '"></span><span class="ancestor-type" style="margin-left: ' + padding + 'px;">' + value + '</span><span class="ancestor-type-rollup">(' + visibleChildren + ' of ' + totalChildren + ')</span>');
+        } else {
+            if (dataContext.parent === null) {
+                padding = 0;
+            }
+
+            markup += ('<span style="margin-left: ' + (indent + padding) + 'px;">' + value + '</span>');
+        }
+
+        return markup;
+    };
+
+    /**
+     * Adds a new controller service of the specified type.
+     * 
+     * @param {string} controllerServiceType
+     */
+    var addControllerService = function (controllerServiceType) {
+        var revision = nf.Client.getRevision();
+
+        // get the desired availability
+        var availability;
+        if (nf.Canvas.isClustered()) {
+            availability = $('#controller-service-availability-combo').combo('getSelectedOption').value;
+        } else {
+            availability = config.node;
+        }
+
+        // add the new controller service
+        var addService = $.ajax({
+            type: 'POST',
+            url: config.urls.controllerServices + '/' + encodeURIComponent(availability),
+            data: {
+                version: revision.version,
+                clientId: revision.clientId,
+                type: controllerServiceType
+            },
+            dataType: 'json'
+        }).done(function (response) {
+            // update the revision
+            nf.Client.setRevision(response.revision);
+
+            // add the item
+            var controllerService = response.controllerService;
+            var controllerServicesGrid = $('#controller-services-table').data('gridInstance');
+            var controllerServicesData = controllerServicesGrid.getData();
+            controllerServicesData.addItem(controllerService);
+
+            // resort
+            controllerServicesData.reSort();
+            controllerServicesGrid.invalidate();
+
+            // select the new controller service
+            var row = controllerServicesData.getRowById(controllerService.id);
+            controllerServicesGrid.setSelectedRows([row]);
+        }).fail(nf.Common.handleAjaxError);
+
+        // hide the dialog
+        $('#new-controller-service-dialog').modal('hide');
+
+        return addService;
+    };
+
+    /**
+     * Initializes the new controller service dialog.
+     */
+    var initNewControllerServiceDialog = function () {
+        // specify the combo options
+        $('#controller-service-type-filter-options').combo({
+            options: [{
+                    text: 'by type',
+                    value: 'label'
+                }, {
+                    text: 'by tag',
+                    value: 'tags'
+                }],
+            select: function (option) {
+                applyControllerServiceTypeFilter();
+            }
+        });
+
+        // specify the controller service availability
+        if (nf.Canvas.isClustered()) {
+            $('#controller-service-availability-combo').combo({
+                options: [{
+                        text: 'Node',
+                        value: config.node,
+                        description: 'This controller service will be available on the nodes only.'
+                    }, {
+                        text: 'Cluster Manager',
+                        value: config.ncm,
+                        description: 'This controller service will be available on the cluster manager only.'
+                    }]
+            });
+            $('#controller-service-availability-container').show();
+        }
+
+        // define the function for filtering the list
+        $('#controller-service-type-filter').keyup(function () {
+            applyControllerServiceTypeFilter();
+        }).focus(function () {
+            if ($(this).hasClass(config.styles.filterList)) {
+                $(this).removeClass(config.styles.filterList).val('');
+            }
+        }).blur(function () {
+            if ($(this).val() === '') {
+                $(this).addClass(config.styles.filterList).val(config.filterText);
+            }
+        }).addClass(config.styles.filterList).val(config.filterText);
+
+        // initialize the processor type table
+        var controllerServiceTypesColumns = [
+            {id: 'type', name: 'Type', field: 'label', formatter: expandableTypeFormatter, sortable: false, resizable: true},
+            {id: 'tags', name: 'Tags', field: 'tags', sortable: false, resizable: true}
+        ];
+
+        // initialize the dataview
+        var controllerServiceTypesData = new Slick.Data.DataView({
+            inlineFilters: false
+        });
+        controllerServiceTypesData.setItems([]);
+        controllerServiceTypesData.setFilterArgs({
+            searchString: getControllerServiceTypeFilterText(),
+            property: $('#controller-service-type-filter-options').combo('getSelectedOption').value
+        });
+        controllerServiceTypesData.setFilter(filterControllerServiceTypes);
+        controllerServiceTypesData.getItemMetadata = function (index) {
+            var item = controllerServiceTypesData.getItem(index);
+            if (item && item.children.length > 0) {
+                return {
+                    selectable: false,
+                    columns: {
+                        0: {
+                            colspan: '*'
+                        }
+                    }
+                };
+            } else {
+                return {};
+            }
+        };
+
+        var getVisibleControllerServiceCount = function () {
+            var count = 0;
+            for (var i = 0; i < controllerServiceTypesData.getLength(); i++) {
+                var item = controllerServiceTypesData.getItem(i);
+                if (item.children.length === 0) {
+                    count++;
+                }
+            }
+            return count;
+        };
+
+        // initialize the grid
+        var controllerServiceTypesGrid = new Slick.Grid('#controller-service-types-table', controllerServiceTypesData, controllerServiceTypesColumns, gridOptions);
+        controllerServiceTypesGrid.setSelectionModel(new Slick.RowSelectionModel());
+        controllerServiceTypesGrid.registerPlugin(new Slick.AutoTooltips());
+        controllerServiceTypesGrid.setSortColumn('type', true);
+        controllerServiceTypesGrid.onSelectedRowsChanged.subscribe(function (e, args) {
+            if ($.isArray(args.rows) && args.rows.length === 1) {
+                var controllerServiceTypeIndex = args.rows[0];
+                var controllerServiceType = controllerServiceTypesGrid.getDataItem(controllerServiceTypeIndex);
+
+                // only allow selection of service implementations
+                if (controllerServiceType.children.length === 0) {
+                    // set the controller service type description
+                    if (nf.Common.isBlank(controllerServiceType.description)) {
+                        $('#controller-service-type-description').attr('title', '').html('<span class="unset">No description specified</span>');
+                    } else {
+                        $('#controller-service-type-description').html(controllerServiceType.description).ellipsis();
+                    }
+
+                    // populate the dom
+                    $('#controller-service-type-name').text(controllerServiceType.label).ellipsis();
+                    $('#selected-controller-service-name').text(controllerServiceType.label);
+                    $('#selected-controller-service-type').text(controllerServiceType.type);
+
+                    // show the selected controller service
+                    $('#controller-service-description-container').show();
+                }
+            }
+        });
+        controllerServiceTypesGrid.onClick.subscribe(function (e, args) {
+            var item = controllerServiceTypesData.getItem(args.row);
+            if (item && item.children.length > 0) {
+                // update the grid
+                item.collapsed = !item.collapsed;
+                controllerServiceTypesData.updateItem(item.id, item);
+
+                // update any affected ancestors
+                var parent = item.parent;
+                while (parent !== null) {
+                    controllerServiceTypesData.updateItem(parent.id, parent);
+                    parent = parent.parent;
+                }
+
+                // prevent selection within slickgrid
+                e.stopImmediatePropagation();
+            }
+        });
+        controllerServiceTypesGrid.onDblClick.subscribe(function (e, args) {
+            var controllerServiceType = controllerServiceTypesGrid.getDataItem(args.row);
+            addControllerService(controllerServiceType.type);
+        });
+
+        // wire up the dataview to the grid
+        controllerServiceTypesData.onRowCountChanged.subscribe(function (e, args) {
+            controllerServiceTypesGrid.updateRowCount();
+            controllerServiceTypesGrid.render();
+
+            // update the total number of displayed processors
+            $('#displayed-controller-service-types').text(getVisibleControllerServiceCount());
+        });
+        controllerServiceTypesData.onRowsChanged.subscribe(function (e, args) {
+            controllerServiceTypesGrid.invalidateRows(args.rows);
+            controllerServiceTypesGrid.render();
+        });
+        controllerServiceTypesData.syncGridSelection(controllerServiceTypesGrid, true);
+
+        // hold onto an instance of the grid
+        $('#controller-service-types-table').data('gridInstance', controllerServiceTypesGrid);
+
+        // load the available controller services
+        $.ajax({
+            type: 'GET',
+            url: config.urls.controllerServiceTypes,
+            dataType: 'json'
+        }).done(function (response) {
+            var id = 0;
+            var tags = [];
+
+            // begin the update
+            controllerServiceTypesData.beginUpdate();
+
+            var addType = function (parentItem, documentedType) {
+                var item = {
+                    id: id++,
+                    label: nf.Common.substringAfterLast(documentedType.type, '.'),
+                    type: documentedType.type,
+                    description: nf.Common.escapeHtml(documentedType.description),
+                    tags: documentedType.tags.join(', '),
+                    parent: parentItem,
+                    children: [],
+                    collapsed: false,
+                    visible: true
+                };
+
+                // add the documented type
+                controllerServiceTypesData.addItem(item);
+
+                // count the frequency of each tag for this type
+                $.each(documentedType.tags, function (i, tag) {
+                    tags.push(tag.toLowerCase());
+                });
+
+                // add each of its children
+                $.each(documentedType.childTypes, function (_, documentedChildType) {
+                    var childItem = addType(item, documentedChildType);
+                    item.children.push(childItem);
+                });
+
+                return item;
+            };
+
+            // go through each controller service type
+            $.each(response.controllerServiceTypes, function (i, documentedType) {
+                addType(null, documentedType);
+            });
+
+            // end the udpate
+            controllerServiceTypesData.endUpdate();
+            
+            // set the total number of processors
+            $('#total-controller-service-types, #displayed-controller-service-types').text(getVisibleControllerServiceCount());
+
+            // create the tag cloud
+            $('#controller-service-tag-cloud').tagcloud({
+                tags: tags,
+                select: applyControllerServiceTypeFilter,
+                remove: applyControllerServiceTypeFilter
+            });
+        }).fail(nf.Common.handleAjaxError);
+
+        // initialize the controller service dialog
+        $('#new-controller-service-dialog').modal({
+            headerText: 'Add Controller Service',
+            overlayBackground: false,
+            buttons: [{
+                    buttonText: 'Add',
+                    handler: {
+                        click: function () {
+                            var selectedServiceType = $('#selected-controller-service-type').text();
+                            
+                            // ensure something was selected
+                            if (selectedServiceType === '') {
+                                nf.Dialog.showOkDialog({
+                                    dialogContent: 'The type of controller service to create must be selected.',
+                                    overlayBackground: false
+                                });
+                            } else {
+                                addControllerService(selectedServiceType);
+                            }
+                        }
+                    }
+                }, {
+                    buttonText: 'Cancel',
+                    handler: {
+                        click: function () {
+                            $(this).modal('hide');
+                        }
+                    }
+                }],
+            handler: {
+                close: function () {
+                    // clear the selected row
+                    clearSelectedControllerService();
+
+                    // clear any filter strings
+                    $('#controller-service-type-filter').addClass(config.styles.filterList).val(config.filterText);
+
+                    // clear the tagcloud
+                    $('#controller-service-tag-cloud').tagcloud('clearSelectedTags');
+
+                    // reset the filter
+                    applyControllerServiceTypeFilter();
+
+                    // unselect any current selection
+                    var processTypesGrid = $('#controller-service-types-table').data('gridInstance');
+                    processTypesGrid.setSelectedRows([]);
+                    processTypesGrid.resetActiveCell();
+                }
+            }
+        }).draggable({
+            containment: 'parent',
+            handle: '.dialog-header'
+        });
+    };
+
+    /**
+     * Formatter for the type column.
+     * 
+     * @param {type} row
+     * @param {type} cell
+     * @param {type} value
+     * @param {type} columnDef
+     * @param {type} dataContext
+     * @returns {String}
+     */
+    var typeFormatter = function (row, cell, value, columnDef, dataContext) {
+        return nf.Common.substringAfterLast(value, '.');
+    };
+
+    /**
+     * Formatter for the availability column.
+     * 
+     * @param {type} row
+     * @param {type} cell
+     * @param {type} value
+     * @param {type} columnDef
+     * @param {type} dataContext
+     * @returns {String}
+     */
+    var availabilityFormatter = function (row, cell, value, columnDef, dataContext) {
+        if (value === config.node) {
+            return 'Node';
+        } else {
+            return 'Cluster Manager';
+        }
+    };
+
+    /**
+     * Sorts the specified data using the specified sort details.
+     * 
+     * @param {object} sortDetails
+     * @param {object} data
+     */
+    var sort = function (sortDetails, data) {
+        // defines a function for sorting
+        var comparer = function (a, b) {
+            var aString = nf.Common.isDefinedAndNotNull(a[sortDetails.columnId]) ? a[sortDetails.columnId] : '';
+            var bString = nf.Common.isDefinedAndNotNull(b[sortDetails.columnId]) ? b[sortDetails.columnId] : '';
+            return aString === bString ? 0 : aString > bString ? 1 : -1;
+        };
+
+        // perform the sort
+        data.sort(comparer, sortDetails.sortAsc);
+    };
+
+    /**
+     * Initializes the controller services tab.
+     */
+    var initControllerServices = function () {
+        // initialize the new controller service dialog
+        initNewControllerServiceDialog();
+
+        // more details formatter
+        var moreControllerServiceDetails = function (row, cell, value, columnDef, dataContext) {
+            var markup = '<img src="images/iconDetails.png" title="View Details" class="pointer view-controller-service" style="margin-top: 5px; float: left;" />&nbsp;&nbsp;';
+            if (!nf.Common.isEmpty(dataContext.validationErrors)) {
+                markup += '<img src="images/iconAlert.png" class="has-errors" style="margin-top: 4px; float: left;" /><span class="hidden row-id">' + nf.Common.escapeHtml(dataContext.id) + '</span>';
+            }
+            return markup;
+        };
+        
+        var controllerServiceStateFormatter = function (row, cell, value, columnDef, dataContext) {
+            // determine the appropriate label
+            var icon = '', label = '';
+            if (!nf.Common.isEmpty(dataContext.validationErrors)) {
+                icon = 'invalid';
+                label = 'Invalid';
+            } else {
+                if (value === 'DISABLED') {
+                    icon = 'disabled';
+                    label = 'Disabled';
+                } else if (value === 'DISABLING') {
+                    icon = 'disabled';
+                    label = 'Disabling';
+                } else if (value === 'ENABLED') {
+                    icon = 'enabled';
+                    label = 'Enabled';
+                } else if (value === 'ENABLING') {
+                    icon = 'enabled';
+                    label = 'Enabling';
+                }
+            }
+            
+            // format the markup
+            var formattedValue = '<div class="' + icon + '" style="margin-top: 3px;"></div>';
+            return formattedValue + '<div class="status-text" style="margin-top: 2px; margin-left: 4px; float: left;">' + label + '</div>';
+        };
+
+        var controllerServiceActionFormatter = function (row, cell, value, columnDef, dataContext) {
+            var markup = '';
+
+            // only DFMs can edit a controller service
+            if (nf.Common.isDFM()) {
+                if (dataContext.state === 'ENABLED' || dataContext.state === 'ENABLING') {
+                    markup += '<img src="images/iconDisable.png" title="Disable" class="pointer disable-controller-service" style="margin-top: 2px;" />&nbsp;';
+                } else if (dataContext.state === 'DISABLED') {
+                    markup += '<img src="images/iconEdit.png" title="Edit" class="pointer edit-controller-service" style="margin-top: 2px;" />&nbsp;';
+                    markup += '<img src="images/iconEnable.png" title="Enable" class="pointer enable-controller-service" style="margin-top: 2px;"/>&nbsp;';
+                    markup += '<img src="images/iconDelete.png" title="Remove" class="pointer delete-controller-service" style="margin-top: 2px;" />&nbsp;';
+                }
+            }
+
+            // always include a button to view the usage
+            markup += '<img src="images/iconUsage.png" title="Usage" class="pointer controller-service-usage" style="margin-top: 2px;"/>&nbsp;';
+
+            return markup;
+        };
+
+        // define the column model for the controller services table
+        var controllerServicesColumns = [
+            {id: 'moreDetails', name: '&nbsp;', resizable: false, formatter: moreControllerServiceDetails, sortable: false, width: 50, maxWidth: 50},
+            {id: 'name', field: 'name', name: 'Name', sortable: true, resizable: true},
+            {id: 'type', field: 'type', name: 'Type', formatter: typeFormatter, sortable: true, resizable: true},
+            {id: 'state', field: 'state', name: 'State', formatter: controllerServiceStateFormatter, sortable: true, resizeable: true}
+        ];
+
+        // only show availability when clustered
+        if (nf.Canvas.isClustered()) {
+            controllerServicesColumns.push({id: 'availability', field: 'availability', name: 'Availability', formatter: availabilityFormatter, sortable: true, resizeable: true});
+        }
+        
+        // action column should always be last
+        controllerServicesColumns.push({id: 'actions', name: '&nbsp;', resizable: false, formatter: controllerServiceActionFormatter, sortable: false, width: 90, maxWidth: 90});
+
+        // initialize the dataview
+        var controllerServicesData = new Slick.Data.DataView({
+            inlineFilters: false
+        });
+        controllerServicesData.setItems([]);
+
+        // initialize the sort
+        sort({
+            columnId: 'name',
+            sortAsc: true
+        }, controllerServicesData);
+
+        // initialize the grid
+        var controllerServicesGrid = new Slick.Grid('#controller-services-table', controllerServicesData, controllerServicesColumns, gridOptions);
+        controllerServicesGrid.setSelectionModel(new Slick.RowSelectionModel());
+        controllerServicesGrid.registerPlugin(new Slick.AutoTooltips());
+        controllerServicesGrid.setSortColumn('name', true);
+        controllerServicesGrid.onSort.subscribe(function (e, args) {
+            sort({
+                columnId: args.sortCol.field,
+                sortAsc: args.sortAsc
+            }, controllerServicesData);
+        });
+
+        // configure a click listener
+        controllerServicesGrid.onClick.subscribe(function (e, args) {
+            var target = $(e.target);
+
+            // get the service at this row
+            var controllerService = controllerServicesData.getItem(args.row);
+
+            // determine the desired action
+            if (controllerServicesGrid.getColumns()[args.cell].id === 'actions') {
+                if (target.hasClass('edit-controller-service')) {
+                    nf.ControllerService.showConfiguration(controllerService);
+                } else if (target.hasClass('enable-controller-service')) {
+                    nf.ControllerService.enable(controllerService);
+                } else if (target.hasClass('disable-controller-service')) {
+                    nf.ControllerService.disable(controllerService);
+                } else if (target.hasClass('delete-controller-service')) {
+                    nf.ControllerService.remove(controllerService);
+                } else if (target.hasClass('controller-service-usage')) {
+                    // close the settings dialog
                     $('#shell-close-button').click();
-                }).fail(function (xhr, status, error) {
-                    // close the details panel
-                    $('#settings-cancel').click();
+                    
+                    // open the documentation for this reporting task
+                    nf.Shell.showPage('../nifi-docs/documentation?' + $.param({
+                        select: nf.Common.substringAfterLast(controllerService.type, '.')
+                    })).done(function() {
+                        nf.Settings.showSettings();
+                    });
+                }
+            } else if (controllerServicesGrid.getColumns()[args.cell].id === 'moreDetails') {
+                if (target.hasClass('view-controller-service')) {
+                    nf.ControllerService.showDetails(controllerService);
+                }
+            }
+        });
+
+        // wire up the dataview to the grid
+        controllerServicesData.onRowCountChanged.subscribe(function (e, args) {
+            controllerServicesGrid.updateRowCount();
+            controllerServicesGrid.render();
+        });
+        controllerServicesData.onRowsChanged.subscribe(function (e, args) {
+            controllerServicesGrid.invalidateRows(args.rows);
+            controllerServicesGrid.render();
+        });
+        controllerServicesData.syncGridSelection(controllerServicesGrid, true);
+
+        // hold onto an instance of the grid
+        $('#controller-services-table').data('gridInstance', controllerServicesGrid).on('mouseenter', 'div.slick-cell', function (e) {
+            var errorIcon = $(this).find('img.has-errors');
+            if (errorIcon.length && !errorIcon.data('qtip')) {
+                var serviceId = $(this).find('span.row-id').text();
+
+                // get the service item
+                var item = controllerServicesData.getItemById(serviceId);
+
+                // format the errors
+                var tooltip = nf.Common.formatUnorderedList(item.validationErrors);
+
+                // show the tooltip
+                if (nf.Common.isDefinedAndNotNull(tooltip)) {
+                    errorIcon.qtip($.extend({
+                        content: tooltip,
+                        position: {
+                            target: 'mouse',
+                            viewport: $(window),
+                            adjust: {
+                                x: 8,
+                                y: 8,
+                                method: 'flipinvert flipinvert'
+                            }
+                        }
+                    }, nf.Common.config.tooltipConfig));
+                }
+            }
+        });
+    };
 
-                    // handle the error
-                    nf.Common.handleAjaxError(xhr, status, error);
+    /**
+     * Loads the controller services.
+     */
+    var loadControllerServices = function () {
+        var services = [];
+
+        // get the controller services that are running on the nodes
+        var nodeControllerServices = $.ajax({
+            type: 'GET',
+            url: config.urls.controllerServices + '/' + encodeURIComponent(config.node),
+            dataType: 'json'
+        }).done(function (response) {
+            var nodeServices = response.controllerServices;
+            if (nf.Common.isDefinedAndNotNull(nodeServices)) {
+                $.each(nodeServices, function (_, nodeService) {
+                    services.push(nodeService);
                 });
+            }
+        });
+
+        // get the controller services that are running on the ncm
+        var ncmControllerServices = $.Deferred(function (deferred) {
+            if (nf.Canvas.isClustered()) {
+                $.ajax({
+                    type: 'GET',
+                    url: config.urls.controllerServices + '/' + encodeURIComponent(config.ncm),
+                    dataType: 'json'
+                }).done(function (response) {
+                    var ncmServices = response.controllerServices;
+                    if (nf.Common.isDefinedAndNotNull(ncmServices)) {
+                        $.each(ncmServices, function (_, ncmService) {
+                            services.push(ncmService);
+                        });
+                    }
+                    deferred.resolve();
+                }).fail(function () {
+                    deferred.reject();
+                });
+            } else {
+                deferred.resolve();
+            }
+        }).promise();
+
+        // add all controller services
+        return $.when(nodeControllerServices, ncmControllerServices).done(function () {
+            var controllerServicesElement = $('#controller-services-table');
+            nf.Common.cleanUpTooltips(controllerServicesElement, 'img.has-errors');
+
+            var controllerServicesGrid = controllerServicesElement.data('gridInstance');
+            var controllerServicesData = controllerServicesGrid.getData();
+
+            // update the controller services
+            controllerServicesData.setItems(services);
+            controllerServicesData.reSort();
+            controllerServicesGrid.invalidate();
+        });
+    };
+
+    /**
+     * Get the text out of the filter field. If the filter field doesn't
+     * have any text it will contain the text 'filter list' so this method
+     * accounts for that.
+     */
+    var getReportingTaskTypeFilterText = function () {
+        var filterText = '';
+        var filterField = $('#reporting-task-type-filter');
+        if (!filterField.hasClass(config.styles.filterList)) {
+            filterText = filterField.val();
+        }
+        return filterText;
+    };
+
+    /**
+     * Filters the reporting task type table.
+     */
+    var applyReportingTaskTypeFilter = function () {
+        // get the dataview
+        var reportingTaskTypesGrid = $('#reporting-task-types-table').data('gridInstance');
+
+        // ensure the grid has been initialized
+        if (nf.Common.isDefinedAndNotNull(reportingTaskTypesGrid)) {
+            var reportingTaskTypesData = reportingTaskTypesGrid.getData();
+
+            // update the search criteria
+            reportingTaskTypesData.setFilterArgs({
+                searchString: getReportingTaskTypeFilterText(),
+                property: $('#reporting-task-type-filter-options').combo('getSelectedOption').value
+            });
+
+            // need to invalidate the entire table since parent elements may need to be 
+            // rerendered due to changes in their children
+            reportingTaskTypesData.refresh();
+            reportingTaskTypesGrid.invalidate();
+        }
+    };
+
+    /**
+     * Hides the selected reporting task.
+     */
+    var clearSelectedReportingTask = function () {
+        if (nf.Canvas.isClustered()) {
+            $('#reporting-task-availability-combo').combo('setSelectedOption', {
+                value: config.node
             });
+        }
+        
+        $('#reporting-task-type-description').text('');
+        $('#reporting-task-type-name').text('');
+        $('#selected-reporting-task-name').text('');
+        $('#selected-reporting-task-type').text('');
+        $('#reporting-task-description-container').hide();
+    };
+
+    /**
+     * Clears the selected reporting task type.
+     */
+    var clearReportingTaskSelection = function () {
+        // clear the selected row
+        clearSelectedReportingTask();
+
+        // clear the active cell the it can be reselected when its included
+        var reportingTaskTypesGrid = $('#reporting-task-types-table').data('gridInstance');
+        reportingTaskTypesGrid.resetActiveCell();
+    };
+
+    /**
+     * Performs the filtering.
+     * 
+     * @param {object} item     The item subject to filtering
+     * @param {object} args     Filter arguments
+     * @returns {Boolean}       Whether or not to include the item
+     */
+    var filterReportingTaskTypes = function (item, args) {
+        // determine if the item matches the filter
+        var matchesFilter = matchesRegex(item, args);
+
+        // determine if the row matches the selected tags
+        var matchesTags = true;
+        if (matchesFilter) {
+            var tagFilters = $('#reporting-task-tag-cloud').tagcloud('getSelectedTags');
+            var hasSelectedTags = tagFilters.length > 0;
+            if (hasSelectedTags) {
+                matchesTags = matchesSelectedTags(tagFilters, item['tags']);
+            }
+        }
+
+        // determine if this row should be visible
+        var matches = matchesFilter && matchesTags;
+
+        // if this row is currently selected and its being filtered
+        if (matches === false && $('#selected-reporting-task-type').text() === item['type']) {
+            clearReportingTaskSelection();
+        }
+
+        // update visibility flag
+        item.visible = matches;
+
+        return matches;
+    };
 
-            // install a cancel button listener to close the shell
-            $('#settings-cancel').click(function () {
-                $('#shell-close-button').click();
+    /**
+     * Adds a new reporting task of the specified type.
+     * 
+     * @param {string} reportingTaskType
+     */
+    var addReportingTask = function (reportingTaskType) {
+        var revision = nf.Client.getRevision();
+
+        // get the desired availability
+        var availability;
+        if (nf.Canvas.isClustered()) {
+            availability = $('#reporting-task-availability-combo').combo('getSelectedOption').value;
+        } else {
+            availability = config.node;
+        }
+
+        // add the new reporting task
+        var addTask = $.ajax({
+            type: 'POST',
+            url: config.urls.reportingTasks + '/' + encodeURIComponent(availability),
+            data: {
+                version: revision.version,
+                clientId: revision.clientId,
+                type: reportingTaskType
+            },
+            dataType: 'json'
+        }).done(function (response) {
+            // update the revision
+            nf.Client.setRevision(response.revision);
+
+            // add the item
+            var reportingTask = response.reportingTask;
+            var reportingTaskGrid = $('#reporting-tasks-table').data('gridInstance');
+            var reportingTaskData = reportingTaskGrid.getData();
+            reportingTaskData.addItem(reportingTask);
+
+            // resort
+            reportingTaskData.reSort();
+            reportingTaskGrid.invalidate();
+
+            // select the new reporting task
+            var row = reportingTaskData.getRowById(reportingTask.id);
+            reportingTaskGrid.setSelectedRows([row]);
+        }).fail(nf.Common.handleAjaxError);
+
+        // hide the dialog
+        $('#new-reporting-task-dialog').modal('hide');
+
+        return addTask;
+    };
+
+    /**
+     * Initializes the new reporting task dialog.
+     */
+    var initNewReportingTaskDialog = function () {
+        // specify the combo options
+        $('#reporting-task-type-filter-options').combo({
+            options: [{
+                    text: 'by type',
+                    value: 'label'
+                }, {
+                    text: 'by tag',
+                    value: 'tags'
+                }],
+            select: function (option) {
+                applyReportingTaskTypeFilter();
+            }
+        });
+
+        // specify the reporting task availability
+        if (nf.Canvas.isClustered()) {
+            $('#reporting-task-availability-combo').combo({
+                options: [{
+                        text: 'Node',
+                        value: config.node,
+                        description: 'This reporting task will be available on the nodes only.'
+                    }, {
+                        text: 'Cluster Manager',
+                        value: config.ncm,
+                        description: 'This reporting task will be available on the cluster manager only.'
+                    }]
             });
+            $('#reporting-task-availability-container').show();
+        }
+
+        // define the function for filtering the list
+        $('#reporting-task-type-filter').keyup(function () {
+            applyReportingTaskTypeFilter();
+        }).focus(function () {
+            if ($(this).hasClass(config.styles.filterList)) {
+                $(this).removeClass(config.styles.filterList).val('');
+            }
+        }).blur(function () {
+            if ($(this).val() === '') {
+                $(this).addClass(config.styles.filterList).val(config.filterText);
+            }
+        }).addClass(config.styles.filterList).val(config.filterText);
+
+        // initialize the processor type table
+        var reportingTaskTypesColumns = [
+            {id: 'type', name: 'Type', field: 'label', sortable: false, resizable: true},
+            {id: 'tags', name: 'Tags', field: 'tags', sortable: false, resizable: true}
+        ];
+
+        // initialize the dataview
+        var reportingTaskTypesData = new Slick.Data.DataView({
+            inlineFilters: false
+        });
+        reportingTaskTypesData.setItems([]);
+        reportingTaskTypesData.setFilterArgs({
+            searchString: getReportingTaskTypeFilterText(),
+            property: $('#reporting-task-type-filter-options').combo('getSelectedOption').value
+        });
+        reportingTaskTypesData.setFilter(filterReportingTaskTypes);
+
+        // initialize the grid
+        var reportingTaskTypesGrid = new Slick.Grid('#reporting-task-types-table', reportingTaskTypesData, reportingTaskTypesColumns, gridOptions);
+        reportingTaskTypesGrid.setSelectionModel(new Slick.RowSelectionModel());
+        reportingTaskTypesGrid.registerPlugin(new Slick.AutoTooltips());
+        reportingTaskTypesGrid.setSortColumn('type', true);
+        reportingTaskTypesGrid.onSelectedRowsChanged.subscribe(function (e, args) {
+            if ($.isArray(args.rows) && args.rows.length === 1) {
+                var reportingTaskTypeIndex = args.rows[0];
+                var reportingTaskType = reportingTaskTypesGrid.getDataItem(reportingTaskTypeIndex);
+
+                // set the reporting task type description
+                if (nf.Common.isBlank(reportingTaskType.description)) {
+                    $('#reporting-task-type-description').attr('title', '').html('<span class="unset">No description specified</span>');
+                } else {
+                    $('#reporting-task-type-description').html(reportingTaskType.description).ellipsis();
+                }
+
+                // populate the dom
+                $('#reporting-task-type-name').text(reportingTaskType.label).ellipsis();
+                $('#selected-reporting-task-name').text(reportingTaskType.label);
+                $('#selected-reporting-task-type').text(reportingTaskType.type);
+
+                // show the selected reporting task
+                $('#reporting-task-description-container').show();
+            }
+        });
+        reportingTaskTypesGrid.onDblClick.subscribe(function (e, args) {
+            var reportingTaskType = reportingTaskTypesGrid.getDataItem(args.row);
+            addReportingTask(reportingTaskType.type);
+        });
+
+        // wire up the dataview to the grid
+        reportingTaskTypesData.onRowCountChanged.subscribe(function (e, args) {
+            reportingTaskTypesGrid.updateRowCount();
+            reportingTaskTypesGrid.render();
+
+            // update the total number of displayed processors
+            $('#displayed-reporting-task-types').text(args.current);
+        });
+        reportingTaskTypesData.onRowsChanged.subscribe(function (e, args) {
+            reportingTaskTypesGrid.invalidateRows(args.rows);
+            reportingTaskTypesGrid.render();
+        });
+        reportingTaskTypesData.syncGridSelection(reportingTaskTypesGrid, true);
+
+        // hold onto an instance of the grid
+        $('#reporting-task-types-table').data('gridInstance', reportingTaskTypesGrid);
+
+        // load the available reporting tasks
+        $.ajax({
+            type: 'GET',
+            url: config.urls.reportingTaskTypes,
+            dataType: 'json'
+        }).done(function (response) {
+            var id = 0;
+            var tags = [];
+
+            // begin the update
+            reportingTaskTypesData.beginUpdate();
+
+            // go through each reporting task type
+            $.each(response.reportingTaskTypes, function (i, documentedType) {
+                // add the documented type
+                reportingTaskTypesData.addItem({
+                    id: id++,
+                    label: nf.Common.substringAfterLast(documentedType.type, '.'),
+                    type: documentedType.type,
+                    description: nf.Common.escapeHtml(documentedType.description),
+                    tags: documentedType.tags.join(', '),
+                    children: [],
+                    collapsed: false,
+                    visible: true
+                });
+
+                // count the frequency of each tag for this type
+                $.each(documentedType.tags, function (i, tag) {
+                    tags.push(tag.toLowerCase());
+                });
+            });
+
+            // end the udpate
+            reportingTaskTypesData.endUpdate();
+
+            // set the total number of processors
+            $('#total-reporting-task-types, #displayed-reporting-task-types').text(response.reportingTaskTypes.length);
+
+            // create the tag cloud
+            $('#reporting-task-tag-cloud').tagcloud({
+                tags: tags,
+                select: applyReportingTaskTypeFilter,
+                remove: applyReportingTaskTypeFilter
+            });
+        }).fail(nf.Common.handleAjaxError);
+
+        // initialize the reporting task dialog
+        $('#new-reporting-task-dialog').modal({
+            headerText: 'Add Reporting Task',
+            overlayBackground: false,
+            buttons: [{
+                    buttonText: 'Add',
+                    handler: {
+                        click: function () {
+                            var selectedTaskType = $('#selected-reporting-task-type').text();
+                            
+                            // ensure something was selected
+                            if (selectedTaskType === '') {
+                                nf.Dialog.showOkDialog({
+                                    dialogContent: 'The type of reporting task to create must be selected.',
+                                    overlayBackground: false
+                                });
+                            } else {
+                                addReportingTask(selectedTaskType);
+                            }
+                        }
+                    }
+                }, {
+                    buttonText: 'Cancel',
+                    handler: {
+                        click: function () {
+                            $(this).modal('hide');
+                        }
+                    }
+                }],
+            handler: {
+                close: function () {
+                    // clear the selected row
+                    clearSelectedReportingTask();
+
+                    // clear any filter strings
+                    $('#reporting-task-type-filter').addClass(config.styles.filterList).val(config.filterText);
+
+                    // clear the tagcloud
+                    $('#reporting-task-tag-cloud').tagcloud('clearSelectedTags');
+
+                    // reset the filter
+                    applyReportingTaskTypeFilter();
+
+                    // unselect any current selection
+                    var reportingTaskTypesGrid = $('#reporting-task-types-table').data('gridInstance');
+                    reportingTaskTypesGrid.setSelectedRows([]);
+                    reportingTaskTypesGrid.resetActiveCell();
+                }
+            }
+        }).draggable({
+            containment: 'parent',
+            handle: '.dialog-header'
+        });
+    };
+
+    /**
+     * Initializes the reporting tasks tab.
+     */
+    var initReportingTasks = function () {
+        // initialize the new reporting task dialog
+        initNewReportingTaskDialog();
+
+        var moreReportingTaskDetails = function (row, cell, value, columnDef, dataContext) {
+            var markup = '<img src="images/iconDetails.png" title="View Details" class="pointer view-reporting-task" style="margin-top: 5px; float: left;" />&nbsp;&nbsp;';
+            if (!nf.Common.isEmpty(dataContext.validationErrors)) {
+                markup += '<img src="images/iconAlert.png" class="has-errors" style="margin-top: 4px; float: left;" /><span class="hidden row-id">' + nf.Common.escapeHtml(dataContext.id) + '</span>';
+            }
+            return markup;
+        };
+        
+        var reportingTaskRunStatusFormatter = function (row, cell, value, columnDef, dataContext) {
+            // determine the appropriate label
+            var label;
+            if (!nf.Common.isEmpty(dataContext.validationErrors)) {
+                label = 'Invalid';
+            } else {
+                if (value === 'STOPPED') {
+                    label = 'Stopped';
+                } else if (value === 'RUNNING') {
+                    label = 'Running';
+                } else {
+                    label = 'Disabled';
+                }
+            }
+            
+            // include the active thread count if appropriate
+            var activeThreadCount = '';
+            if (nf.Common.isDefinedAndNotNull(dataContext.activeThreadCount) && dataContext.activeThreadCount > 0) {
+                activeThreadCount = '(' + dataContext.activeThreadCount + ')';
+            }
+            
+            // format the markup
+            var formattedValue = '<div class="' + nf.Common.escapeHtml(label.toLowerCase()) + '" style="margin-top: 3px;"></div>';
+            return formattedValue + '<div class="status-text" style="margin-top: 2px; margin-left: 4px; float: left;">' + nf.Common.escapeHtml(label) + '</div><div style="float: left; margin-left: 4px;">' + nf.Common.escapeHtml(activeThreadCount) + '</div>';
+        };
+        
+        var reportingTaskActionFormatter = function (row, cell, value, columnDef, dataContext) {
+            var markup = '';
+
+            // only DFMs can edit reporting tasks
+            if (nf.Common.isDFM()) {
+                if (dataContext.state === 'RUNNING') {
+                    markup += '<img src="images/iconStop.png" title="Stop" class="pointer stop-reporting-task" style="margin-top: 2px;" />&nbsp;';
+                } else if (dataContext.state === 'STOPPED' || dataContext.state === 'DISABLED') {
+                    markup += '<img src="images/iconEdit.png" title="Edit" class="pointer edit-reporting-task" style="margin-top: 2px;" />&nbsp;';
+                    markup += '<img src="images/iconRun.png" title="Start" class="pointer start-reporting-task" style="margin-top: 2px;"/>&nbsp;';
+                    markup += '<img src="images/iconDelete.png" title="Remove" class="pointer delete-reporting-task" style="margin-top: 2px;" />&nbsp;';
+                }
+            }
+
+            // always include a button to view the usage
+            markup += '<img src="images/iconUsage.png" title="Usage" class="pointer reporting-task-usage" style="margin-top: 2px;"/>&nbsp;';
+
+            return markup;
+        };
+
+        // define the column model for the reporting tasks table
+        var reportingTasksColumnModel = [
+            {id: 'moreDetails', field: 'moreDetails', name: '&nbsp;', resizable: false, formatter: moreReportingTaskDetails, sortable: true, width: 50, maxWidth: 50},
+            {id: 'name', field: 'name', name: 'Name', sortable: true, resizable: true},
+            {id: 'type', field: 'type', name: 'Type', sortable: true, resizable: true, formatter: typeFormatter},
+            {id: 'state', field: 'state', name: 'Run Status', sortable: true, resizeable: true, formatter: reportingTaskRunStatusFormatter}
+        ];
+
+        // only show availability when clustered
+        if (nf.Canvas.isClustered()) {
+            reportingTasksColumnModel.push({id: 'availability', field: 'availability', name: 'Availability', formatter: availabilityFormatter, sortable: true, resizeable: true});
+        }
+        
+        // action column should always be last
+        reportingTasksColumnModel.push({id: 'actions', name: '&nbsp;', resizable: false, formatter: reportingTaskActionFormatter, sortable: false, width: 90, maxWidth: 90});
+
+        // initialize the dataview
+        var reportingTasksData = new Slick.Data.DataView({
+            inlineFilters: false
+        });
+        reportingTasksData.setItems([]);
+
+        // initialize the sort
+        sort({
+            columnId: 'name',
+            sortAsc: true
+        }, reportingTasksData);
+
+        // initialize the grid
+        var reportingTasksGrid = new Slick.Grid('#reporting-tasks-table', reportingTasksData, reportingTasksColumnModel, gridOptions);
+        reportingTasksGrid.setSelectionModel(new Slick.RowSelectionModel());
+        reportingTasksGrid.registerPlugin(new Slick.AutoTooltips());
+        reportingTasksGrid.setSortColumn('name', true);
+        reportingTasksGrid.onSort.subscribe(function (e, args) {
+            sort({
+                columnId: args.sortCol.field,
+                sortAsc: args.sortAsc
+            }, reportingTasksData);
+        });
+
+        // configure a click listener
+        reportingTasksGrid.onClick.subscribe(function (e, args) {
+            var target = $(e.target);
+
+            // get the service at this row
+            var reportingTask = reportingTasksData.getItem(args.row);
+
+            // determine the desired action
+            if (reportingTasksGrid.getColumns()[args.cell].id === 'actions') {
+                if (target.hasClass('edit-reporting-task')) {
+                    nf.ReportingTask.showConfiguration(reportingTask);
+                } else if (target.hasClass('start-reporting-task')) {
+                    nf.ReportingTask.start(reportingTask);
+                } else if (target.hasClass('stop-reporting-task')) {
+                    nf.ReportingTask.stop(reportingTask);
+                } else if (target.hasClass('delete-reporting-task')) {
+                    nf.ReportingTask.remove(reportingTask);
+                } else if (target.hasClass('reporting-task-usage')) {
+                    // close the settings dialog
+                    $('#shell-close-button').click();
+                    
+                    // open the documentation for this reporting task
+                    nf.Shell.showPage('../nifi-docs/documentation?' + $.param({
+                        select: nf.Common.substringAfterLast(reportingTask.type, '.')
+                    })).done(function() {
+                        nf.Settings.showSettings();
+                    });
+                }
+            } else if (reportingTasksGrid.getColumns()[args.cell].id === 'moreDetails') {
+                if (target.hasClass('view-reporting-task')) {
+                    nf.ReportingTask.showDetails(reportingTask);
+                }
+            }
+        });
+
+        // wire up the dataview to the grid
+        reportingTasksData.onRowCountChanged.subscribe(function (e, args) {
+            reportingTasksGrid.updateRowCount();
+            reportingTasksGrid.render();
+        });
+        reportingTasksData.onRowsChanged.subscribe(function (e, args) {
+            reportingTasksGrid.invalidateRows(args.rows);
+            reportingTasksGrid.render();
+        });
+        reportingTasksData.syncGridSelection(reportingTasksGrid, true);
+
+        // hold onto an instance of the grid
+        $('#reporting-tasks-table').data('gridInstance', reportingTasksGrid).on('mouseenter', 'div.slick-cell', function (e) {
+            var errorIcon = $(this).find('img.has-errors');
+            if (errorIcon.length && !errorIcon.data('qtip')) {
+                var taskId = $(this).find('span.row-id').text();
+
+                // get the service item
+                var item = reportingTasksData.getItemById(taskId);
+
+                // format the errors
+                var tooltip = nf.Common.formatUnorderedList(item.validationErrors);
+
+                // show the tooltip
+                if (nf.Common.isDefinedAndNotNull(tooltip)) {
+                    errorIcon.qtip($.extend({
+                        content: tooltip,
+                        position: {
+                            target: 'mouse',
+                            viewport: $(window),
+                            adjust: {
+                                x: 8,
+                                y: 8,
+                                method: 'flipinvert flipinvert'
+                            }
+                        }
+                    }, nf.Common.config.tooltipConfig));
+                }
+            }
+        });
+    };
+
+    /**
+     * Loads the reporting tasks.
+     */
+    var loadReportingTasks = function () {
+        var tasks = [];
+
+        // get the reporting tasks that are running on the nodes
+        var nodeReportingTasks = $.ajax({
+            type: 'GET',
+            url: config.urls.reportingTasks + '/' + encodeURIComponent(config.node),
+            dataType: 'json'
+        }).done(function (response) {
+            var nodeTasks = response.reportingTasks;
+            if (nf.Common.isDefinedAndNotNull(nodeTasks)) {
+                $.each(nodeTasks, function (_, nodeTask) {
+                    tasks.push(nodeTask);
+                });
+            }
+        });
+
+        // get the reporting tasks that are running on the ncm
+        var ncmReportingTasks = $.Deferred(function (deferred) {
+            if (nf.Canvas.isClustered()) {
+                $.ajax({
+                    type: 'GET',
+                    url: config.urls.reportingTasks + '/' + encodeURIComponent(config.ncm),
+                    dataType: 'json'
+                }).done(function (response) {
+                    var ncmTasks = response.reportingTasks;
+                    if (nf.Common.isDefinedAndNotNull(ncmTasks)) {
+                        $.each(ncmTasks, function (_, ncmTask) {
+                            tasks.push(ncmTask);
+                        });
+                    }
+                    deferred.resolve();
+                }).fail(function () {
+                    deferred.reject();
+                });
+            } else {
+                deferred.resolve();
+            }
+        }).promise();
+
+        // add all reporting tasks
+        return $.when(nodeReportingTasks, ncmReportingTasks).done(function () {
+            var reportingTasksElement = $('#reporting-tasks-table');
+            nf.Common.cleanUpTooltips(reportingTasksElement, 'img.has-errors');
+
+            var reportingTasksGrid = reportingTasksElement.data('gridInstance');
+            var reportingTasksData = reportingTasksGrid.getData();
+
+            // update the reporting tasks
+            reportingTasksData.setItems(tasks);
+            reportingTasksData.reSort();
+            reportingTasksGrid.invalidate();
+        });
+    };
+
+    return {
+        /**
+         * Initializes the status page.
+         */
+        init: function () {
+            // initialize the settings tabs
+            $('#settings-tabs').tabbs({
+                tabStyle: 'settings-tab',
+                selectedTabStyle: 'settings-selected-tab',
+                tabs: [{
+                        name: 'General',
+                        tabContentId: 'general-settings-tab-content'
+                    }, {
+                        name: 'Controller Services',
+                        tabContentId: 'controller-services-tab-content'
+                    }, {
+                        name: 'Reporting Tasks',
+                        tabContentId: 'reporting-tasks-tab-content'
+                    }],
+                select: function () {
+                    var tab = $(this).text();
+                    if (tab === 'General') {
+                        $('#new-service-or-task').hide();
+                    } else {
+                        $('#new-service-or-task').show();
+
+                        // update the tooltip on the button
+                        $('#new-service-or-task').attr('title', function () {
+                            if (tab === 'Controller Services') {
+                                return 'Create a new controller service';
+                            } else if (tab === 'Reporting Tasks') {
+                                return 'Create a new reporting task';
+                            }
+                        });
+
+                        // resize the table
+                        nf.Settings.resetTableSize();
+                    }
+                }
+            });
+
+            // setup the tooltip for the refresh icon
+            $('#settings-refresh-required-icon').qtip($.extend({
+                content: 'This flow has been modified by another user. Please refresh.'
+            }, nf.CanvasUtils.config.systemTooltipConfig));
+
+            // refresh the system diagnostics when clicked
+            nf.Common.addHoverEffect('#settings-refresh-button', 'button-refresh', 'button-refresh-hover').click(function () {
+                if ($('#settings-refresh-required-icon').is(':visible')) {
+                    nf.CanvasHeader.reloadAndClearWarnings();
+                } else {
+                    nf.Settings.loadSettings();
+                }
+            });
+
+            // create a new controller service or reporting task
+            $('#new-service-or-task').on('click', function () {
+                var selectedTab = $('li.settings-selected-tab').text();
+                if (selectedTab === 'Controller Services') {
+                    $('#new-controller-service-dialog').modal('show');
+                    
+                    // reset the canvas size after the dialog is shown
+                    var controllerServiceTypesGrid = $('#controller-service-types-table').data('gridInstance');
+                    if (nf.Common.isDefinedAndNotNull(controllerServiceTypesGrid)) {
+                        controllerServiceTypesGrid.resizeCanvas();
+                    }
+                    
+                    // set the initial focus
+                    $('#controller-service-type-filter').focus();
+                } else if (selectedTab === 'Reporting Tasks') {
+                    $('#new-reporting-task-dialog').modal('show');
+                    
+                    // reset the canvas size after the dialog is shown
+                    var reportingTaskTypesGrid = $('#reporting-task-types-table').data('gridInstance');
+                    if (nf.Common.isDefinedAndNotNull(reportingTaskTypesGrid)) {
+                        reportingTaskTypesGrid.resizeCanvas();
+                    }
+                    
+                    // set the initial focus
+                    $('#reporting-task-type-filter').focus();
+                }
+            });
+
+            // initialize each tab
+            initGeneral();
+            initControllerServices();
+            initReportingTasks();
+        },
+        
+        /**
+         * Update the size of the grid based on its container's current size.
+         */
+        resetTableSize: function () {
+            var controllerServicesGrid = $('#controller-services-table').data('gridInstance');
+            if (nf.Common.isDefinedAndNotNull(controllerServicesGrid)) {
+                controllerServicesGrid.resizeCanvas();
+            }
+
+            var reportingTasksGrid = $('#reporting-tasks-table').data('gridInstance');
+            if (nf.Common.isDefinedAndNotNull(reportingTasksGrid)) {
+                reportingTasksGrid.resizeCanvas();
+            }
         },
         
         /**
          * Shows the settings dialog.
          */
         showSettings: function () {
-            $.ajax({
+            // show the settings dialog
+            nf.Shell.showContent('#settings').done(function () {
+                // reset button state
+                $('#settings-save').mouseout();
+            });
+            
+            // adjust the table size
+            nf.Settings.resetTableSize();
+        },
+        
+        /**
+         * Loads the settings.
+         */
+        loadSettings: function () {
+            var settings = $.ajax({
                 type: 'GET',
                 url: config.urls.controllerConfig,
                 dataType: 'json'
@@ -127,20 +1735,31 @@ nf.Settings = (function () {
                 if (nf.Common.isDefinedAndNotNull(response.config)) {
                     // set the header
                     $('#settings-header-text').text(response.config.name + ' Settings');
+                    $('#settings-last-refreshed').text(response.config.currentTime);
 
                     // populate the controller settings
-                    $('#data-flow-title-field').val(response.config.name);
-                    $('#data-flow-comments-field').val(response.config.comments);
-                    $('#maximum-timer-driven-thread-count-field').val(response.config.maxTimerDrivenThreadCount);
-                    $('#maximum-event-driven-thread-count-field').val(response.config.maxEventDrivenThreadCount);
+                    if (nf.Common.isDFM()) {
+                        $('#data-flow-title-field').val(response.config.name);
+                        $('#data-flow-comments-field').val(response.config.comments);
+                        $('#maximum-timer-driven-thread-count-field').val(response.config.maxTimerDrivenThreadCount);
+                        $('#maximum-event-driven-thread-count-field').val(response.config.maxEventDrivenThreadCount);
+                    } else {
+                        $('#read-only-data-flow-title-field').html(nf.Common.formatValue(response.config.name));
+                        $('#read-only-data-flow-comments-field').html(nf.Common.formatValue(response.config.comments));
+                        $('#read-only-maximum-timer-driven-thread-count-field').text(response.config.maxTimerDrivenThreadCount);
+                        $('#read-only-maximum-event-driven-thread-count-field').text(response.config.maxEventDrivenThreadCount);
+                    }
                 }
+            });
 
-                // show the settings dialog
-                nf.Shell.showContent('#settings').done(function () {
-                    // reset button state
-                    $('#settings-save, #settings-cancel').mouseout();
-                });
-            }).fail(nf.Common.handleAjaxError);
+            // load the controller services
+            var controllerServices = loadControllerServices();
+
+            // load the reporting tasks
+            var reportingTasks = loadReportingTasks();
+
+            // return a deferred for all parts of the settings
+            return $.when(settings, controllerServices, reportingTasks).fail(nf.Common.handleAjaxError);
         }
     };
 }());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-snippet.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-snippet.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-snippet.js
index aa2ecf6..e2e958c 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-snippet.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-snippet.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.Snippet = (function () {
 
     var config = {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-storage.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-storage.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-storage.js
index 0be05fe..3937970 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-storage.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-storage.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.Storage = (function () {
 
     // Store items for two days before being eligible for removal.

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-toolbar-action.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-toolbar-action.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-toolbar-action.js
index d2cde9a..c3adfd6 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-toolbar-action.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-toolbar-action.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 /**
  * Creates a new toolbar action.
  * 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster-table.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster-table.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster-table.js
index 3ede0e5..74bd958 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster-table.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster-table.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, Slick */
+
 nf.ClusterTable = (function () {
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js
index f6cbdc6..5f00e2c 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/cluster/nf-cluster.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, top */
+
 $(document).ready(function () {
     // initialize the counters page
     nf.Cluster.init();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters-table.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters-table.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters-table.js
index 28a36bd..5688118 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters-table.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters-table.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, Slick */
+
 nf.CountersTable = (function () {
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js
index 1f9a861..cbfe336 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/counters/nf-counters.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, top */
+
 $(document).ready(function () {
     // initialize the counters page
     nf.Counters.init();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-model.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-model.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-model.js
index 0564b17..48194b5 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-model.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-model.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global Slick, nf */
+
 (function ($) {
 
     function HistoryModel() {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-table.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-table.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-table.js
index 72fc549..14c97cf 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-table.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-table.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, Slick */
+
 nf.HistoryTable = (function () {
 
     /**
@@ -356,7 +359,7 @@ nf.HistoryTable = (function () {
 
         // inspect the operation to determine if there are any component details
         if (nf.Common.isDefinedAndNotNull(componentDetails)) {
-            if (action.sourceType === 'Processor') {
+            if (action.sourceType === 'Processor' || action.sourceType === 'ControllerService' || action.sourceType === 'ReportingTask') {
                 detailsMarkup.append(
                         $('<div class="action-detail"><div class="history-details-name">Type</div>' + nf.Common.escapeHtml(componentDetails.type) + '</div>'));
             } else if (action.sourceType === 'RemoteProcessGroup') {
@@ -435,4 +438,4 @@ nf.HistoryTable = (function () {
             historyGrid.onViewportChanged.notify();
         }
     };
-}());
\ No newline at end of file
+}());

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js
index 36364df..516507c 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, top */
+
 $(document).ready(function () {
     // initialize the status page
     nf.History.init();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-client.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-client.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-client.js
index 4cc3035..0d490b7 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-client.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-client.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 nf.Client = {};
 
 nf.Client.version = -1;


[16/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessorAuditor.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessorAuditor.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessorAuditor.java
index c3c70c4..ff377a3 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessorAuditor.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessorAuditor.java
@@ -30,7 +30,7 @@ import java.util.Set;
 import org.apache.nifi.action.Action;
 import org.apache.nifi.action.Component;
 import org.apache.nifi.action.Operation;
-import org.apache.nifi.action.component.details.ProcessorDetails;
+import org.apache.nifi.action.component.details.ExtensionDetails;
 import org.apache.nifi.action.details.ActionDetails;
 import org.apache.nifi.action.details.ConfigureDetails;
 import org.apache.nifi.components.PropertyDescriptor;
@@ -78,13 +78,10 @@ public class ProcessorAuditor extends NiFiAuditor {
      * advice precedence. By normalizing all advice into Around advice we can
      * alleviate this issue.
      *
-     * @param processor
+     * @param proceedingJoinPoint
+     * @return 
+     * @throws java.lang.Throwable
      */
-//    @AfterReturning(
-//        pointcut="within(org.apache.nifi.web.dao.ProcessorDAO+) && "
-//            + "execution(org.apache.nifi.web.api.dto.ProcessorDTO createProcessor(org.apache.nifi.web.api.dto.ProcessorDTO))",
-//        returning="processor"
-//    )
     @Around("within(org.apache.nifi.web.dao.ProcessorDAO+) && "
             + "execution(org.apache.nifi.controller.ProcessorNode createProcessor(java.lang.String, org.apache.nifi.web.api.dto.ProcessorDTO))")
     public ProcessorNode createProcessorAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
@@ -106,6 +103,7 @@ public class ProcessorAuditor extends NiFiAuditor {
      * Audits the configuration of a single processor.
      *
      * @param proceedingJoinPoint
+     * @param groupId
      * @param processorDTO
      * @param processorDAO
      * @return
@@ -115,7 +113,7 @@ public class ProcessorAuditor extends NiFiAuditor {
             + "execution(org.apache.nifi.controller.ProcessorNode updateProcessor(java.lang.String, org.apache.nifi.web.api.dto.ProcessorDTO)) && "
             + "args(groupId, processorDTO) && "
             + "target(processorDAO)")
-    public Object updateProcessorAdvice(ProceedingJoinPoint proceedingJoinPoint, String groupId, ProcessorDTO processorDTO, ProcessorDAO processorDAO) throws Throwable {
+    public ProcessorNode updateProcessorAdvice(ProceedingJoinPoint proceedingJoinPoint, String groupId, ProcessorDTO processorDTO, ProcessorDAO processorDAO) throws Throwable {
         // determine the initial values for each property/setting thats changing
         ProcessorNode processor = processorDAO.getProcessor(groupId, processorDTO.getId());
         final Map<String, String> values = extractConfiguredPropertyValues(processor, processorDTO);
@@ -137,7 +135,7 @@ public class ProcessorAuditor extends NiFiAuditor {
             Map<String, String> updatedValues = extractConfiguredPropertyValues(processor, processorDTO);
 
             // create the processor details
-            ProcessorDetails processorDetails = new ProcessorDetails();
+            ExtensionDetails processorDetails = new ExtensionDetails();
             processorDetails.setType(processor.getProcessor().getClass().getSimpleName());
 
             // create a processor action
@@ -240,6 +238,7 @@ public class ProcessorAuditor extends NiFiAuditor {
      * Audits the removal of a processor via deleteProcessor().
      *
      * @param proceedingJoinPoint
+     * @param groupId
      * @param processorId
      * @param processorDAO
      * @throws Throwable
@@ -281,6 +280,7 @@ public class ProcessorAuditor extends NiFiAuditor {
      *
      * @param processor
      * @param operation
+     * @param actionDetails
      * @return
      */
     public Action generateAuditRecord(ProcessorNode processor, Operation operation, ActionDetails actionDetails) {
@@ -292,7 +292,7 @@ public class ProcessorAuditor extends NiFiAuditor {
         // ensure the user was found
         if (user != null) {
             // create the processor details
-            ProcessorDetails processorDetails = new ProcessorDetails();
+            ExtensionDetails processorDetails = new ExtensionDetails();
             processorDetails.setType(processor.getProcessor().getClass().getSimpleName());
 
             // create the processor action for adding this processor

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RelationshipAuditor.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RelationshipAuditor.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RelationshipAuditor.java
index 9cc694b..ba78141 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RelationshipAuditor.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RelationshipAuditor.java
@@ -76,16 +76,13 @@ public class RelationshipAuditor extends NiFiAuditor {
      * advice precedence. By normalizing all advice into Around advice we can
      * alleviate this issue.
      *
-     * @param connection
+     * @param proceedingJoinPoint
+     * @return 
+     * @throws java.lang.Throwable
      */
-//    @AfterReturning(
-//        pointcut="within(org.apache.nifi.view.dao.ConnectionDAO+) && "
-//            + "execution(org.apache.nifi.view.model.Connection createConnection(org.apache.nifi.api.dto.ConnectionDTO))",
-//        returning="connection"
-//    )
     @Around("within(org.apache.nifi.web.dao.ConnectionDAO+) && "
             + "execution(org.apache.nifi.connectable.Connection createConnection(java.lang.String, org.apache.nifi.web.api.dto.ConnectionDTO))")
-    public Object createConnectionAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
+    public Connection createConnectionAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
         // perform the underlying operation
         Connection connection = (Connection) proceedingJoinPoint.proceed();
 
@@ -105,6 +102,7 @@ public class RelationshipAuditor extends NiFiAuditor {
      * Audits the creation and removal of relationships via updateConnection().
      *
      * @param proceedingJoinPoint
+     * @param groupId
      * @param connectionDTO
      * @param connectionDAO
      * @return
@@ -114,7 +112,7 @@ public class RelationshipAuditor extends NiFiAuditor {
             + "execution(org.apache.nifi.connectable.Connection updateConnection(java.lang.String, org.apache.nifi.web.api.dto.ConnectionDTO)) && "
             + "args(groupId, connectionDTO) && "
             + "target(connectionDAO)")
-    public Object updateConnectionAdvice(ProceedingJoinPoint proceedingJoinPoint, String groupId, ConnectionDTO connectionDTO, ConnectionDAO connectionDAO) throws Throwable {
+    public Connection updateConnectionAdvice(ProceedingJoinPoint proceedingJoinPoint, String groupId, ConnectionDTO connectionDTO, ConnectionDAO connectionDAO) throws Throwable {
         // get the previous configuration
         Connection connection = connectionDAO.getConnection(groupId, connectionDTO.getId());
         Connectable previousDestination = connection.getDestination();
@@ -218,6 +216,7 @@ public class RelationshipAuditor extends NiFiAuditor {
      * Audits the removal of relationships via deleteConnection().
      *
      * @param proceedingJoinPoint
+     * @param groupId
      * @param id
      * @param connectionDAO
      * @throws Throwable
@@ -251,7 +250,9 @@ public class RelationshipAuditor extends NiFiAuditor {
      * Creates action details for connect/disconnect actions.
      *
      * @param connection
+     * @param source
      * @param relationships
+     * @param destination
      * @return
      */
     public ConnectDetails createConnectDetails(final Connection connection, final Connectable source, final Collection<Relationship> relationships, final Connectable destination) {
@@ -327,6 +328,7 @@ public class RelationshipAuditor extends NiFiAuditor {
      *
      * @param connection
      * @param operation
+     * @param actionDetails
      * @return
      */
     public Action generateAuditRecordForConnection(Connection connection, Operation operation, ActionDetails actionDetails) {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RemoteProcessGroupAuditor.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RemoteProcessGroupAuditor.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RemoteProcessGroupAuditor.java
index a10495d..d0836d4 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RemoteProcessGroupAuditor.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/RemoteProcessGroupAuditor.java
@@ -60,16 +60,13 @@ public class RemoteProcessGroupAuditor extends NiFiAuditor {
      * advice precedence. By normalizing all advice into Around advice we can
      * alleviate this issue.
      *
-     * @param processor
+     * @param proceedingJoinPoint
+     * @return 
+     * @throws java.lang.Throwable 
      */
-//    @AfterReturning(
-//        pointcut="within(org.apache.nifi.web.dao.RemoteProcessGroupDAO+) && "
-//            + "execution(org.apache.nifi.web.api.dto.RemoteProcessGroupDTO createRemoteProcessGroup(java.lang.String, org.apache.nifi.web.api.dto.RemoteProcessGroupDTO))",
-//        returning="remoteProcessGroup"
-//    )
     @Around("within(org.apache.nifi.web.dao.RemoteProcessGroupDAO+) && "
             + "execution(org.apache.nifi.groups.RemoteProcessGroup createRemoteProcessGroup(java.lang.String, org.apache.nifi.web.api.dto.RemoteProcessGroupDTO))")
-    public Object createRemoteProcessGroupAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
+    public RemoteProcessGroup createRemoteProcessGroupAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
         // create the remote process group
         RemoteProcessGroup remoteProcessGroup = (RemoteProcessGroup) proceedingJoinPoint.proceed();
 
@@ -99,7 +96,7 @@ public class RemoteProcessGroupAuditor extends NiFiAuditor {
             + "execution(org.apache.nifi.groups.RemoteProcessGroup updateRemoteProcessGroup(java.lang.String, org.apache.nifi.web.api.dto.RemoteProcessGroupDTO)) && "
             + "args(groupId, remoteProcessGroupDTO) && "
             + "target(remoteProcessGroupDAO)")
-    public Object auditUpdateProcessGroupConfiguration(ProceedingJoinPoint proceedingJoinPoint, String groupId, RemoteProcessGroupDTO remoteProcessGroupDTO, RemoteProcessGroupDAO remoteProcessGroupDAO) throws Throwable {
+    public RemoteProcessGroup auditUpdateProcessGroupConfiguration(ProceedingJoinPoint proceedingJoinPoint, String groupId, RemoteProcessGroupDTO remoteProcessGroupDTO, RemoteProcessGroupDAO remoteProcessGroupDAO) throws Throwable {
         final RemoteProcessGroup remoteProcessGroup = remoteProcessGroupDAO.getRemoteProcessGroup(groupId, remoteProcessGroupDTO.getId());
 
         // record the current value of this remoteProcessGroups configuration for comparisons later
@@ -332,7 +329,9 @@ public class RemoteProcessGroupAuditor extends NiFiAuditor {
     /**
      * Generates an audit record for the specified remote process group.
      *
-     * @param port
+     * @param remoteProcessGroup
+     * @param operation
+     * @return 
      */
     public Action generateAuditRecord(RemoteProcessGroup remoteProcessGroup, Operation operation) {
         return generateAuditRecord(remoteProcessGroup, operation, null);
@@ -341,7 +340,10 @@ public class RemoteProcessGroupAuditor extends NiFiAuditor {
     /**
      * Generates an audit record for the specified remote process group.
      *
-     * @param port
+     * @param remoteProcessGroup
+     * @param operation
+     * @param actionDetails
+     * @return 
      */
     public Action generateAuditRecord(RemoteProcessGroup remoteProcessGroup, Operation operation, ActionDetails actionDetails) {
         Action action = null;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ReportingTaskAuditor.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ReportingTaskAuditor.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ReportingTaskAuditor.java
new file mode 100644
index 0000000..ba2cdfb
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ReportingTaskAuditor.java
@@ -0,0 +1,353 @@
+/*
+ * 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.audit;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.nifi.action.Action;
+import org.apache.nifi.action.Component;
+import org.apache.nifi.action.Operation;
+import org.apache.nifi.action.component.details.ExtensionDetails;
+import org.apache.nifi.action.details.ActionDetails;
+import org.apache.nifi.action.details.ConfigureDetails;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.controller.ReportingTaskNode;
+import org.apache.nifi.controller.ScheduledState;
+import org.apache.nifi.web.security.user.NiFiUserUtils;
+import org.apache.nifi.user.NiFiUser;
+import org.apache.nifi.web.api.dto.ReportingTaskDTO;
+import org.apache.nifi.web.dao.ReportingTaskDAO;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Audits reporting creation/removal and configuration changes.
+ */
+@Aspect
+public class ReportingTaskAuditor extends NiFiAuditor {
+
+    private static final Logger logger = LoggerFactory.getLogger(ReportingTaskAuditor.class);
+
+    private static final String NAME = "Name";
+    private static final String ANNOTATION_DATA = "Annotation Data";
+
+    /**
+     * Audits the creation of reporting task via createReportingTask().
+     *
+     * This method only needs to be run 'after returning'. However, in Java 7
+     * the order in which these methods are returned from
+     * Class.getDeclaredMethods (even though there is no order guaranteed) seems
+     * to differ from Java 6. SpringAOP depends on this ordering to determine
+     * advice precedence. By normalizing all advice into Around advice we can
+     * alleviate this issue.
+     *
+     * @param proceedingJoinPoint
+     * @return
+     * @throws java.lang.Throwable
+     */
+    @Around("within(org.apache.nifi.web.dao.ReportingTaskDAO+) && "
+            + "execution(org.apache.nifi.controller.ReportingTaskNode createReportingTask(org.apache.nifi.web.api.dto.ReportingTaskDTO))")
+    public ReportingTaskNode createReportingTaskAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
+        // update the reporting task state
+        ReportingTaskNode reportingTask = (ReportingTaskNode) proceedingJoinPoint.proceed();
+
+        // if no exceptions were thrown, add the reporting task action...
+        final Action action = generateAuditRecord(reportingTask, Operation.Add);
+
+        // save the actions
+        if (action != null) {
+            saveAction(action, logger);
+        }
+
+        return reportingTask;
+    }
+
+    /**
+     * Audits the configuration of a reporting task.
+     *
+     * @param proceedingJoinPoint
+     * @param reportingTaskDTO
+     * @param reportingTaskDAO
+     * @return
+     * @throws Throwable
+     */
+    @Around("within(org.apache.nifi.web.dao.ReportingTaskDAO+) && "
+            + "execution(org.apache.nifi.controller.ReportingTaskNode updateReportingTask(org.apache.nifi.web.api.dto.ReportingTaskDTO)) && "
+            + "args(reportingTaskDTO) && "
+            + "target(reportingTaskDAO)")
+    public Object updateReportingTaskAdvice(ProceedingJoinPoint proceedingJoinPoint, ReportingTaskDTO reportingTaskDTO, ReportingTaskDAO reportingTaskDAO) throws Throwable {
+        // determine the initial values for each property/setting thats changing
+        ReportingTaskNode reportingTask = reportingTaskDAO.getReportingTask(reportingTaskDTO.getId());
+        final Map<String, String> values = extractConfiguredPropertyValues(reportingTask, reportingTaskDTO);
+        final ScheduledState scheduledState = reportingTask.getScheduledState();
+
+        // update the reporting task state
+        final ReportingTaskNode updatedReportingTask = (ReportingTaskNode) proceedingJoinPoint.proceed();
+
+        // if no exceptions were thrown, add the reporting task action...
+        reportingTask = reportingTaskDAO.getReportingTask(updatedReportingTask.getIdentifier());
+
+        // get the current user
+        final NiFiUser user = NiFiUserUtils.getNiFiUser();
+
+        // ensure the user was found
+        if (user != null) {
+            // determine the updated values
+            Map<String, String> updatedValues = extractConfiguredPropertyValues(reportingTask, reportingTaskDTO);
+
+            // create the reporting task details
+            ExtensionDetails taskDetails = new ExtensionDetails();
+            taskDetails.setType(reportingTask.getReportingTask().getClass().getSimpleName());
+
+            // create a reporting task action
+            Date actionTimestamp = new Date();
+            Collection<Action> actions = new ArrayList<>();
+
+            // go through each updated value
+            for (String property : updatedValues.keySet()) {
+                String newValue = updatedValues.get(property);
+                String oldValue = values.get(property);
+                Operation operation = null;
+
+                // determine the type of operation
+                if (oldValue == null || newValue == null || !newValue.equals(oldValue)) {
+                    operation = Operation.Configure;
+                }
+
+                // create a configuration action accordingly
+                if (operation != null) {
+                    // clear the value if this property is sensitive
+                    final PropertyDescriptor propertyDescriptor = reportingTask.getReportingTask().getPropertyDescriptor(property);
+                    if (propertyDescriptor != null && propertyDescriptor.isSensitive()) {
+                        if (newValue != null) {
+                            newValue = "********";
+                        }
+                        if (oldValue != null) {
+                            oldValue = "********";
+                        }
+                    } else if (ANNOTATION_DATA.equals(property)) {
+                        if (newValue != null) {
+                            newValue = "<annotation data not shown>";
+                        }
+                        if (oldValue != null) {
+                            oldValue = "<annotation data not shown>";
+                        }
+                    }
+
+                    final ConfigureDetails actionDetails = new ConfigureDetails();
+                    actionDetails.setName(property);
+                    actionDetails.setValue(newValue);
+                    actionDetails.setPreviousValue(oldValue);
+
+                    // create a configuration action
+                    Action configurationAction = new Action();
+                    configurationAction.setUserDn(user.getDn());
+                    configurationAction.setUserName(user.getUserName());
+                    configurationAction.setOperation(operation);
+                    configurationAction.setTimestamp(actionTimestamp);
+                    configurationAction.setSourceId(reportingTask.getIdentifier());
+                    configurationAction.setSourceName(reportingTask.getName());
+                    configurationAction.setSourceType(Component.ReportingTask);
+                    configurationAction.setComponentDetails(taskDetails);
+                    configurationAction.setActionDetails(actionDetails);
+                    actions.add(configurationAction);
+                }
+            }
+
+            // determine the new executing state
+            final ScheduledState updatedScheduledState = reportingTask.getScheduledState();
+
+            // determine if the running state has changed and its not disabled
+            if (scheduledState != updatedScheduledState) {
+                // create a reporting task action
+                Action taskAction = new Action();
+                taskAction.setUserDn(user.getDn());
+                taskAction.setUserName(user.getUserName());
+                taskAction.setTimestamp(new Date());
+                taskAction.setSourceId(reportingTask.getIdentifier());
+                taskAction.setSourceName(reportingTask.getName());
+                taskAction.setSourceType(Component.ReportingTask);
+                taskAction.setComponentDetails(taskDetails);
+
+                // set the operation accordingly
+                if (ScheduledState.RUNNING.equals(updatedScheduledState)) {
+                    taskAction.setOperation(Operation.Start);
+                } else if (ScheduledState.DISABLED.equals(updatedScheduledState)) {
+                    taskAction.setOperation(Operation.Disable);
+                } else {
+                    // state is now stopped... consider the previous state
+                    if (ScheduledState.RUNNING.equals(scheduledState)) {
+                        taskAction.setOperation(Operation.Stop);
+                    } else if (ScheduledState.DISABLED.equals(scheduledState)) {
+                        taskAction.setOperation(Operation.Enable);
+                    }
+                }
+                actions.add(taskAction);
+            }
+
+            // ensure there are actions to record
+            if (!actions.isEmpty()) {
+                // save the actions
+                saveActions(actions, logger);
+            }
+        }
+
+        return updatedReportingTask;
+    }
+
+    /**
+     * Audits the removal of a reporting task via deleteReportingTask().
+     *
+     * @param proceedingJoinPoint
+     * @param reportingTaskId
+     * @param reportingTaskDAO
+     * @throws Throwable
+     */
+    @Around("within(org.apache.nifi.web.dao.ReportingTaskDAO+) && "
+            + "execution(void deleteReportingTask(java.lang.String)) && "
+            + "args(reportingTaskId) && "
+            + "target(reportingTaskDAO)")
+    public void removeReportingTaskAdvice(ProceedingJoinPoint proceedingJoinPoint, String reportingTaskId, ReportingTaskDAO reportingTaskDAO) throws Throwable {
+        // get the reporting task before removing it
+        ReportingTaskNode reportingTask = reportingTaskDAO.getReportingTask(reportingTaskId);
+
+        // remove the reporting task
+        proceedingJoinPoint.proceed();
+
+        // if no exceptions were thrown, add removal actions...
+        // audit the reporting task removal
+        final Action action = generateAuditRecord(reportingTask, Operation.Remove);
+
+        // save the actions
+        if (action != null) {
+            saveAction(action, logger);
+        }
+    }
+
+    /**
+     * Generates an audit record for the creation of a reporting task.
+     *
+     * @param reportingTask
+     * @param operation
+     * @return
+     */
+    public Action generateAuditRecord(ReportingTaskNode reportingTask, Operation operation) {
+        return generateAuditRecord(reportingTask, operation, null);
+    }
+
+    /**
+     * Generates an audit record for the creation of a reporting task.
+     *
+     * @param reportingTask
+     * @param operation
+     * @param actionDetails
+     * @return
+     */
+    public Action generateAuditRecord(ReportingTaskNode reportingTask, Operation operation, ActionDetails actionDetails) {
+        Action action = null;
+
+        // get the current user
+        NiFiUser user = NiFiUserUtils.getNiFiUser();
+
+        // ensure the user was found
+        if (user != null) {
+            // create the reporting task details
+            ExtensionDetails taskDetails = new ExtensionDetails();
+            taskDetails.setType(reportingTask.getReportingTask().getClass().getSimpleName());
+
+            // create the reporting task action for adding this reporting task
+            action = new Action();
+            action.setUserDn(user.getDn());
+            action.setUserName(user.getUserName());
+            action.setOperation(operation);
+            action.setTimestamp(new Date());
+            action.setSourceId(reportingTask.getIdentifier());
+            action.setSourceName(reportingTask.getName());
+            action.setSourceType(Component.ReportingTask);
+            action.setComponentDetails(taskDetails);
+
+            if (actionDetails != null) {
+                action.setActionDetails(actionDetails);
+            }
+        }
+
+        return action;
+    }
+
+    /**
+     * Extracts the values for the configured properties from the specified ReportingTask.
+     *
+     * @param reportingTask
+     * @param reportingTaskDTO
+     * @return
+     */
+    private Map<String, String> extractConfiguredPropertyValues(ReportingTaskNode reportingTask, ReportingTaskDTO reportingTaskDTO) {
+        Map<String, String> values = new HashMap<>();
+
+        if (reportingTaskDTO.getName() != null) {
+            values.put(NAME, reportingTask.getName());
+        }
+        if (reportingTaskDTO.getAnnotationData() != null) {
+            values.put(ANNOTATION_DATA, reportingTask.getAnnotationData());
+        }
+        if (reportingTaskDTO.getProperties() != null) {
+            // for each property specified, extract its configured value
+            Map<String, String> properties = reportingTaskDTO.getProperties();
+            Map<PropertyDescriptor, String> configuredProperties = reportingTask.getProperties();
+            for (String propertyName : properties.keySet()) {
+                // build a descriptor for getting the configured value
+                PropertyDescriptor propertyDescriptor = new PropertyDescriptor.Builder().name(propertyName).build();
+                String configuredPropertyValue = configuredProperties.get(propertyDescriptor);
+
+                // if the configured value couldn't be found, use the default value from the actual descriptor
+                if (configuredPropertyValue == null) {
+                    propertyDescriptor = locatePropertyDescriptor(configuredProperties.keySet(), propertyDescriptor);
+                    configuredPropertyValue = propertyDescriptor.getDefaultValue();
+                }
+                values.put(propertyName, configuredPropertyValue);
+            }
+        }
+
+        return values;
+    }
+
+    /**
+     * Locates the actual property descriptor for the given spec property
+     * descriptor.
+     *
+     * @param propertyDescriptors
+     * @param specDescriptor
+     * @return
+     */
+    private PropertyDescriptor locatePropertyDescriptor(Set<PropertyDescriptor> propertyDescriptors, PropertyDescriptor specDescriptor) {
+        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
+            if (propertyDescriptor.equals(specDescriptor)) {
+                return propertyDescriptor;
+            }
+        }
+        return specDescriptor;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java
index 9c075e5..1f8942b 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java
@@ -25,7 +25,7 @@ import java.util.Set;
 import org.apache.nifi.action.Action;
 import org.apache.nifi.action.Component;
 import org.apache.nifi.action.Operation;
-import org.apache.nifi.action.component.details.ProcessorDetails;
+import org.apache.nifi.action.component.details.ExtensionDetails;
 import org.apache.nifi.action.component.details.RemoteProcessGroupDetails;
 import org.apache.nifi.action.details.ConnectDetails;
 import org.apache.nifi.connectable.ConnectableType;
@@ -94,7 +94,7 @@ public class SnippetAuditor extends NiFiAuditor {
      */
     @Around("within(org.apache.nifi.web.dao.SnippetDAO+) && "
             + "execution(org.apache.nifi.web.api.dto.FlowSnippetDTO copySnippet(java.lang.String, java.lang.String, java.lang.Double, java.lang.Double))")
-    public Object copySnippetAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
+    public FlowSnippetDTO copySnippetAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
         // perform the underlying operation
         FlowSnippetDTO snippet = (FlowSnippetDTO) proceedingJoinPoint.proceed();
         auditSnippet(snippet);
@@ -110,7 +110,7 @@ public class SnippetAuditor extends NiFiAuditor {
      */
     @Around("within(org.apache.nifi.web.dao.TemplateDAO+) && "
             + "execution(org.apache.nifi.web.api.dto.FlowSnippetDTO instantiateTemplate(java.lang.String, java.lang.Double, java.lang.Double, java.lang.String))")
-    public Object instantiateTemplateAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
+    public FlowSnippetDTO instantiateTemplateAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
         // perform the underlying operation
         FlowSnippetDTO snippet = (FlowSnippetDTO) proceedingJoinPoint.proceed();
         auditSnippet(snippet);
@@ -153,7 +153,7 @@ public class SnippetAuditor extends NiFiAuditor {
 
         // processors
         for (final ProcessorDTO processor : snippet.getProcessors()) {
-            final ProcessorDetails processorDetails = new ProcessorDetails();
+            final ExtensionDetails processorDetails = new ExtensionDetails();
             processorDetails.setType(StringUtils.substringAfterLast(processor.getType(), "."));
 
             final Action action = generateAuditRecord(processor.getId(), processor.getName(), Component.Processor, Operation.Add, timestamp);
@@ -256,15 +256,16 @@ public class SnippetAuditor extends NiFiAuditor {
      * Audits a bulk move.
      *
      * @param proceedingJoinPoint
-     * @param snippetId
+     * @param snippetDTO
      * @param snippetDAO
+     * @return 
      * @throws Throwable
      */
     @Around("within(org.apache.nifi.web.dao.SnippetDAO+) && "
             + "execution(org.apache.nifi.controller.Snippet updateSnippet(org.apache.nifi.web.api.dto.SnippetDTO)) && "
             + "args(snippetDTO) && "
             + "target(snippetDAO)")
-    public Object updateSnippetAdvice(ProceedingJoinPoint proceedingJoinPoint, SnippetDTO snippetDTO, SnippetDAO snippetDAO) throws Throwable {
+    public Snippet updateSnippetAdvice(ProceedingJoinPoint proceedingJoinPoint, SnippetDTO snippetDTO, SnippetDAO snippetDAO) throws Throwable {
         // get the snippet before removing it
         Snippet snippet = snippetDAO.getSnippet(snippetDTO.getId());
         final String previousGroupId = snippet.getParentGroupId();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
index 63d302c..e3afbf9 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
@@ -19,14 +19,17 @@ package org.apache.nifi.web;
 import java.util.Collection;
 import java.util.Date;
 import java.util.Set;
+import org.apache.nifi.controller.ScheduledState;
 
 import org.apache.nifi.controller.repository.claim.ContentDirection;
+import org.apache.nifi.controller.service.ControllerServiceState;
 import org.apache.nifi.web.api.dto.BulletinBoardDTO;
 import org.apache.nifi.web.api.dto.BulletinQueryDTO;
 import org.apache.nifi.web.api.dto.ClusterDTO;
 import org.apache.nifi.web.api.dto.ConnectionDTO;
 import org.apache.nifi.web.api.dto.ControllerConfigurationDTO;
 import org.apache.nifi.web.api.dto.ControllerDTO;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
 import org.apache.nifi.web.api.dto.CounterDTO;
 import org.apache.nifi.web.api.dto.CountersDTO;
 import org.apache.nifi.web.api.dto.DocumentedTypeDTO;
@@ -38,9 +41,12 @@ import org.apache.nifi.web.api.dto.NodeSystemDiagnosticsDTO;
 import org.apache.nifi.web.api.dto.PortDTO;
 import org.apache.nifi.web.api.dto.ProcessGroupDTO;
 import org.apache.nifi.web.api.dto.ProcessorDTO;
-import org.apache.nifi.web.api.dto.ProcessorHistoryDTO;
+import org.apache.nifi.web.api.dto.ComponentHistoryDTO;
+import org.apache.nifi.web.api.dto.ControllerServiceReferencingComponentDTO;
+import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupPortDTO;
+import org.apache.nifi.web.api.dto.ReportingTaskDTO;
 import org.apache.nifi.web.api.dto.RevisionDTO;
 import org.apache.nifi.web.api.dto.SnippetDTO;
 import org.apache.nifi.web.api.dto.SystemDiagnosticsDTO;
@@ -242,7 +248,21 @@ public interface NiFiServiceFacade {
      * @return The list of available processor types
      */
     Set<DocumentedTypeDTO> getProcessorTypes();
-
+    
+    /**
+     * Returns the list of controller service types.
+     * 
+     * @return The list of available controller types
+     */
+    Set<DocumentedTypeDTO> getControllerServiceTypes();
+    
+    /**
+     * Returns the list of reporting task types.
+     * 
+     * @return The list of available reporting task types
+     */
+    Set<DocumentedTypeDTO> getReportingTaskTypes();
+    
     /**
      * Returns the list of prioritizer types.
      *
@@ -360,6 +380,16 @@ public interface NiFiServiceFacade {
     StatusHistoryDTO getProcessorStatusHistory(String groupId, String id);
 
     /**
+     * Get the descriptor for the specified property of the specified processor.
+     * 
+     * @param groupId
+     * @param id
+     * @param property
+     * @return 
+     */
+    PropertyDescriptorDTO getProcessorPropertyDescriptor(String groupId, String id, String property);
+    
+    /**
      * Gets all the Processor transfer objects for this controller.
      *
      * @param groupId
@@ -924,6 +954,172 @@ public interface NiFiServiceFacade {
     ConfigurationSnapshot<Void> deleteLabel(Revision revision, String groupId, String labelId);
 
     // ----------------------------------------
+    // Controller Services methods
+    // ----------------------------------------
+    
+    /**
+     * Creates a controller service.
+     *
+     * @param revision Revision to compare with current base revision
+     * @param controllerServiceDTO The controller service DTO
+     * @return The controller service DTO
+     */
+    ConfigurationSnapshot<ControllerServiceDTO> createControllerService(Revision revision, ControllerServiceDTO controllerServiceDTO);
+    
+    /**
+     * Gets all controller services.
+     * 
+     * @return 
+     */
+    Set<ControllerServiceDTO> getControllerServices();
+    
+    /**
+     * Gets the specified controller service.
+     * 
+     * @param controllerServiceId
+     * @return 
+     */
+    ControllerServiceDTO getControllerService(String controllerServiceId);
+    
+    /**
+     * Get the descriptor for the specified property of the specified controller service.
+     * 
+     * @param id
+     * @param property
+     * @return 
+     */
+    PropertyDescriptorDTO getControllerServicePropertyDescriptor(String id, String property);
+    
+    /**
+     * Gets the references for specified controller service.
+     * 
+     * @param controllerServiceId
+     * @return 
+     */
+    Set<ControllerServiceReferencingComponentDTO> getControllerServiceReferencingComponents(String controllerServiceId);
+    
+    /**
+     * Updates the referencing components for the specified controller service.
+     * 
+     * @param revision
+     * @param controllerServiceId
+     * @param scheduledState
+     * @param controllerServiceState the value of state 
+     * @return The referencing component dtos
+     */
+    ConfigurationSnapshot<Set<ControllerServiceReferencingComponentDTO>> updateControllerServiceReferencingComponents(Revision revision, String controllerServiceId, ScheduledState scheduledState, ControllerServiceState controllerServiceState);
+    
+    /**
+     * Updates the specified label.
+     *
+     * @param revision Revision to compare with current base revision
+     * @param controllerServiceDTO The controller service DTO
+     * @return The controller service DTO
+     */
+    ConfigurationSnapshot<ControllerServiceDTO> updateControllerService(Revision revision, ControllerServiceDTO controllerServiceDTO);
+
+    /**
+     * Deletes the specified label.
+     *
+     * @param revision Revision to compare with current base revision
+     * @param controllerServiceId The controller service id
+     * @return 
+     */
+    ConfigurationSnapshot<Void> deleteControllerService(Revision revision, String controllerServiceId);
+    
+    /**
+     * Verifies the specified controller service can be updated.
+     *
+     * @param controllerServiceDTO
+     */
+    void verifyUpdateControllerService(ControllerServiceDTO controllerServiceDTO);
+    
+    /**
+     * Verifies the referencing components of the specified controller service can be updated.
+     * 
+     * @param controllerServiceId
+     * @param scheduledState
+     * @param controllerServiceState 
+     */
+    void verifyUpdateControllerServiceReferencingComponents(String controllerServiceId, ScheduledState scheduledState, ControllerServiceState controllerServiceState);
+    
+    /**
+     * Verifies the specified controller service can be removed.
+     *
+     * @param controllerServiceId
+     */
+    void verifyDeleteControllerService(String controllerServiceId);
+    
+    // ----------------------------------------
+    // Reporting Task methods
+    // ----------------------------------------
+    
+    /**
+     * Creates a reporting task.
+     *
+     * @param revision Revision to compare with current base revision
+     * @param reportingTaskDTO The reporting task DTO
+     * @return The reporting task DTO
+     */
+    ConfigurationSnapshot<ReportingTaskDTO> createReportingTask(Revision revision, ReportingTaskDTO reportingTaskDTO);
+    
+    /**
+     * Gets all reporting tasks.
+     * 
+     * @return 
+     */
+    Set<ReportingTaskDTO> getReportingTasks();
+    
+    /**
+     * Gets the specified reporting task.
+     * 
+     * @param reportingTaskId
+     * @return 
+     */
+    ReportingTaskDTO getReportingTask(String reportingTaskId);
+    
+    /**
+     * Get the descriptor for the specified property of the specified reporting task.
+     * 
+     * @param id
+     * @param property
+     * @return 
+     */
+    PropertyDescriptorDTO getReportingTaskPropertyDescriptor(String id, String property);
+    
+    /**
+     * Updates the specified reporting task.
+     *
+     * @param revision Revision to compare with current base revision
+     * @param reportingTaskDTO The reporting task DTO
+     * @return The reporting task DTO
+     */
+    ConfigurationSnapshot<ReportingTaskDTO> updateReportingTask(Revision revision, ReportingTaskDTO reportingTaskDTO);
+
+    /**
+     * Deletes the specified reporting task.
+     *
+     * @param revision Revision to compare with current base revision
+     * @param reportingTaskId The reporting task id
+     * @return 
+     */
+    ConfigurationSnapshot<Void> deleteReportingTask(Revision revision, String reportingTaskId);
+    
+    /**
+     * Verifies the specified reporting task can be updated.
+     *
+     * @param reportingTaskDTO
+     */
+    void verifyUpdateReportingTask(ReportingTaskDTO reportingTaskDTO);
+
+    /**
+     * Verifies the specified reporting task can be removed.
+     *
+     * @param reportingTaskId
+     */
+    void verifyDeleteReportingTask(String reportingTaskId);
+    
+    // ----------------------------------------
     // History methods
     // ----------------------------------------
     /**
@@ -950,12 +1146,12 @@ public interface NiFiServiceFacade {
     void deleteActions(Date endDate);
 
     /**
-     * Gets the history for the specified property for the specified processor.
+     * Gets the history for the specified property for the specified component.
      *
-     * @param processorId
+     * @param componentId
      * @return
      */
-    ProcessorHistoryDTO getProcessorHistory(String processorId);
+    ComponentHistoryDTO getComponentHistory(String componentId);
 
     // ----------------------------------------
     // Snippet methods


[45/62] [abbrv] incubator-nifi git commit: NIFI-475: - Adding support to create controller services inline when editing a components properties.

Posted by ma...@apache.org.
NIFI-475:
- Adding support to create controller services inline when editing a components properties.

NIFI-475:
- Adding support to create controller services inline when editing a components properties.

NIFI-475:
- Prompting the user to save changes before navigating to the controller service definition.

NIFI-475:
- Adding support to create controller services inline when editing a components properties.

NIFI-475:
- Adding support to create controller services inline when editing a components properties.

NIFI-475:
- Prompting the user to save changes before navigating to the controller service definition.

NIFI-475:
- Prompting the user to save changes before navigating to the controller service definition.

NIFI-475:
- Prompting the user to save changes before navigating to the controller service definition.

NIFI-475:
- Only providing the option to go to the service if the dialog is originating from the canvas (not for instance the summary page).

NIFI-475:
- Removing the children from the documented types. This prevents conveying the documented type hierarchies but that isn't necessary given the support for creating instances inline when editing properties.

NIFI-475:
- Removing the type hierarchies from the new controller service dialog.

NIFI-475:
- Including the controller service description in the inline controller service dialog.


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/2154b822
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/2154b822
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/2154b822

Branch: refs/heads/NIFI-25
Commit: 2154b822bf32b661c80bb41d96d569ee3491a41b
Parents: e456ea3
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Apr 6 16:29:07 2015 -0400
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Apr 8 12:25:48 2015 -0400

----------------------------------------------------------------------
 .../nifi/web/api/dto/DocumentedTypeDTO.java     |  14 -
 .../nifi/web/api/dto/PropertyDescriptorDTO.java |   9 +-
 .../org/apache/nifi/web/NiFiServiceFacade.java  |   3 +-
 .../nifi/web/StandardNiFiServiceFacade.java     |   4 +-
 .../apache/nifi/web/api/ControllerResource.java |   7 +-
 .../org/apache/nifi/web/api/dto/DtoFactory.java | 135 +--------
 .../nifi/web/controller/ControllerFacade.java   |  43 ++-
 .../org/apache/nifi/web/util/SnippetUtils.java  |   6 +-
 .../src/main/webapp/css/settings.css            |  15 -
 .../propertytable/jquery.propertytable.css      |  43 +++
 .../propertytable/jquery.propertytable.js       | 289 +++++++++++++++++--
 .../js/nf/canvas/nf-controller-service.js       | 186 ++++++------
 .../js/nf/canvas/nf-processor-configuration.js  | 136 +++++----
 .../webapp/js/nf/canvas/nf-reporting-task.js    | 135 +++++----
 .../src/main/webapp/js/nf/canvas/nf-settings.js | 227 ++-------------
 15 files changed, 657 insertions(+), 595 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2154b822/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DocumentedTypeDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DocumentedTypeDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DocumentedTypeDTO.java
index 7cf1b84..6e4aeb9 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DocumentedTypeDTO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DocumentedTypeDTO.java
@@ -26,7 +26,6 @@ import javax.xml.bind.annotation.XmlType;
 public class DocumentedTypeDTO {
 
     private String type;
-    private Set<DocumentedTypeDTO> childTypes;
     private String description;
     private Set<String> tags;
 
@@ -69,17 +68,4 @@ public class DocumentedTypeDTO {
         this.tags = tags;
     }
 
-    /**
-     * Child types for this type.
-     * 
-     * @return 
-     */
-    public Set<DocumentedTypeDTO> getChildTypes() {
-        return childTypes;
-    }
-
-    public void setChildTypes(Set<DocumentedTypeDTO> childTypes) {
-        this.childTypes = childTypes;
-    }
-    
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2154b822/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/PropertyDescriptorDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/PropertyDescriptorDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/PropertyDescriptorDTO.java
index d10a324..ecde255 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/PropertyDescriptorDTO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/PropertyDescriptorDTO.java
@@ -34,7 +34,7 @@ public class PropertyDescriptorDTO {
     private boolean sensitive;
     private boolean dynamic;
     private boolean supportsEl;
-    private boolean identifiesControllerService;
+    private String identifiesControllerService;
 
     /**
      * The set of allowable values for this property. If empty then the
@@ -158,15 +158,16 @@ public class PropertyDescriptorDTO {
     }
 
     /**
-     * Whether this descriptor represents a controller service.
+     * If this property identifies a controller service, this returns the 
+     * fully qualified type, null otherwise.
      * 
      * @return 
      */
-    public boolean isIdentifiesControllerService() {
+    public String getIdentifiesControllerService() {
         return identifiesControllerService;
     }
 
-    public void setIdentifiesControllerService(boolean identifiesControllerService) {
+    public void setIdentifiesControllerService(String identifiesControllerService) {
         this.identifiesControllerService = identifiesControllerService;
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2154b822/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
index e3afbf9..8d9dade 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
@@ -252,9 +252,10 @@ public interface NiFiServiceFacade {
     /**
      * Returns the list of controller service types.
      * 
+     * @param serviceType Filters only service types that implement this type
      * @return The list of available controller types
      */
-    Set<DocumentedTypeDTO> getControllerServiceTypes();
+    Set<DocumentedTypeDTO> getControllerServiceTypes(String serviceType);
     
     /**
      * Returns the list of reporting task types.

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2154b822/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
index 086c46b..88637b4 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
@@ -1718,8 +1718,8 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     }
 
     @Override
-    public Set<DocumentedTypeDTO> getControllerServiceTypes() {
-        return controllerFacade.getControllerServiceTypes();
+    public Set<DocumentedTypeDTO> getControllerServiceTypes(final String serviceType) {
+        return controllerFacade.getControllerServiceTypes(serviceType);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2154b822/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
index 98f17d5..c0b4cd7 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
@@ -737,6 +737,7 @@ public class ControllerResource extends ApplicationResource {
      * @param clientId Optional client id. If the client id is not specified, a
      * new one will be generated. This value (whether specified or generated) is
      * included in the response.
+     * @param serviceType Returns only services that implement this type
      * @return A controllerServicesTypesEntity.
      */
     @GET
@@ -744,7 +745,9 @@ public class ControllerResource extends ApplicationResource {
     @Path("/controller-service-types")
     @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
     @TypeHint(ControllerServiceTypesEntity.class)
-    public Response getControllerServiceTypes(@QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) {
+    public Response getControllerServiceTypes(
+            @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+            @QueryParam("serviceType") String serviceType) {
 
         // replicate if cluster manager
         if (properties.isClusterManager()) {
@@ -758,7 +761,7 @@ public class ControllerResource extends ApplicationResource {
         // create response entity
         final ControllerServiceTypesEntity entity = new ControllerServiceTypesEntity();
         entity.setRevision(revision);
-        entity.setControllerServiceTypes(serviceFacade.getControllerServiceTypes());
+        entity.setControllerServiceTypes(serviceFacade.getControllerServiceTypes(serviceType));
 
         // generate the response
         return clusterContext(generateOkResponse(entity)).build();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2154b822/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
index 7fe76ad..4e83ac5 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
@@ -1384,135 +1384,6 @@ public final class DtoFactory {
     }
     
     /**
-     * Identifies all baseTypes for the specified type that are assignable to the specified baseType.
-     * 
-     * @param baseType
-     * @param type
-     * @param baseTypes 
-     */
-    private void identifyBaseTypes(final Class baseType, final Class type, final Set<Class> baseTypes, final  boolean recurse) {
-        final Class[] interfaces = type.getInterfaces();
-        for (final Class i : interfaces) {
-            if (baseType.isAssignableFrom(i) && !baseType.equals(i)) {
-                baseTypes.add(i);
-            }
-        }
-        
-        if (recurse) {
-            if (type.getSuperclass() != null) {
-                identifyBaseTypes(baseType, type.getSuperclass(), baseTypes, recurse);
-            }
-        }
-    }
-    
-    /**
-     * Gets the DocumentedTypeDTOs from the specified classes for the specified baseClass.
-     *
-     * @param baseClass
-     * @param classes
-     * @return
-     */
-    public Set<DocumentedTypeDTO> fromDocumentedTypes(final Class baseClass, final Set<Class> classes) {
-        final Set<DocumentedTypeDTO> types = new LinkedHashSet<>();
-        final Set<Class> sortedClasses = new TreeSet<>(CLASS_NAME_COMPARATOR);
-        sortedClasses.addAll(classes);
-        
-        // identify all interfaces that extend baseClass for all classes
-        final Set<Class> interfaces = new HashSet<>();
-        for (final Class<?> cls : sortedClasses) {
-            identifyBaseTypes(baseClass, cls, interfaces, true);
-        }
-        
-        // build a lookup of all interfaces
-        final Map<Class, DocumentedTypeDTO> lookup = new HashMap<>();
-        
-        // convert the interfaces to DTO form
-        for (final Class<?> i : interfaces) {
-            final DocumentedTypeDTO type = new DocumentedTypeDTO();
-            type.setType(i.getName());
-            type.setDescription(getCapabilityDescription(i));
-            type.setTags(getTags(i));
-            type.setChildTypes(new LinkedHashSet<DocumentedTypeDTO>());
-            lookup.put(i, type);
-        }
-        
-        // move the interfaces into the appropriate hierarchy
-        final Collection<Class> rootTypes = new ArrayList<>();
-        for (final Class<?> i : interfaces) {
-            rootTypes.add(i);
-            
-            // identify the base types
-            final Set<Class> baseTypes = new LinkedHashSet<>();
-            identifyBaseTypes(baseClass, i, baseTypes, false);
-            
-            // move this interfaces into the hierarchy where appropriate
-            if (!baseTypes.isEmpty()) {
-                // get the DTO for each base type
-                for (final Class baseType : baseTypes) {
-                    final DocumentedTypeDTO parentInteface = lookup.get(baseType);
-                    final DocumentedTypeDTO childInterface = lookup.get(i);
-                    
-                    // include all parent tags in the respective children
-                    childInterface.getTags().addAll(parentInteface.getTags());
-                    
-                    // update the hierarchy
-                    parentInteface.getChildTypes().add(childInterface);
-                }
-                
-                // remove this interface from the lookup (this will only
-                // leave the interfaces that are ancestor roots)
-                rootTypes.remove(i);
-            }
-        }
-
-        // include the interfaces
-        sortedClasses.addAll(rootTypes);
-        
-        // get the DTO form for all interfaces and classes
-        for (final Class<?> cls : sortedClasses) {
-            boolean add = false;
-            
-            final DocumentedTypeDTO type;
-            if (rootTypes.contains(cls)) {
-                type = lookup.get(cls);
-                add = true;
-            } else {
-                type = new DocumentedTypeDTO();
-                type.setType(cls.getName());
-                type.setDescription(getCapabilityDescription(cls));
-                type.setTags(getTags(cls));
-            }
-            
-            // identify the base types
-            final Set<Class> baseTypes = new LinkedHashSet<>();
-            identifyBaseTypes(baseClass, cls, baseTypes, false);
-            
-            // include this type if it doesn't belong to another hierarchy
-            if (baseTypes.isEmpty()) {
-                add = true;
-            } else {
-                // get the DTO for each base type
-                for (final Class baseType : baseTypes) {
-                    final DocumentedTypeDTO parentInterface = lookup.get(baseType);
-
-                    // include all parent tags in the respective children
-                    type.getTags().addAll(parentInterface.getTags());
-
-                    // update the hierarchy
-                    parentInterface.getChildTypes().add(type);
-                }
-            }
-            
-            // add if appropriate
-            if (add) {
-                types.add(type);
-            }
-        }
-
-        return types;
-    }
-
-    /**
      * Creates a ProcessorDTO from the specified ProcessorNode.
      *
      * @param node
@@ -1958,7 +1829,11 @@ public final class DtoFactory {
         dto.setDescription(propertyDescriptor.getDescription());
         dto.setDefaultValue(propertyDescriptor.getDefaultValue());
         dto.setSupportsEl(propertyDescriptor.isExpressionLanguageSupported());
-        dto.setIdentifiesControllerService(propertyDescriptor.getControllerServiceDefinition() != null);
+        
+        // set the identifies controller service is applicable
+        if (propertyDescriptor.getControllerServiceDefinition() != null) {
+            dto.setIdentifiesControllerService(propertyDescriptor.getControllerServiceDefinition().getName());
+        }
 
         final Class<? extends ControllerService> serviceDefinition = propertyDescriptor.getControllerServiceDefinition();
         if (propertyDescriptor.getAllowableValues() == null) {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2154b822/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
index a373f05..b5e6f7e 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
@@ -108,6 +108,7 @@ import org.apache.nifi.web.api.dto.status.ProcessGroupStatusDTO;
 import org.apache.nifi.web.api.dto.status.StatusHistoryDTO;
 import org.apache.nifi.web.DownloadableContent;
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.ClassUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.admin.service.UserService;
 import org.apache.nifi.authorization.DownloadAuthorization;
@@ -348,12 +349,48 @@ public class ControllerFacade {
     }
     
     /**
+     * Returns whether the specified type implements the specified serviceType.
+     * 
+     * @param baseType
+     * @param type
+     * @return
+     */
+    private boolean implementsServiceType(final String serviceType, final Class type) {
+        final List<Class<?>> interfaces = ClassUtils.getAllInterfaces(type);
+        for (final Class i : interfaces) {
+            if (ControllerService.class.isAssignableFrom(i) && i.getName().equals(serviceType)) {
+                return true;
+            }
+        }
+        
+        return false;
+    }
+    
+    /**
      * Gets the ControllerService types that this controller supports.
      * 
+     * @param serviceType
      * @return 
      */
-    public Set<DocumentedTypeDTO> getControllerServiceTypes() {
-        return dtoFactory.fromDocumentedTypes(ControllerService.class, ExtensionManager.getExtensions(ControllerService.class));
+    public Set<DocumentedTypeDTO> getControllerServiceTypes(final String serviceType) { 
+        final Set<Class> serviceImplementations = ExtensionManager.getExtensions(ControllerService.class);
+        
+        // identify the controller services that implement the specified serviceType if applicable
+        final Set<Class> matchingServiceImplementions;
+        if (serviceType != null) {
+            matchingServiceImplementions = new HashSet<>();
+            
+            // check each type and remove those that aren't in the specified ancestry
+            for (final Class type : serviceImplementations) {
+                if (implementsServiceType(serviceType, type)) {
+                    matchingServiceImplementions.add(type);
+                }
+            }
+        } else {
+            matchingServiceImplementions = serviceImplementations;
+        }
+        
+        return dtoFactory.fromDocumentedTypes(matchingServiceImplementions);
     }
     
     /**
@@ -362,7 +399,7 @@ public class ControllerFacade {
      * @return 
      */
     public Set<DocumentedTypeDTO> getReportingTaskTypes() {
-        return dtoFactory.fromDocumentedTypes(ReportingTask.class, ExtensionManager.getExtensions(ReportingTask.class));
+        return dtoFactory.fromDocumentedTypes(ExtensionManager.getExtensions(ReportingTask.class));
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2154b822/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/SnippetUtils.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/SnippetUtils.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/SnippetUtils.java
index fa9bc41..40e5730 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/SnippetUtils.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/SnippetUtils.java
@@ -222,7 +222,7 @@ public final class SnippetUtils {
                 }
                 
                 final PropertyDescriptorDTO propertyDescriptorDto = descriptors.get(propName);
-                if ( propertyDescriptorDto != null && propertyDescriptorDto.isIdentifiesControllerService() ) {
+                if ( propertyDescriptorDto != null && propertyDescriptorDto.getIdentifiesControllerService() != null ) {
                     final ControllerServiceNode serviceNode = flowController.getControllerServiceNode(propValue);
                     if ( serviceNode != null ) {
                         addControllerServicesToSnippet(snippet, serviceNode);
@@ -363,7 +363,7 @@ public final class SnippetUtils {
                 final Map<String, PropertyDescriptorDTO> descriptors = serviceDTO.getDescriptors();
                 if ( properties != null && descriptors != null ) {
                     for ( final PropertyDescriptorDTO descriptor : descriptors.values() ) {
-                        if ( descriptor.isIdentifiesControllerService() ) {
+                        if ( descriptor.getIdentifiesControllerService() != null ) {
                             final String currentServiceId = properties.get(descriptor.getName());
                             if ( currentServiceId == null ) {
                                 continue;
@@ -558,7 +558,7 @@ public final class SnippetUtils {
         final Map<String, PropertyDescriptorDTO> descriptors = configDto.getDescriptors();
         if ( properties != null && descriptors != null ) {
             for ( final PropertyDescriptorDTO descriptor : descriptors.values() ) {
-                if ( descriptor.isIdentifiesControllerService() ) {
+                if ( descriptor.getIdentifiesControllerService() != null ) {
                     final String currentServiceId = properties.get(descriptor.getName());
                     if ( currentServiceId == null ) {
                         continue;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2154b822/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/settings.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/settings.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/settings.css
index 91805e1..ae93bcc 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/settings.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/settings.css
@@ -43,21 +43,6 @@
     cursor: pointer;
 }
 
-span.expansion-button {
-    width: 10px;
-    height: 10px;
-    float: left;
-}
-
-span.ancestor-type {
-    font-weight: bold;
-}
-
-span.ancestor-type-rollup {
-    margin-left: 3px;
-    color: #aaa;
-}
-
 /* settings tabs */
 
 #settings-tabs-container {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2154b822/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.css
index 74b6f4c..d1f22b2 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.css
@@ -104,6 +104,49 @@ div.new-property-button-container {
 }
 
 /*
+    New inline controller service dialog
+*/
+
+div.new-inline-controller-service-dialog {
+    z-index: 1301;
+    display: none;
+    padding: 10px;
+    border: 3px solid #365C6A;
+    box-shadow: 4px 4px 6px rgba(0, 0, 0, 0.9);
+    cursor: move;
+    width: 350px;
+    height: 250px;
+}
+
+div.new-inline-controller-service-combo {
+    height: 18px;
+    width: 340px;
+    margin-bottom: 10px;
+}
+
+div.new-inline-controller-service-tags {
+    height: 18px;
+    width: 340px;
+    margin-bottom: 10px;
+    overflow: hidden;
+    white-space: nowrap;
+}
+
+div.new-inline-controller-service-description {
+    height: 115px;
+    width: 340px;
+    overflow: auto;
+}
+
+div.new-inline-controller-service-button-container {
+    position: absolute;
+    left: 0;
+    bottom: 0;
+    right: 0;
+    padding: 0 8px 10px;
+}
+
+/*
     Styles for the property editor.
 */
 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2154b822/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
index bbafc60..c0b8884 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
@@ -23,7 +23,17 @@
  *
  * {
  *   readOnly: true,
- *   newPropertyDialogContainer: 'body'
+ *   dialogContainer: 'body',
+ *   descriptorDeferred: function () {
+ *      return $.Deferred(function (deferred) {
+ *          deferred.resolve();
+ *      }).promise;
+ *   },
+ *   goToServiceDeferred: function () {
+ *      return $.Deferred(function (deferred) {
+ *          deferred.resolve();
+ *      }).promise;
+ *   }
  * }
  */
 
@@ -443,6 +453,10 @@
             var gridContainer = $(args.grid.getContainerNode());
             var descriptors = gridContainer.data('descriptors');
             propertyDescriptor = descriptors[args.item.property];
+            
+            // get the options
+            var propertyContainer = gridContainer.closest('.property-container');
+            var configurationOptions = propertyContainer.data('options');
 
             // create the wrapper
             wrapper = $('<div></div>').css({
@@ -490,6 +504,15 @@
                     disabled: true
                 });
             }
+            
+            // if this descriptor identifies a controller service, provide a way to create one
+            if (nf.Common.isDefinedAndNotNull(propertyDescriptor.identifiesControllerService)) {
+                options.push({
+                    text: 'Create new service...',
+                    value: undefined,
+                    optionClass: 'unset'
+                });
+            }
 
             // determine the max height
             var position = args.position;
@@ -499,7 +522,16 @@
             // build the combo field
             combo = $('<div class="value-combo combo"></div>').combo({
                 options: options,
-                maxHeight: maxHeight
+                maxHeight: maxHeight,
+                select: function (option) {
+                    if (typeof option.value === 'undefined') {
+                        // cancel the current edit
+                        scope.cancel();
+                        
+                        // prompt for the new service type
+                        promptForNewControllerService(gridContainer, args.grid, args.item, propertyDescriptor.identifiesControllerService, configurationOptions);
+                    }
+                }
             }).width(position.width - 16).appendTo(wrapper);
 
             // add buttons for handling user input
@@ -744,6 +776,154 @@
             }
         }
     };
+    
+    /**
+     * Gets the available controller services that implement the specified type and
+     * prompts the user to create one.
+     * 
+     * @param {jQuery} gridContainer The grid container
+     * @param {slickgrid} grid The grid
+     * @param {object} item The item
+     * @param {type} serviceType The type of service to create
+     * @param {object} configurationOptions The configuration options
+     */
+    var promptForNewControllerService = function (gridContainer, grid, item, serviceType, configurationOptions) {
+        $.ajax({
+            type: 'GET',
+            url: '../nifi-api/controller/controller-service-types',
+            data: {
+                serviceType: serviceType
+            },
+            dataType: 'json'
+        }).done(function (response) {
+            var options = [];
+            $.each(response.controllerServiceTypes, function (i, controllerServiceType) {
+                options.push({
+                    text: nf.Common.substringAfterLast(controllerServiceType.type, '.'),
+                    value: controllerServiceType.type,
+                    description: nf.Common.escapeHtml(controllerServiceType.description)
+                });
+            });
+            
+            // ensure there are some applicable controller services
+            if (options.length === 0) {
+                nf.Dialog.showOkDialog({
+                    dialogContent: 'No controller service types found that are applicable for this property.',
+                    overlayBackground: false
+                });
+            } else {
+                var newControllerServiceDialogMarkup = 
+                        '<div class="new-inline-controller-service-dialog dialog cancellable">' +
+                            '<div>' +
+                                '<div class="setting-name">Controller Service</div>' +
+                                '<div class="setting-field">' +
+                                    '<div class="new-inline-controller-service-combo"></div>' +
+                                '</div>' +
+                            '</div>' +
+                            '<div>' +
+                                '<div class="setting-name">Tags</div>' +
+                                '<div class="setting-field">' +
+                                    '<div class="new-inline-controller-service-tags"></div>' +
+                                '</div>' +
+                            '</div>' +
+                            '<div>' +
+                                '<div class="setting-name">Description</div>' +
+                                '<div class="setting-field">' +
+                                    '<div class="new-inline-controller-service-description"></div>' +
+                                '</div>' +
+                            '</div>' +
+                            '<div class="new-inline-controller-service-button-container">' +
+                                '<div class="new-inline-controller-service-create button button-normal">Create</div>' +
+                                '<div class="new-inline-controller-service-cancel button button-normal">Cancel</div>' +
+                                '<div class="clear"></div>' +
+                            '</div>' +
+                        '</div>';
+
+                var newControllerServiceDialog = $(newControllerServiceDialogMarkup).appendTo(configurationOptions.dialogContainer);
+                var newControllerServiceCombo = newControllerServiceDialog.find('div.new-inline-controller-service-combo');
+                var newControllerServiceTags = newControllerServiceDialog.find('div.new-inline-controller-service-tags');
+                var newControllerServiceDescription = newControllerServiceDialog.find('div.new-inline-controller-service-description');
+                
+                // build the combo field
+                newControllerServiceCombo.combo({
+                    options: options,
+                    select: function (option) {
+                        var service;
+                        $.each(response.controllerServiceTypes, function (i, controllerServiceType) {
+                            if (controllerServiceType.type === option.value) {
+                                service = controllerServiceType;
+                                return false;
+                            }
+                        });
+                        
+                        // set the service details
+                        newControllerServiceTags.text(service.tags.join(', ')).ellipsis();
+                        newControllerServiceDescription.text(service.description);
+                    }
+                });
+                
+                var create = function () {
+                    var newControllerServiceType = newControllerServiceCombo.combo('getSelectedOption').value;
+
+                    // create service of the specified type
+                    var revision = nf.Client.getRevision();
+
+                    // add the new controller service
+                    $.ajax({
+                        type: 'POST',
+                        url: '../nifi-api/controller/controller-services/node',
+                        data: {
+                            version: revision.version,
+                            clientId: revision.clientId,
+                            type: newControllerServiceType
+                        },
+                        dataType: 'json'
+                    }).done(function (response) {
+                        // update the revision
+                        nf.Client.setRevision(response.revision);
+
+                        $.Deferred(function (deferred) {
+                            // load the property descriptor if possible
+                            if (typeof configurationOptions.descriptorDeferred === 'function') {
+                                configurationOptions.descriptorDeferred(item.property).done(function(response) {
+                                    var descriptor = response.propertyDescriptor;
+
+                                    // store the descriptor for use later
+                                    var descriptors = gridContainer.data('descriptors');
+                                    if (!nf.Common.isUndefined(descriptors)) {
+                                        descriptors[descriptor.name] = descriptor;
+                                    }
+
+                                    deferred.resolve();
+                                });
+                            } else {
+                                deferred.resolve();
+                            }
+                        }).done(function() {
+                            // add a row for the new property
+                            var data = grid.getData();
+                            data.updateItem(item.id, $.extend(item, {
+                                value: response.controllerService.id
+                            }));
+
+                            // close the dialog
+                            newControllerServiceDialog.hide();
+                        });
+                    }).fail(nf.Common.handleAjaxError);
+                };
+
+                var cancel = function () {
+                    newControllerServiceDialog.hide();
+                };
+
+                // make the new property dialog draggable
+                newControllerServiceDialog.draggable({
+                    cancel: 'input, textarea, pre, .button, .' + editorClass,
+                    containment: 'body'
+                }).on('click', 'div.new-inline-controller-service-create', create).on('click', 'div.new-inline-controller-service-cancel', cancel).modal('show');
+            }
+        }).fail(nf.Common.handleAjaxError);
+    };
 
     var initPropertiesTable = function (table, options) {
         // function for formatting the property name
@@ -826,20 +1006,37 @@
             {id: 'value', field: 'value', name: 'Value', sortable: false, resizable: true, cssClass: 'pointer', rerenderOnResize: true, formatter: valueFormatter}
         ];
 
-        if (options.readOnly !== true) {
-            // custom formatter for the actions column
-            var actionFormatter = function (row, cell, value, columnDef, dataContext) {
-                var markup = '';
+        // custom formatter for the actions column
+        var actionFormatter = function (row, cell, value, columnDef, dataContext) {
+            var markup = '';
 
-                // allow user defined properties to be removed
-                if (dataContext.type === 'userDefined') {
-                    markup = '<img src="images/iconDelete.png" title="Delete" class="delete-property pointer" style="margin-top: 2px" />';
-                }
+            // get the property descriptor
+            var descriptors = table.data('descriptors');
+            var propertyDescriptor = descriptors[dataContext.property];
+            
+            var identifiesControllerService = nf.Common.isDefinedAndNotNull(propertyDescriptor.identifiesControllerService);
+            var isConfigured = nf.Common.isDefinedAndNotNull(dataContext.value);
+            var isOnCanvas = nf.Common.isDefinedAndNotNull(nf.Canvas);
+            
+            // check to see if we should provide a button for going to a controller service
+            if (identifiesControllerService && isConfigured && isOnCanvas) {
+                // ensure the configured value is referencing a valid service
+                $.each(propertyDescriptor.allowableValues, function (_, allowableValue) {
+                    if (allowableValue.value === dataContext.value) {
+                        markup = '<img src="images/iconGoTo.png" title="Go To" class="go-to-service pointer" style="margin-top: 2px" />';
+                        return false;
+                    }
+                });
+            }
 
-                return markup;
-            };
-            propertyColumns.push({id: "actions", name: "&nbsp;", minWidth: 20, width: 20, formatter: actionFormatter});
-        }
+            // allow user defined properties to be removed
+            if (options.readOnly !== true && dataContext.type === 'userDefined') {
+                markup = '<img src="images/iconDelete.png" title="Delete" class="delete-property pointer" style="margin-top: 2px" />';
+            }
+
+            return markup;
+        };
+        propertyColumns.push({id: "actions", name: "&nbsp;", minWidth: 20, width: 20, formatter: actionFormatter});
 
         var propertyConfigurationOptions = {
             forceFitColumns: true,
@@ -899,6 +1096,39 @@
                 }
             }
         };
+        
+        var goToControllerService = function (property) {
+            // close the dialog
+            var dialog = table.closest('.dialog');
+            if (dialog.hasClass('modal')) {
+                dialog.modal('hide');
+            } else {
+                dialog.hide();
+            }
+
+            $.Deferred(function (deferred) {
+                if ($('#settings').is(':visible')) {
+                    deferred.resolve();
+                } else {
+                    // reload the settings and show
+                    nf.Settings.loadSettings().done(function () {
+                        nf.Settings.showSettings();
+                        deferred.resolve();
+                    });
+                }
+            }).done(function () {
+                var controllerServiceGrid = $('#controller-services-table').data('gridInstance');
+                var controllerServiceData = controllerServiceGrid.getData();
+
+                // select the desired service
+                var row = controllerServiceData.getRowById(property.value);
+                controllerServiceGrid.setSelectedRows([row]);
+                controllerServiceGrid.scrollRowIntoView(row);
+
+                // select the controller services tab
+                $('#settings-tabs').find('li:eq(1)').click();
+            });
+        };
 
         // initialize the grid
         var propertyGrid = new Slick.Grid(table, propertyData, propertyColumns, propertyConfigurationOptions);
@@ -916,10 +1146,11 @@
                 // prevents standard edit logic
                 e.stopImmediatePropagation();
             } else if (propertyGrid.getColumns()[args.cell].id === 'actions') {
+                var property = propertyData.getItem(args.row);
+                
                 var target = $(e.target);
                 if (target.hasClass('delete-property')) {
                     // mark the property in question for removal
-                    var property = propertyData.getItem(args.row);
                     property.hidden = true;
 
                     // refresh the table
@@ -927,6 +1158,17 @@
 
                     // prevents standard edit logic
                     e.stopImmediatePropagation();
+                } else if (target.hasClass('go-to-service')) {
+                    if (options.readOnly === true) {
+                        goToControllerService(property);
+                    } else {
+                        // load the property descriptor if possible
+                        if (typeof options.goToServiceDeferred === 'function') {
+                            options.goToServiceDeferred().done(function() {
+                                goToControllerService(property);
+                            });
+                        }
+                    }
                 }
             }
         });
@@ -1076,8 +1318,8 @@
             nf.Common.removeAllPropertyDetailDialogs();
         } else {
             // clear any existing new property dialogs
-            if (nf.Common.isDefinedAndNotNull(options.newPropertyDialogContainer)) {
-                $(options.newPropertyDialogContainer).children('div.new-property-dialog').hide();
+            if (nf.Common.isDefinedAndNotNull(options.dialogContainer)) {
+                $(options.dialogContainer).children('div.new-property-dialog').hide();
             }
         }
 
@@ -1108,7 +1350,7 @@
                     var propertyTableContainer = $(this);
 
                     // clear any current contents, remote events, and store options
-                    propertyTableContainer.empty().unbind().data('options', options);
+                    propertyTableContainer.empty().unbind().addClass('property-container').data('options', options);
 
                     // build the component
                     var header = $('<div class="properties-header"></div>').appendTo(propertyTableContainer);
@@ -1118,7 +1360,7 @@
                     var table = $('<div class="property-table"></div>').appendTo(propertyTableContainer);
 
                     // optionally add a add new property button
-                    if (options.readOnly !== true && nf.Common.isDefinedAndNotNull(options.newPropertyDialogContainer)) {
+                    if (options.readOnly !== true && nf.Common.isDefinedAndNotNull(options.dialogContainer)) {
                         // build the new property dialog
                         var newPropertyDialogMarkup = 
                                 '<div class="new-property-dialog dialog cancellable">' +
@@ -1135,7 +1377,7 @@
                                     '</div>' +
                                 '</div>';
 
-                        var newPropertyDialog = $(newPropertyDialogMarkup).appendTo(options.newPropertyDialogContainer);
+                        var newPropertyDialog = $(newPropertyDialogMarkup).appendTo(options.dialogContainer);
                         var newPropertyNameField = newPropertyDialog.find('input.new-property-name');
 
                         var add = function () {
@@ -1295,8 +1537,9 @@
                 clear(propertyTableContainer);
                 
                 // clear any existing new property dialogs
-                if (nf.Common.isDefinedAndNotNull(options.newPropertyDialogContainer)) {
-                    $(options.newPropertyDialogContainer).children('div.new-property-dialog').remove();
+                if (nf.Common.isDefinedAndNotNull(options.dialogContainer)) {
+                    $(options.dialogContainer).children('div.new-property-dialog').remove();
+                    $(options.dialogContainer).children('div.new-inline-controller-service-dialog').remove();
                 }
             });
         },
@@ -1372,4 +1615,4 @@
             return methods.init.apply(this, arguments);
         }
     };
-})(jQuery);
\ No newline at end of file
+})(jQuery);

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2154b822/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
index 26b7253..39bbe64 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
@@ -374,6 +374,7 @@ nf.ControllerService = (function () {
                     // select the selected row
                     var row = controllerServiceData.getRowById(referencingComponent.id);
                     controllerServiceGrid.setSelectedRows([row]);
+                    controllerServiceGrid.scrollRowIntoView(row);
                     
                     // close the dialog and shell
                     referenceContainer.closest('.dialog').modal('hide');
@@ -419,6 +420,7 @@ nf.ControllerService = (function () {
                     // select the selected row
                     var row = reportingTaskData.getRowById(referencingComponent.id);
                     reportingTaskGrid.setSelectedRows([row]);
+                    reportingTaskGrid.scrollRowIntoView(row);
                     
                     // select the reporting task tab
                     $('#settings-tabs').find('li:last').click();
@@ -1070,6 +1072,83 @@ nf.ControllerService = (function () {
     };
     
     /**
+     * Goes to a service configuration from the property table.
+     */
+    var goToServiceFromProperty = function () {
+        return $.Deferred(function (deferred) {
+            // close all fields currently being edited
+            $('#controller-service-properties').propertytable('saveRow');
+
+            // determine if changes have been made
+            if (isSaveRequired()) {
+                // see if those changes should be saved
+                nf.Dialog.showYesNoDialog({
+                    dialogContent: 'Save changes before going to this Controller Service?',
+                    overlayBackground: false,
+                    noHandler: function () {
+                        deferred.resolve();
+                    },
+                    yesHandler: function () {
+                        var controllerService = $('#controller-service-configuration').data('controllerServiceDetails');
+                        saveControllerService(controllerService).done(function () {
+                            deferred.resolve();
+                        }).fail(function () {
+                            deferred.reject();
+                        });
+                    }
+                });
+            } else {
+                deferred.resolve();
+            }
+        }).promise();
+    };
+    
+    var saveControllerService = function (controllerService) {
+        // marshal the settings and properties and update the controller service
+        var updatedControllerService = marshalDetails();
+
+        // ensure details are valid as far as we can tell
+        if (validateDetails(updatedControllerService)) {
+            var previouslyReferencedServiceIds = [];
+            $.each(identifyReferencedServiceDescriptors(controllerService), function (_, descriptor) {
+                var modifyingService = !nf.Common.isUndefined(updatedControllerService.controllerService.properties) && !nf.Common.isUndefined(updatedControllerService.controllerService.properties[descriptor.name]);
+                var isCurrentlyConfigured = nf.Common.isDefinedAndNotNull(controllerService.properties[descriptor.name]);
+
+                // if we are attempting to update a controller service reference
+                if (modifyingService && isCurrentlyConfigured) {
+
+                    // record the current value if set
+                    previouslyReferencedServiceIds.push(controllerService.properties[descriptor.name]);
+                }
+            });
+
+            // update the selected component
+            return $.ajax({
+                type: 'PUT',
+                data: JSON.stringify(updatedControllerService),
+                url: controllerService.uri,
+                dataType: 'json',
+                processData: false,
+                contentType: 'application/json'
+            }).done(function (response) {
+                if (nf.Common.isDefinedAndNotNull(response.controllerService)) {
+                    // update the revision
+                    nf.Client.setRevision(response.revision);
+
+                    // reload all previously referenced controller services
+                    $.each(previouslyReferencedServiceIds, function(_, oldServiceReferenceId) {
+                        reloadControllerService(oldServiceReferenceId);
+                    });
+                }
+            }).fail(handleControllerServiceConfigurationError);
+        } else {
+            return $.Deferred(function (deferred) {
+                deferred.reject();
+            }).promise();
+        }
+    };
+    
+    /**
      * Identifies the descriptors that identify controller services.
      * 
      * @param {object} component
@@ -1078,7 +1157,7 @@ nf.ControllerService = (function () {
         var referencedServiceDescriptors = [];
         
         $.each(component.descriptors, function(_, descriptor) {
-            if (descriptor.identifiesControllerService === true) {
+            if (nf.Common.isDefinedAndNotNull(descriptor.identifiesControllerService)) {
                 referencedServiceDescriptors.push(descriptor);
             }
         });
@@ -1180,8 +1259,9 @@ nf.ControllerService = (function () {
             // initialize the property table
             $('#controller-service-properties').propertytable({
                 readOnly: false,
-                newPropertyDialogContainer: '#new-controller-service-property-container',
-                descriptorDeferred: getControllerServicePropertyDescriptor
+                dialogContainer: '#new-controller-service-property-container',
+                descriptorDeferred: getControllerServicePropertyDescriptor,
+                goToServiceDeferred: goToServiceFromProperty
             });
             
             // initialize the disable service dialog
@@ -1324,8 +1404,9 @@ nf.ControllerService = (function () {
                 // initialize the property table
                 $('#controller-service-properties').propertytable('destroy').propertytable({
                     readOnly: false,
-                    newPropertyDialogContainer: '#new-controller-service-property-container',
-                    descriptorDeferred: getControllerServicePropertyDescriptor
+                    dialogContainer: '#new-controller-service-property-container',
+                    descriptorDeferred: getControllerServicePropertyDescriptor,
+                    goToServiceDeferred: goToServiceFromProperty
                 });
                 
                 // update the mode
@@ -1392,49 +1473,15 @@ nf.ControllerService = (function () {
                                 // close all fields currently being edited
                                 $('#controller-service-properties').propertytable('saveRow');
 
-                                // marshal the settings and properties and update the controller service
-                                var updatedControllerService = marshalDetails();
-
-                                // ensure details are valid as far as we can tell
-                                if (validateDetails(updatedControllerService)) {
-                                    var previouslyReferencedServiceIds = [];
-                                    $.each(identifyReferencedServiceDescriptors(controllerService), function (_, descriptor) {
-                                        var modifyingService = !nf.Common.isUndefined(updatedControllerService.controllerService.properties) && !nf.Common.isUndefined(updatedControllerService.controllerService.properties[descriptor.name]);
-                                        var isCurrentlyConfigured = nf.Common.isDefinedAndNotNull(controllerService.properties[descriptor.name]);
-                                        
-                                        // if we are attempting to update a controller service reference
-                                        if (modifyingService && isCurrentlyConfigured) {
-                                            // record the current value if set
-                                            previouslyReferencedServiceIds.push(controllerService.properties[descriptor.name]);
-                                        }
-                                    });
+                                // save the controller service
+                                saveControllerService(controllerService).done(function (response) {
+                                    // reload the controller service
+                                    renderControllerService(response.controllerService);
+                                    reloadControllerServiceReferences(response.controllerService);
                                     
-                                    // update the selected component
-                                    $.ajax({
-                                        type: 'PUT',
-                                        data: JSON.stringify(updatedControllerService),
-                                        url: controllerService.uri,
-                                        dataType: 'json',
-                                        processData: false,
-                                        contentType: 'application/json'
-                                    }).done(function (response) {
-                                        if (nf.Common.isDefinedAndNotNull(response.controllerService)) {
-                                            nf.Client.setRevision(response.revision);
-
-                                            // reload the controller service
-                                            renderControllerService(response.controllerService);
-                                            reloadControllerServiceReferences(response.controllerService);
-                                            
-                                            // reload all previously referenced controller services
-                                            $.each(previouslyReferencedServiceIds, function(_, oldServiceReferenceId) {
-                                                reloadControllerService(oldServiceReferenceId);
-                                            });
-
-                                            // close the details panel
-                                            controllerServiceDialog.modal('hide');
-                                        }
-                                    }).fail(handleControllerServiceConfigurationError);
-                                }
+                                    // close the details panel
+                                    controllerServiceDialog.modal('hide');
+                                });
                             }
                         }
                     }, {
@@ -1480,47 +1527,10 @@ nf.ControllerService = (function () {
                                         overlayBackground: false,
                                         noHandler: openCustomUi,
                                         yesHandler: function () {
-                                            // marshal the settings and properties and update the controller service
-                                            var updatedControllerService = marshalDetails();
-
-                                            // ensure details are valid as far as we can tell
-                                            if (validateDetails(updatedControllerService)) {
-                                                var previouslyReferencedServiceIds = [];
-                                                $.each(identifyReferencedServiceDescriptors(controllerService), function (_, descriptor) {
-                                                    var modifyingService = !nf.Common.isUndefined(updatedControllerService.controllerService.properties) && !nf.Common.isUndefined(updatedControllerService.controllerService.properties[descriptor.name]);
-                                                    var isCurrentlyConfigured = nf.Common.isDefinedAndNotNull(controllerService.properties[descriptor.name]);
-
-                                                    // if we are attempting to update a controller service reference
-                                                    if (modifyingService && isCurrentlyConfigured) {
-                                                        
-                                                        // record the current value if set
-                                                        previouslyReferencedServiceIds.push(controllerService.properties[descriptor.name]);
-                                                    }
-                                                });
-
-                                                // update the selected component
-                                                $.ajax({
-                                                    type: 'PUT',
-                                                    data: JSON.stringify(updatedControllerService),
-                                                    url: controllerService.uri,
-                                                    dataType: 'json',
-                                                    processData: false,
-                                                    contentType: 'application/json'
-                                                }).done(function (response) {
-                                                    if (nf.Common.isDefinedAndNotNull(response.controllerService)) {
-                                                        // update the revision
-                                                        nf.Client.setRevision(response.revision);
-
-                                                        // reload all previously referenced controller services
-                                                        $.each(previouslyReferencedServiceIds, function(_, oldServiceReferenceId) {
-                                                            reloadControllerService(oldServiceReferenceId);
-                                                        });
-
-                                                        // open the custom ui
-                                                        openCustomUi();
-                                                    }
-                                                }).fail(handleControllerServiceConfigurationError);
-                                            }
+                                            saveControllerService(controllerService).done(function () {
+                                                // open the custom ui
+                                                openCustomUi();
+                                            });
                                         }
                                     });
                                 } else {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2154b822/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js
index 4e828df..2e48579 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js
@@ -380,6 +380,70 @@ nf.ProcessorConfiguration = (function () {
             }
         });
     };
+    
+    /**
+     * Goes to a service configuration from the property table.
+     */
+    var goToServiceFromProperty = function () {
+        return $.Deferred(function (deferred) {
+            // close all fields currently being edited
+            $('#processor-properties').propertytable('saveRow');
+
+            // determine if changes have been made
+            if (isSaveRequired()) {
+                // see if those changes should be saved
+                nf.Dialog.showYesNoDialog({
+                    dialogContent: 'Save changes before going to this Controller Service?',
+                    overlayBackground: false,
+                    noHandler: function () {
+                        deferred.resolve();
+                    },
+                    yesHandler: function () {
+                        var processor = $('#processor-configuration').data('processorDetails');
+                        saveProcessor(processor).done(function () {
+                            deferred.resolve();
+                        }).fail(function () {
+                            deferred.reject();
+                        });
+                    }
+                });
+            } else {
+                deferred.resolve();
+            }
+        }).promise();
+    };
+    
+    /**
+     * 
+     * @param {type} processor
+     * @returns {undefined}
+     */
+    var saveProcessor = function (processor) {
+        // marshal the settings and properties and update the processor
+        var updatedProcessor = marshalDetails();
+
+        // ensure details are valid as far as we can tell
+        if (validateDetails(updatedProcessor)) {
+            // update the selected component
+            return $.ajax({
+                type: 'PUT',
+                data: JSON.stringify(updatedProcessor),
+                url: processor.uri,
+                dataType: 'json',
+                processData: false,
+                contentType: 'application/json'
+            }).done(function (response) {
+                if (nf.Common.isDefinedAndNotNull(response.processor)) {
+                    // update the revision
+                    nf.Client.setRevision(response.revision);
+                }
+            }).fail(handleProcessorConfigurationError);
+        } else {
+            return $.Deferred(function (deferred) {
+                deferred.reject();
+            }).promise();
+        }
+    };
 
     return {
         /**
@@ -470,7 +534,7 @@ nf.ProcessorConfiguration = (function () {
             // initialize the property table
             $('#processor-properties').propertytable({
                 readOnly: false,
-                newPropertyDialogContainer: '#new-processor-property-container',
+                dialogContainer: '#new-processor-property-container',
                 descriptorDeferred: function(propertyName) {
                     var processor = $('#processor-configuration').data('processorDetails');
                     return $.ajax({
@@ -481,7 +545,8 @@ nf.ProcessorConfiguration = (function () {
                         },
                         dataType: 'json'
                     }).fail(nf.Common.handleAjaxError);
-                }
+                },
+                goToServiceDeferred: goToServiceFromProperty
             });
         },
         
@@ -625,35 +690,17 @@ nf.ProcessorConfiguration = (function () {
                                     // close all fields currently being edited
                                     $('#processor-properties').propertytable('saveRow');
 
-                                    // marshal the settings and properties and update the processor
-                                    var updatedProcessor = marshalDetails();
-
-                                    // ensure details are valid as far as we can tell
-                                    if (validateDetails(updatedProcessor)) {
-                                        // update the selected component
-                                        $.ajax({
-                                            type: 'PUT',
-                                            data: JSON.stringify(updatedProcessor),
-                                            url: processor.uri,
-                                            dataType: 'json',
-                                            processData: false,
-                                            contentType: 'application/json'
-                                        }).done(function (response) {
-                                            if (nf.Common.isDefinedAndNotNull(response.processor)) {
-                                                // update the revision
-                                                nf.Client.setRevision(response.revision);
-
-                                                // set the new processor state based on the response
-                                                nf.Processor.set(response.processor);
-
-                                                // reload the processor's outgoing connections
-                                                reloadProcessorConnections(processor);
-
-                                                // close the details panel
-                                                $('#processor-configuration').modal('hide');
-                                            }
-                                        }).fail(handleProcessorConfigurationError);
-                                    }
+                                    // save the processor
+                                    saveProcessor(processor).done(function (response) {
+                                        // set the new processor state based on the response
+                                        nf.Processor.set(response.processor);
+
+                                        // reload the processor's outgoing connections
+                                        reloadProcessorConnections(processor);
+
+                                        // close the details panel
+                                        $('#processor-configuration').modal('hide');
+                                    });
                                 }
                             }
                         }, {
@@ -696,29 +743,10 @@ nf.ProcessorConfiguration = (function () {
                                             overlayBackground: false,
                                             noHandler: openCustomUi,
                                             yesHandler: function () {
-                                                // marshal the settings and properties and update the processor
-                                                var updatedProcessor = marshalDetails();
-
-                                                // ensure details are valid as far as we can tell
-                                                if (validateDetails(updatedProcessor)) {
-                                                    // update the selected component
-                                                    $.ajax({
-                                                        type: 'PUT',
-                                                        data: JSON.stringify(updatedProcessor),
-                                                        url: processor.uri,
-                                                        dataType: 'json',
-                                                        processData: false,
-                                                        contentType: 'application/json'
-                                                    }).done(function (response) {
-                                                        if (nf.Common.isDefinedAndNotNull(response.processor)) {
-                                                            // update the revision
-                                                            nf.Client.setRevision(response.revision);
-
-                                                            // open the custom ui
-                                                            openCustomUi();
-                                                        }
-                                                    }).fail(handleProcessorConfigurationError);
-                                                }
+                                                saveProcessor(processor).done(function (deferred) {
+                                                    // open the custom ui
+                                                    openCustomUi();
+                                                });
                                             }
                                         });
                                     } else {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2154b822/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-reporting-task.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-reporting-task.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-reporting-task.js
index ed13f10..998213e 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-reporting-task.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-reporting-task.js
@@ -207,6 +207,70 @@ nf.ReportingTask = (function () {
     };
     
     /**
+     * Goes to a service configuration from the property table.
+     */
+    var goToServiceFromProperty = function () {
+        return $.Deferred(function (deferred) {
+            // close all fields currently being edited
+            $('#reporting-task-properties').propertytable('saveRow');
+
+            // determine if changes have been made
+            if (isSaveRequired()) {
+                // see if those changes should be saved
+                nf.Dialog.showYesNoDialog({
+                    dialogContent: 'Save changes before going to this Controller Service?',
+                    overlayBackground: false,
+                    noHandler: function () {
+                        deferred.resolve();
+                    },
+                    yesHandler: function () {
+                        var reportingTask = $('#reporting-task-configuration').data('reportingTaskDetails');
+                        saveReportingTask(reportingTask).done(function () {
+                            deferred.resolve();
+                        }).fail(function () {
+                            deferred.reject();
+                        });
+                    }
+                });
+            } else {
+                deferred.resolve();
+            }
+        }).promise();
+    };
+    
+    /**
+     * Saves the specified reporting task.
+     * 
+     * @param {type} reportingTask
+     */
+    var saveReportingTask = function (reportingTask) {
+        // marshal the settings and properties and update the reporting task
+        var updatedReportingTask = marshalDetails();
+
+        // ensure details are valid as far as we can tell
+        if (validateDetails(updatedReportingTask)) {
+            // update the selected component
+            return $.ajax({
+                type: 'PUT',
+                data: JSON.stringify(updatedReportingTask),
+                url: reportingTask.uri,
+                dataType: 'json',
+                processData: false,
+                contentType: 'application/json'
+            }).done(function (response) {
+                if (nf.Common.isDefinedAndNotNull(response.reportingTask)) {
+                    // update the revision
+                    nf.Client.setRevision(response.revision);
+                }
+            }).fail(handleReportingTaskConfigurationError);
+        } else {
+            return $.Deferred(function (deferred) {
+                deferred.reject();
+            }).promise();
+        }
+    };
+    
+    /**
      * Gets a property descriptor for the controller service currently being configured.
      * 
      * @param {type} propertyName
@@ -288,8 +352,9 @@ nf.ReportingTask = (function () {
             // initialize the property table
             $('#reporting-task-properties').propertytable({
                 readOnly: false,
-                newPropertyDialogContainer: '#new-reporting-task-property-container',
-                deferredDescriptor: getReportingTaskPropertyDescriptor
+                dialogContainer: '#new-reporting-task-property-container',
+                deferredDescriptor: getReportingTaskPropertyDescriptor,
+                goToServiceDeferred: goToServiceFromProperty
             });
         },
         
@@ -308,8 +373,9 @@ nf.ReportingTask = (function () {
                 // initialize the property table
                 $('#reporting-task-properties').propertytable('destroy').propertytable({
                     readOnly: false,
-                    newPropertyDialogContainer: '#new-reporting-task-property-container',
-                    deferredDescriptor: getReportingTaskPropertyDescriptor
+                    dialogContainer: '#new-reporting-task-property-container',
+                    deferredDescriptor: getReportingTaskPropertyDescriptor,
+                    goToServiceDeferred: goToServiceFromProperty
                 });
                 
                 // update the mode
@@ -407,33 +473,15 @@ nf.ReportingTask = (function () {
                                 // close all fields currently being edited
                                 $('#reporting-task-properties').propertytable('saveRow');
 
-                                // marshal the settings and properties and update the reporting task
-                                var updatedReportingTask = marshalDetails();
-
-                                // ensure details are valid as far as we can tell
-                                if (validateDetails(updatedReportingTask)) {
-                                    // update the selected component
-                                    $.ajax({
-                                        type: 'PUT',
-                                        data: JSON.stringify(updatedReportingTask),
-                                        url: reportingTask.uri,
-                                        dataType: 'json',
-                                        processData: false,
-                                        contentType: 'application/json'
-                                    }).done(function (response) {
-                                        if (nf.Common.isDefinedAndNotNull(response.reportingTask)) {
-                                            // update the revision
-                                            nf.Client.setRevision(response.revision);
-
-                                            // reload the reporting task
-                                            renderReportingTask(response.reportingTask);
-                                            nf.ControllerService.reloadReferencedServices(response.reportingTask);
+                                // save the reporting task
+                                saveReportingTask(reportingTask).done(function (response) {
+                                    // reload the reporting task
+                                    renderReportingTask(response.reportingTask);
+                                    nf.ControllerService.reloadReferencedServices(response.reportingTask);
 
-                                            // close the details panel
-                                            $('#reporting-task-configuration').modal('hide');
-                                        }
-                                    }).fail(handleReportingTaskConfigurationError);
-                                }
+                                    // close the details panel
+                                    $('#reporting-task-configuration').modal('hide');
+                                });
                             }
                         }
                     }, {
@@ -481,29 +529,10 @@ nf.ReportingTask = (function () {
                                         overlayBackground: false,
                                         noHandler: openCustomUi,
                                         yesHandler: function () {
-                                            // marshal the settings and properties and update the reporting task
-                                            var updatedReportingTask = marshalDetails();
-
-                                            // ensure details are valid as far as we can tell
-                                            if (validateDetails(updatedReportingTask)) {
-                                                // update the selected component
-                                                $.ajax({
-                                                    type: 'PUT',
-                                                    data: JSON.stringify(updatedReportingTask),
-                                                    url: reportingTask.uri,
-                                                    dataType: 'json',
-                                                    processData: false,
-                                                    contentType: 'application/json'
-                                                }).done(function (response) {
-                                                    if (nf.Common.isDefinedAndNotNull(response.reportingTask)) {
-                                                        // update the revision
-                                                        nf.Client.setRevision(response.revision);
-
-                                                        // open the custom ui
-                                                        openCustomUi();
-                                                    }
-                                                }).fail(handleReportingTaskConfigurationError);
-                                            }
+                                            saveReportingTask(reportingTask).done(function () {
+                                                // open the custom ui
+                                                openCustomUi();
+                                            });
                                         }
                                     });
                                 } else {


[13/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
new file mode 100644
index 0000000..1711f3c
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
@@ -0,0 +1,803 @@
+/*
+ * 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.web.api;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import org.apache.nifi.cluster.manager.impl.WebClusterManager;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.ConfigurationSnapshot;
+import org.apache.nifi.web.NiFiServiceFacade;
+import org.apache.nifi.web.Revision;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.request.ClientIdParameter;
+import org.apache.nifi.web.api.request.LongParameter;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.controller.ScheduledState;
+import org.apache.nifi.controller.service.ControllerServiceState;
+import org.apache.nifi.ui.extension.UiExtension;
+import org.apache.nifi.ui.extension.UiExtensionMapping;
+import org.apache.nifi.web.UiExtensionType;
+import static org.apache.nifi.web.api.ApplicationResource.CLIENT_ID;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
+import org.apache.nifi.web.api.dto.ControllerServiceReferencingComponentDTO;
+import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
+import org.apache.nifi.web.api.entity.ControllerServiceEntity;
+import org.apache.nifi.web.api.entity.ControllerServiceReferencingComponentsEntity;
+import org.apache.nifi.web.api.entity.ControllerServicesEntity;
+import org.apache.nifi.web.api.entity.PropertyDescriptorEntity;
+import org.apache.nifi.web.util.Availability;
+import org.codehaus.enunciate.jaxrs.TypeHint;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.access.prepost.PreAuthorize;
+
+/**
+ * RESTful endpoint for managing a Controller Service.
+ */
+public class ControllerServiceResource extends ApplicationResource {
+
+    private static final Logger logger = LoggerFactory.getLogger(ControllerServiceResource.class);
+
+    private NiFiServiceFacade serviceFacade;
+    private WebClusterManager clusterManager;
+    private NiFiProperties properties;
+
+    @Context
+    private ServletContext servletContext;
+    
+    /**
+     * Populates the uri for the specified controller service.
+     * 
+     * @param controllerServices
+     * @return 
+     */
+    private Set<ControllerServiceDTO> populateRemainingControllerServicesContent(final String availability, final Set<ControllerServiceDTO> controllerServices) {
+        for (ControllerServiceDTO controllerService : controllerServices) {
+            populateRemainingControllerServiceContent(availability, controllerService);
+        }
+        return controllerServices;
+    }
+    
+    /**
+     * Populates the uri for the specified controller service.
+     */
+    private ControllerServiceDTO populateRemainingControllerServiceContent(final String availability, final ControllerServiceDTO controllerService) {
+        // populate the controller service href
+        controllerService.setUri(generateResourceUri("controller", "controller-services", availability, controllerService.getId()));
+        controllerService.setAvailability(availability);
+        
+        // see if this processor has any ui extensions
+        final UiExtensionMapping uiExtensionMapping = (UiExtensionMapping) servletContext.getAttribute("nifi-ui-extensions");
+        if (uiExtensionMapping.hasUiExtension(controllerService.getType())) {
+            final List<UiExtension> uiExtensions = uiExtensionMapping.getUiExtension(controllerService.getType());
+            for (final UiExtension uiExtension : uiExtensions) {
+                if (UiExtensionType.ControllerServiceConfiguration.equals(uiExtension.getExtensionType())) {
+                    controllerService.setCustomUiUrl(uiExtension.getContextPath() + "/configure");
+                }
+            }
+        }
+        
+        return controllerService;
+    }
+
+    /**
+     * Parses the availability and ensure that the specified availability makes sense for the
+     * given NiFi instance.
+     * 
+     * @param availability
+     * @return 
+     */
+    private Availability parseAvailability(final String availability) {
+        final Availability avail;
+        try {
+            avail = Availability.valueOf(availability.toUpperCase());
+        } catch (IllegalArgumentException iae) {
+            throw new IllegalArgumentException(String.format("Availability: Value must be one of [%s]", StringUtils.join(Availability.values(), ", ")));
+        }
+        
+        // ensure this nifi is an NCM is specifying NCM availability
+        if (!properties.isClusterManager() && Availability.NCM.equals(avail)) {
+            throw new IllegalArgumentException("Availability of NCM is only applicable when the NiFi instance is the cluster manager.");
+        }
+        
+        return avail;
+    }
+    
+    /**
+     * Retrieves all the of controller services in this NiFi.
+     *
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @param availability Whether the controller service is available on the NCM only (ncm) or on the 
+     * nodes only (node). If this instance is not clustered all services should use the node availability.
+     * @return A controllerServicesEntity.
+     */
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}")
+    @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
+    @TypeHint(ControllerServicesEntity.class)
+    public Response getControllerServices(@QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, @PathParam("availability") String availability) {
+        final Availability avail = parseAvailability(availability);
+        
+        // replicate if cluster manager
+        if (properties.isClusterManager() && Availability.NODE.equals(avail)) {
+            return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
+        }
+
+        // get all the controller services
+        final Set<ControllerServiceDTO> controllerServices = populateRemainingControllerServicesContent(availability, serviceFacade.getControllerServices());
+        
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+
+        // create the response entity
+        final ControllerServicesEntity entity = new ControllerServicesEntity();
+        entity.setRevision(revision);
+        entity.setControllerServices(controllerServices);
+
+        // generate the response
+        return clusterContext(generateOkResponse(entity)).build();
+    }
+
+    /**
+     * Creates a new controller service.
+     *
+     * @param httpServletRequest
+     * @param version The revision is used to verify the client is working with
+     * the latest version of the flow.
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @param availability Whether the controller service is available on the NCM only (ncm) or on the 
+     * nodes only (node). If this instance is not clustered all services should use the node availability.
+     * @param type The type of controller service to create.
+     * @return A controllerServiceEntity.
+     */
+    @POST
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}")
+    @PreAuthorize("hasRole('ROLE_DFM')")
+    @TypeHint(ControllerServiceEntity.class)
+    public Response createControllerService(
+            @Context HttpServletRequest httpServletRequest,
+            @FormParam(VERSION) LongParameter version,
+            @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+            @PathParam("availability") String availability, 
+            @FormParam("type") String type) {
+        
+        // create the controller service DTO
+        final ControllerServiceDTO controllerServiceDTO = new ControllerServiceDTO();
+        controllerServiceDTO.setType(type);
+
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+        if (version != null) {
+            revision.setVersion(version.getLong());
+        }
+
+        // create the controller service entity
+        final ControllerServiceEntity controllerServiceEntity = new ControllerServiceEntity();
+        controllerServiceEntity.setRevision(revision);
+        controllerServiceEntity.setControllerService(controllerServiceDTO);
+
+        return createControllerService(httpServletRequest, availability, controllerServiceEntity);
+    }
+
+    /**
+     * Creates a new Controller Service.
+     *
+     * @param httpServletRequest
+     * @param availability Whether the controller service is available on the NCM only (ncm) or on the 
+     * nodes only (node). If this instance is not clustered all services should use the node availability.
+     * @param controllerServiceEntity A controllerServiceEntity.
+     * @return A controllerServiceEntity.
+     */
+    @POST
+    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}")
+    @PreAuthorize("hasRole('ROLE_DFM')")
+    @TypeHint(ControllerServiceEntity.class)
+    public Response createControllerService(
+            @Context HttpServletRequest httpServletRequest,
+            @PathParam("availability") String availability, 
+            ControllerServiceEntity controllerServiceEntity) {
+        
+        final Availability avail = parseAvailability(availability);
+
+        if (controllerServiceEntity == null || controllerServiceEntity.getControllerService()== null) {
+            throw new IllegalArgumentException("Controller service details must be specified.");
+        }
+
+        if (controllerServiceEntity.getRevision() == null) {
+            throw new IllegalArgumentException("Revision must be specified.");
+        }
+        
+        if (controllerServiceEntity.getControllerService().getId() != null) {
+            throw new IllegalArgumentException("Controller service ID cannot be specified.");
+        }
+
+        if (StringUtils.isBlank(controllerServiceEntity.getControllerService().getType())) {
+            throw new IllegalArgumentException("The type of controller service to create must be specified.");
+        }
+        
+        // get the revision
+        final RevisionDTO revision = controllerServiceEntity.getRevision();
+        
+        // if cluster manager, convert POST to PUT (to maintain same ID across nodes) and replicate
+        if (properties.isClusterManager() && Availability.NODE.equals(avail)) {
+            // create ID for resource
+            final String id = UUID.randomUUID().toString();
+
+            // set ID for resource
+            controllerServiceEntity.getControllerService().setId(id);
+
+            // convert POST request to PUT request to force entity ID to be the same across nodes
+            URI putUri = null;
+            try {
+                putUri = new URI(getAbsolutePath().toString() + "/" + id);
+            } catch (final URISyntaxException e) {
+                throw new WebApplicationException(e);
+            }
+
+            // change content type to JSON for serializing entity
+            final Map<String, String> headersToOverride = new HashMap<>();
+            headersToOverride.put("content-type", MediaType.APPLICATION_JSON);
+
+            // replicate put request
+            return (Response) clusterManager.applyRequest(HttpMethod.PUT, putUri, updateClientId(controllerServiceEntity), getHeaders(headersToOverride)).getResponse();
+        }
+
+        // handle expects request (usually from the cluster manager)
+        final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER);
+        if (expects != null) {
+            return generateContinueResponse().build();
+        }
+
+        // create the controller service and generate the json
+        final ConfigurationSnapshot<ControllerServiceDTO> controllerResponse = serviceFacade.createControllerService(
+                new Revision(revision.getVersion(), revision.getClientId()), controllerServiceEntity.getControllerService());
+        final ControllerServiceDTO controllerService = controllerResponse.getConfiguration();
+
+        // get the updated revision
+        final RevisionDTO updatedRevision = new RevisionDTO();
+        updatedRevision.setClientId(revision.getClientId());
+        updatedRevision.setVersion(controllerResponse.getVersion());
+
+        // build the response entity
+        final ControllerServiceEntity entity = new ControllerServiceEntity();
+        entity.setRevision(updatedRevision);
+        entity.setControllerService(populateRemainingControllerServiceContent(availability, controllerService));
+
+        // build the response
+        return clusterContext(generateCreatedResponse(URI.create(controllerService.getUri()), entity)).build();
+    }
+
+    /**
+     * Retrieves the specified controller service.
+     *
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @param availability Whether the controller service is available on the NCM only (ncm) or on the 
+     * nodes only (node). If this instance is not clustered all services should use the node availability.
+     * @param id The id of the controller service to retrieve
+     * @return A controllerServiceEntity.
+     */
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}/{id}")
+    @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
+    @TypeHint(ControllerServiceEntity.class)
+    public Response getControllerService(@QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, 
+            @PathParam("availability") String availability, @PathParam("id") String id) {
+
+        final Availability avail = parseAvailability(availability);
+        
+        // replicate if cluster manager
+        if (properties.isClusterManager() && Availability.NODE.equals(avail)) {
+            return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
+        }
+
+        // get the controller service
+        final ControllerServiceDTO controllerService = serviceFacade.getControllerService(id);
+
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+
+        // create the response entity
+        final ControllerServiceEntity entity = new ControllerServiceEntity();
+        entity.setRevision(revision);
+        entity.setControllerService(populateRemainingControllerServiceContent(availability, controllerService));
+
+        return clusterContext(generateOkResponse(entity)).build();
+    }
+    
+    /**
+     * Returns the descriptor for the specified property.
+     * 
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @param availability
+     * @param id The id of the controller service.
+     * @param propertyName The property
+     * @return a propertyDescriptorEntity
+     */
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}/{id}/descriptors")
+    @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
+    @TypeHint(PropertyDescriptorEntity.class)
+    public Response getPropertyDescriptor(
+            @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, 
+            @PathParam("availability") String availability, @PathParam("id") String id, 
+            @QueryParam("propertyName") String propertyName) {
+        
+        final Availability avail = parseAvailability(availability);
+        
+        // ensure the property name is specified
+        if (propertyName == null) {
+            throw new IllegalArgumentException("The property name must be specified.");
+        }
+        
+        // replicate if cluster manager and service is on node
+        if (properties.isClusterManager() && Availability.NODE.equals(avail)) {
+            return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
+        }
+        
+        // get the property descriptor
+        final PropertyDescriptorDTO descriptor = serviceFacade.getControllerServicePropertyDescriptor(id, propertyName);
+        
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+        
+        // generate the response entity
+        final PropertyDescriptorEntity entity = new PropertyDescriptorEntity();
+        entity.setRevision(revision);
+        entity.setPropertyDescriptor(descriptor);
+        
+        // generate the response
+        return clusterContext(generateOkResponse(entity)).build();
+    }
+    
+    /**
+     * Retrieves the references of the specified controller service.
+     *
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @param availability Whether the controller service is available on the NCM only (ncm) or on the 
+     * nodes only (node). If this instance is not clustered all services should use the node availability.
+     * @param id The id of the controller service to retrieve
+     * @return A controllerServiceEntity.
+     */
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}/{id}/references")
+    @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
+    @TypeHint(ControllerServiceEntity.class)
+    public Response getControllerServiceReferences(
+            @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+            @PathParam("availability") String availability, @PathParam("id") String id) {
+
+        final Availability avail = parseAvailability(availability);
+        
+        // replicate if cluster manager
+        if (properties.isClusterManager() && Availability.NODE.equals(avail)) {
+            return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
+        }
+
+        // get the controller service
+        final Set<ControllerServiceReferencingComponentDTO> controllerServiceReferences = serviceFacade.getControllerServiceReferencingComponents(id);
+
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+
+        // create the response entity
+        final ControllerServiceReferencingComponentsEntity entity = new ControllerServiceReferencingComponentsEntity();
+        entity.setRevision(revision);
+        entity.setControllerServiceReferencingComponents(controllerServiceReferences);
+
+        return clusterContext(generateOkResponse(entity)).build();
+    }
+    
+    /**
+     * Updates the references of the specified controller service.
+     *
+     * @param httpServletRequest
+     * @param version The revision is used to verify the client is working with
+     * the latest version of the flow.
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @param availability Whether the controller service is available on the NCM only (ncm) or on the 
+     * nodes only (node). If this instance is not clustered all services should use the node availability.
+     * @param id The id of the controller service to retrieve
+     * @param state Sets the state of referencing components. A value of RUNNING or STOPPED will update
+     * referencing schedulable components (Processors and Reporting Tasks). A value of ENABLED or
+     * DISABLED will update referencing controller services.
+     * @return A controllerServiceEntity.
+     */
+    @PUT
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}/{id}/references")
+    @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
+    @TypeHint(ControllerServiceEntity.class)
+    public Response updateControllerServiceReferences(
+            @Context HttpServletRequest httpServletRequest,
+            @FormParam(VERSION) LongParameter version,
+            @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+            @PathParam("availability") String availability, @PathParam("id") String id,
+            @FormParam("state") @DefaultValue(StringUtils.EMPTY) String state) {
+
+        // parse the state to determine the desired action
+        
+        // need to consider controller service state first as it shares a state with
+        // scheduled state (disabled) which is applicable for referencing services
+        // but not referencing schedulable components
+        ControllerServiceState controllerServiceState = null;
+        try {
+            controllerServiceState = ControllerServiceState.valueOf(state);
+        } catch (final IllegalArgumentException iae) {
+            // ignore
+        }
+        
+        ScheduledState scheduledState = null;
+        try {
+            scheduledState = ScheduledState.valueOf(state);
+        } catch (final IllegalArgumentException iae) {
+            // ignore
+        }
+        
+        // ensure an action has been specified
+        if (scheduledState == null && controllerServiceState == null) {
+            throw new IllegalArgumentException("Must specify the updated state. To update referencing Processors "
+                    + "and Reporting Tasks the state should be RUNNING or STOPPED. To update the referencing Controller Services the "
+                    + "state should be ENABLED or DISABLED.");
+        }
+        
+        // ensure the controller service state is not ENABLING or DISABLING
+        if (controllerServiceState != null && (ControllerServiceState.ENABLING.equals(controllerServiceState) || ControllerServiceState.DISABLING.equals(controllerServiceState))) {
+            throw new IllegalArgumentException("Cannot set the referencing services to ENABLING or DISABLING");
+        }
+        
+        // determine the availability
+        final Availability avail = parseAvailability(availability);
+        
+        // replicate if cluster manager
+        if (properties.isClusterManager() && Availability.NODE.equals(avail)) {
+            return clusterManager.applyRequest(HttpMethod.PUT, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
+        }
+
+        // handle expects request (usually from the cluster manager)
+        final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER);
+        if (expects != null) {
+            serviceFacade.verifyUpdateControllerServiceReferencingComponents(id, scheduledState, controllerServiceState);
+            return generateContinueResponse().build();
+        }
+        
+        // determine the specified version
+        Long clientVersion = null;
+        if (version != null) {
+            clientVersion = version.getLong();
+        }
+        
+        // get the controller service
+        final ConfigurationSnapshot<Set<ControllerServiceReferencingComponentDTO>> response = 
+                serviceFacade.updateControllerServiceReferencingComponents(new Revision(clientVersion, clientId.getClientId()), id, scheduledState, controllerServiceState);
+
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+        revision.setVersion(response.getVersion());
+
+        // create the response entity
+        final ControllerServiceReferencingComponentsEntity entity = new ControllerServiceReferencingComponentsEntity();
+        entity.setRevision(revision);
+        entity.setControllerServiceReferencingComponents(response.getConfiguration());
+
+        return clusterContext(generateOkResponse(entity)).build();
+    }
+
+    /**
+     * Updates the specified controller service.
+     *
+     * @param httpServletRequest
+     * @param version The revision is used to verify the client is working with
+     * the latest version of the flow.
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @param availability Whether the controller service is available on the NCM only (ncm) or on the 
+     * nodes only (node). If this instance is not clustered all services should use the node availability.
+     * @param id The id of the controller service to update.
+     * @param name The name of the controller service
+     * @param annotationData The annotation data for the controller service
+     * @param comments The comments for the controller service
+     * @param state The state of this controller service. Should be ENABLED or DISABLED.
+     * @param markedForDeletion Array of property names whose value should be removed.
+     * @param formParams Additionally, the processor properties and styles are
+     * specified in the form parameters. Because the property names and styles
+     * differ from processor to processor they are specified in a map-like
+     * fashion:
+     * <br>
+     * <ul>
+     * <li>properties[required.file.path]=/path/to/file</li>
+     * <li>properties[required.hostname]=localhost</li>
+     * <li>properties[required.port]=80</li>
+     * <li>properties[optional.file.path]=/path/to/file</li>
+     * <li>properties[optional.hostname]=localhost</li>
+     * <li>properties[optional.port]=80</li>
+     * <li>properties[user.defined.pattern]=^.*?s.*$</li>
+     * </ul>
+     * @return A controllerServiceEntity.
+     */
+    @PUT
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}/{id}")
+    @PreAuthorize("hasRole('ROLE_DFM')")
+    @TypeHint(ControllerServiceEntity.class)
+    public Response updateControllerService(
+            @Context HttpServletRequest httpServletRequest,
+            @FormParam(VERSION) LongParameter version,
+            @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+            @PathParam("availability") String availability, @PathParam("id") String id, @FormParam("name") String name,
+            @FormParam("annotationData") String annotationData, @FormParam("comments") String comments,
+            @FormParam("state") String state, @FormParam("markedForDeletion[]") List<String> markedForDeletion,
+            MultivaluedMap<String, String> formParams) {
+
+        // create collections for holding the controller service properties
+        final Map<String, String> updatedProperties = new LinkedHashMap<>();
+        
+        // go through each parameter and look for processor properties
+        for (String parameterName : formParams.keySet()) {
+            if (StringUtils.isNotBlank(parameterName)) {
+                // see if the parameter name starts with an expected parameter type...
+                // if so, store the parameter name and value in the corresponding collection
+                if (parameterName.startsWith("properties")) {
+                    final int startIndex = StringUtils.indexOf(parameterName, "[");
+                    final int endIndex = StringUtils.lastIndexOf(parameterName, "]");
+                    if (startIndex != -1 && endIndex != -1) {
+                        final String propertyName = StringUtils.substring(parameterName, startIndex + 1, endIndex);
+                        updatedProperties.put(propertyName, formParams.getFirst(parameterName));
+                    }
+                }
+            }
+        }
+        
+        // set the properties to remove
+        for (String propertyToDelete : markedForDeletion) {
+            updatedProperties.put(propertyToDelete, null);
+        }
+        
+        // create the controller service DTO
+        final ControllerServiceDTO controllerServiceDTO = new ControllerServiceDTO();
+        controllerServiceDTO.setId(id);
+        controllerServiceDTO.setName(name);
+        controllerServiceDTO.setAnnotationData(annotationData);
+        controllerServiceDTO.setComments(comments);
+        controllerServiceDTO.setState(state);
+
+        // only set the properties when appropriate
+        if (!updatedProperties.isEmpty()) {
+            controllerServiceDTO.setProperties(updatedProperties);
+        }
+        
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+        if (version != null) {
+            revision.setVersion(version.getLong());
+        }
+
+        // create the controller service entity
+        final ControllerServiceEntity controllerServiceEntity = new ControllerServiceEntity();
+        controllerServiceEntity.setRevision(revision);
+        controllerServiceEntity.setControllerService(controllerServiceDTO);
+
+        // update the controller service
+        return updateControllerService(httpServletRequest, availability, id, controllerServiceEntity);
+    }
+
+    /**
+     * Updates the specified a new Controller Service.
+     *
+     * @param httpServletRequest
+     * @param availability Whether the controller service is available on the NCM only (ncm) or on the 
+     * nodes only (node). If this instance is not clustered all services should use the node availability.
+     * @param id The id of the controller service to update.
+     * @param controllerServiceEntity A controllerServiceEntity.
+     * @return A controllerServiceEntity.
+     */
+    @PUT
+    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}/{id}")
+    @PreAuthorize("hasRole('ROLE_DFM')")
+    @TypeHint(ControllerServiceEntity.class)
+    public Response updateControllerService(
+            @Context HttpServletRequest httpServletRequest,
+            @PathParam("availability") String availability, 
+            @PathParam("id") String id,
+            ControllerServiceEntity controllerServiceEntity) {
+
+        final Availability avail = parseAvailability(availability);
+        
+        if (controllerServiceEntity == null || controllerServiceEntity.getControllerService()== null) {
+            throw new IllegalArgumentException("Controller service details must be specified.");
+        }
+
+        if (controllerServiceEntity.getRevision() == null) {
+            throw new IllegalArgumentException("Revision must be specified.");
+        }
+
+        // ensure the ids are the same
+        final ControllerServiceDTO requestControllerServiceDTO = controllerServiceEntity.getControllerService();
+        if (!id.equals(requestControllerServiceDTO.getId())) {
+            throw new IllegalArgumentException(String.format("The controller service id (%s) in the request body does not equal the "
+                    + "controller service id of the requested resource (%s).", requestControllerServiceDTO.getId(), id));
+        }
+
+        // replicate if cluster manager
+        if (properties.isClusterManager() && Availability.NODE.equals(avail)) {
+            // change content type to JSON for serializing entity
+            final Map<String, String> headersToOverride = new HashMap<>();
+            headersToOverride.put("content-type", MediaType.APPLICATION_JSON);
+
+            // replicate the request
+            return clusterManager.applyRequest(HttpMethod.PUT, getAbsolutePath(), updateClientId(controllerServiceEntity), getHeaders(headersToOverride)).getResponse();
+        }
+        
+        // handle expects request (usually from the cluster manager)
+        final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER);
+        if (expects != null) {
+            serviceFacade.verifyUpdateControllerService(requestControllerServiceDTO);
+            return generateContinueResponse().build();
+        }
+
+        // update the controller service
+        final RevisionDTO revision = controllerServiceEntity.getRevision();
+        final ConfigurationSnapshot<ControllerServiceDTO> controllerResponse = serviceFacade.updateControllerService(
+                new Revision(revision.getVersion(), revision.getClientId()), requestControllerServiceDTO);
+
+        // get the results
+        final ControllerServiceDTO responseControllerServiceDTO = controllerResponse.getConfiguration();
+
+        // get the updated revision
+        final RevisionDTO updatedRevision = new RevisionDTO();
+        updatedRevision.setClientId(revision.getClientId());
+        updatedRevision.setVersion(controllerResponse.getVersion());
+
+        // build the response entity
+        final ControllerServiceEntity entity = new ControllerServiceEntity();
+        entity.setRevision(updatedRevision);
+        entity.setControllerService(populateRemainingControllerServiceContent(availability, responseControllerServiceDTO));
+
+        return clusterContext(generateOkResponse(entity)).build();
+    }
+
+    /**
+     * Removes the specified controller service.
+     *
+     * @param httpServletRequest
+     * @param version The revision is used to verify the client is working with
+     * the latest version of the flow.
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @param availability Whether the controller service is available on the NCM only (ncm) or on the 
+     * nodes only (node). If this instance is not clustered all services should use the node availability.
+     * @param id The id of the controller service to remove.
+     * @return A entity containing the client id and an updated revision.
+     */
+    @DELETE
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}/{id}")
+    @PreAuthorize("hasRole('ROLE_DFM')")
+    @TypeHint(ControllerServiceEntity.class)
+    public Response removeControllerService(
+            @Context HttpServletRequest httpServletRequest,
+            @QueryParam(VERSION) LongParameter version,
+            @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+            @PathParam("availability") String availability, @PathParam("id") String id) {
+
+        final Availability avail = parseAvailability(availability);
+        
+        // replicate if cluster manager
+        if (properties.isClusterManager() && Availability.NODE.equals(avail)) {
+            return clusterManager.applyRequest(HttpMethod.DELETE, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
+        }
+
+        // handle expects request (usually from the cluster manager)
+        final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER);
+        if (expects != null) {
+            serviceFacade.verifyDeleteControllerService(id);
+            return generateContinueResponse().build();
+        }
+
+        // determine the specified version
+        Long clientVersion = null;
+        if (version != null) {
+            clientVersion = version.getLong();
+        }
+
+        // delete the specified controller service
+        final ConfigurationSnapshot<Void> controllerResponse = serviceFacade.deleteControllerService(new Revision(clientVersion, clientId.getClientId()), id);
+
+        // get the updated revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+        revision.setVersion(controllerResponse.getVersion());
+
+        // build the response entity
+        final ControllerServiceEntity entity = new ControllerServiceEntity();
+        entity.setRevision(revision);
+
+        return clusterContext(generateOkResponse(entity)).build();
+    }
+
+    // setters
+    
+    public void setServiceFacade(NiFiServiceFacade serviceFacade) {
+        this.serviceFacade = serviceFacade;
+    }
+
+    public void setClusterManager(WebClusterManager clusterManager) {
+        this.clusterManager = clusterManager;
+    }
+
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FunnelResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FunnelResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FunnelResource.java
index 4406c2e..3492de2 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FunnelResource.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FunnelResource.java
@@ -244,7 +244,7 @@ public class FunnelResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         final FunnelEntity entity = new FunnelEntity();
@@ -408,7 +408,7 @@ public class FunnelResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         final FunnelEntity entity = new FunnelEntity();
@@ -465,7 +465,7 @@ public class FunnelResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO revision = new RevisionDTO();
         revision.setClientId(clientId.getClientId());
-        revision.setVersion(controllerResponse.getRevision());
+        revision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         final FunnelEntity entity = new FunnelEntity();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/HistoryResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/HistoryResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/HistoryResource.java
index 38806eb..0f60f52 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/HistoryResource.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/HistoryResource.java
@@ -37,7 +37,7 @@ import org.apache.nifi.web.api.dto.RevisionDTO;
 import org.apache.nifi.web.api.dto.action.ActionDTO;
 import org.apache.nifi.web.api.dto.action.HistoryDTO;
 import org.apache.nifi.web.api.dto.action.HistoryQueryDTO;
-import org.apache.nifi.web.api.entity.ProcessorHistoryEntity;
+import org.apache.nifi.web.api.entity.ComponentHistoryEntity;
 import org.codehaus.enunciate.jaxrs.TypeHint;
 import org.springframework.security.access.prepost.PreAuthorize;
 
@@ -245,7 +245,7 @@ public class HistoryResource extends ApplicationResource {
     @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
     @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
     @Path("/processors/{processorId}")
-    @TypeHint(ProcessorHistoryEntity.class)
+    @TypeHint(ComponentHistoryEntity.class)
     public Response getProcessorHistory(
             @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
             @PathParam("processorId") final String processorId) {
@@ -255,14 +255,76 @@ public class HistoryResource extends ApplicationResource {
         revision.setClientId(clientId.getClientId());
 
         // create the response entity
-        final ProcessorHistoryEntity entity = new ProcessorHistoryEntity();
+        final ComponentHistoryEntity entity = new ComponentHistoryEntity();
         entity.setRevision(revision);
-        entity.setProcessorHistory(serviceFacade.getProcessorHistory(processorId));
+        entity.setComponentHistory(serviceFacade.getComponentHistory(processorId));
 
         // generate the response
         return generateOkResponse(entity).build();
     }
+    
+    /**
+     * Gets the actions for the specified controller service.
+     *
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @param controllerServiceId The id of the controller service.
+     * @return An componentHistoryEntity.
+     */
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
+    @Path("/controller-services/{controllerServiceId}")
+    @TypeHint(ComponentHistoryEntity.class)
+    public Response getControllerServiceHistory(
+            @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+            @PathParam("controllerServiceId") final String controllerServiceId) {
 
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+
+        // create the response entity
+        final ComponentHistoryEntity entity = new ComponentHistoryEntity();
+        entity.setRevision(revision);
+        entity.setComponentHistory(serviceFacade.getComponentHistory(controllerServiceId));
+
+        // generate the response
+        return generateOkResponse(entity).build();
+    }
+    
+    /**
+     * Gets the actions for the specified reporting task.
+     *
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @param reportingTaskId The id of the reporting task.
+     * @return An componentHistoryEntity.
+     */
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
+    @Path("/reporting-tasks/{reportingTaskId}")
+    @TypeHint(ComponentHistoryEntity.class)
+    public Response getReportingTaskHistory(
+            @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+            @PathParam("reportingTaskId") final String reportingTaskId) {
+
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+
+        // create the response entity
+        final ComponentHistoryEntity entity = new ComponentHistoryEntity();
+        entity.setRevision(revision);
+        entity.setComponentHistory(serviceFacade.getComponentHistory(reportingTaskId));
+
+        // generate the response
+        return generateOkResponse(entity).build();
+    }
+    
     /* setters */
     public void setServiceFacade(NiFiServiceFacade serviceFacade) {
         this.serviceFacade = serviceFacade;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/InputPortResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/InputPortResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/InputPortResource.java
index 58c3c9e..f3a6326 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/InputPortResource.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/InputPortResource.java
@@ -251,7 +251,7 @@ public class InputPortResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         final InputPortEntity entity = new InputPortEntity();
@@ -446,7 +446,7 @@ public class InputPortResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         final InputPortEntity entity = new InputPortEntity();
@@ -503,7 +503,7 @@ public class InputPortResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO revision = new RevisionDTO();
         revision.setClientId(clientId.getClientId());
-        revision.setVersion(controllerResponse.getRevision());
+        revision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         final InputPortEntity entity = new InputPortEntity();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/LabelResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/LabelResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/LabelResource.java
index 9a61cfc..6435671 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/LabelResource.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/LabelResource.java
@@ -260,7 +260,7 @@ public class LabelResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         final LabelEntity entity = new LabelEntity();
@@ -463,7 +463,7 @@ public class LabelResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         final LabelEntity entity = new LabelEntity();
@@ -519,7 +519,7 @@ public class LabelResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO revision = new RevisionDTO();
         revision.setClientId(clientId.getClientId());
-        revision.setVersion(controllerResponse.getRevision());
+        revision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         final LabelEntity entity = new LabelEntity();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/OutputPortResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/OutputPortResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/OutputPortResource.java
index 224ab18..a9dce5f 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/OutputPortResource.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/OutputPortResource.java
@@ -251,7 +251,7 @@ public class OutputPortResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         final OutputPortEntity entity = new OutputPortEntity();
@@ -447,7 +447,7 @@ public class OutputPortResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         final OutputPortEntity entity = new OutputPortEntity();
@@ -504,7 +504,7 @@ public class OutputPortResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO revision = new RevisionDTO();
         revision.setClientId(clientId.getClientId());
-        revision.setVersion(controllerResponse.getRevision());
+        revision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         final OutputPortEntity entity = new OutputPortEntity();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
index 6439bda..1bf3f77 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
@@ -287,7 +287,7 @@ public class ProcessGroupResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO revision = new RevisionDTO();
         revision.setClientId(clientId.getClientId());
-        revision.setVersion(controllerResponse.getRevision());
+        revision.setVersion(controllerResponse.getVersion());
 
         // create the response entity
         final ProcessGroupEntity processGroupEntity = new ProcessGroupEntity();
@@ -365,7 +365,7 @@ public class ProcessGroupResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO revision = new RevisionDTO();
         revision.setClientId(clientId.getClientId());
-        revision.setVersion(controllerResponse.getRevision());
+        revision.setVersion(controllerResponse.getVersion());
 
         // create the response entity
         final FlowSnippetEntity entity = new FlowSnippetEntity();
@@ -441,7 +441,7 @@ public class ProcessGroupResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO revision = new RevisionDTO();
         revision.setClientId(clientId.getClientId());
-        revision.setVersion(response.getRevision());
+        revision.setVersion(response.getVersion());
 
         // create the response entity
         final FlowSnippetEntity entity = new FlowSnippetEntity();
@@ -559,7 +559,7 @@ public class ProcessGroupResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(response.getRevision());
+        updatedRevision.setVersion(response.getVersion());
 
         // create the response entity
         final ProcessGroupEntity entity = new ProcessGroupEntity();
@@ -616,7 +616,7 @@ public class ProcessGroupResource extends ApplicationResource {
         // create the revision
         final RevisionDTO revision = new RevisionDTO();
         revision.setClientId(clientId.getClientId());
-        revision.setVersion(controllerResponse.getRevision());
+        revision.setVersion(controllerResponse.getVersion());
 
         // create the response entity
         final ProcessGroupEntity processGroupEntity = new ProcessGroupEntity();
@@ -795,7 +795,7 @@ public class ProcessGroupResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // create the response entity
         final ProcessGroupEntity entity = new ProcessGroupEntity();
@@ -931,7 +931,7 @@ public class ProcessGroupResource extends ApplicationResource {
         // create the revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(response.getRevision());
+        updatedRevision.setVersion(response.getVersion());
 
         // create the response entity
         final ProcessGroupEntity entity = new ProcessGroupEntity();
@@ -989,7 +989,7 @@ public class ProcessGroupResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO revision = new RevisionDTO();
         revision.setClientId(clientId.getClientId());
-        revision.setVersion(controllerResponse.getRevision());
+        revision.setVersion(controllerResponse.getVersion());
 
         // create the response entity
         final ProcessGroupEntity entity = new ProcessGroupEntity();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
index b11c40a..31ab10b 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
@@ -70,6 +70,11 @@ import org.apache.nifi.web.api.request.IntegerParameter;
 import org.apache.nifi.web.api.request.LongParameter;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.ui.extension.UiExtension;
+import org.apache.nifi.ui.extension.UiExtensionMapping;
+import org.apache.nifi.web.UiExtensionType;
+import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
+import org.apache.nifi.web.api.entity.PropertyDescriptorEntity;
 import org.codehaus.enunciate.jaxrs.TypeHint;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -118,9 +123,21 @@ public class ProcessorResource extends ApplicationResource {
         // get the config details and see if there is a custom ui for this processor type
         ProcessorConfigDTO config = processor.getConfig();
         if (config != null) {
+            // consider legacy custom ui fist
             String customUiUrl = servletContext.getInitParameter(processor.getType());
             if (StringUtils.isNotBlank(customUiUrl)) {
                 config.setCustomUiUrl(customUiUrl);
+            } else {
+                // see if this processor has any ui extensions
+                final UiExtensionMapping uiExtensionMapping = (UiExtensionMapping) servletContext.getAttribute("nifi-ui-extensions");
+                if (uiExtensionMapping.hasUiExtension(processor.getType())) {
+                    final List<UiExtension> uiExtensions = uiExtensionMapping.getUiExtension(processor.getType());
+                    for (final UiExtension uiExtension : uiExtensions) {
+                        if (UiExtensionType.ProcessorConfiguration.equals(uiExtension.getExtensionType())) {
+                            config.setCustomUiUrl(uiExtension.getContextPath() + "/configure");
+                        }
+                    }
+                }
             }
         }
 
@@ -245,6 +262,10 @@ public class ProcessorResource extends ApplicationResource {
         if (processorEntity.getProcessor().getId() != null) {
             throw new IllegalArgumentException("Processor ID cannot be specified.");
         }
+        
+        if (StringUtils.isBlank(processorEntity.getProcessor().getType())) {
+            throw new IllegalArgumentException("The type of processor to create must be specified.");
+        }
 
         // if cluster manager, convert POST to PUT (to maintain same ID across nodes) and replicate
         if (properties.isClusterManager()) {
@@ -288,7 +309,7 @@ public class ProcessorResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // generate the response entity
         final ProcessorEntity entity = new ProcessorEntity();
@@ -373,6 +394,51 @@ public class ProcessorResource extends ApplicationResource {
         // generate the response
         return clusterContext(generateOkResponse(entity)).build();
     }
+    
+    /**
+     * Returns the descriptor for the specified property.
+     * 
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @param id The id of the processor
+     * @param propertyName The property
+     * @return a propertyDescriptorEntity
+     */
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{id}/descriptors")
+    @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
+    @TypeHint(PropertyDescriptorEntity.class)
+    public Response getPropertyDescriptor(
+            @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, 
+            @PathParam("id") String id, @QueryParam("propertyName") String propertyName) {
+        
+        // ensure the property name is specified
+        if (propertyName == null) {
+            throw new IllegalArgumentException("The property name must be specified.");
+        }
+        
+        // replicate if cluster manager
+        if (properties.isClusterManager()) {
+            return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
+        }
+        
+        // get the property descriptor
+        final PropertyDescriptorDTO descriptor = serviceFacade.getProcessorPropertyDescriptor(groupId, id, propertyName);
+        
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+        
+        // generate the response entity
+        final PropertyDescriptorEntity entity = new PropertyDescriptorEntity();
+        entity.setRevision(revision);
+        entity.setPropertyDescriptor(descriptor);
+        
+        // generate the response
+        return clusterContext(generateOkResponse(entity)).build();
+    }
 
     /**
      * Updates the specified processor with the specified values.
@@ -607,7 +673,7 @@ public class ProcessorResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // generate the response entity
         final ProcessorEntity entity = new ProcessorEntity();
@@ -664,7 +730,7 @@ public class ProcessorResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(clientId.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // generate the response entity
         final ProcessorEntity entity = new ProcessorEntity();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java
index 27fa292..4e15c36 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java
@@ -396,7 +396,7 @@ public class RemoteProcessGroupResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         final RemoteProcessGroupEntity entity = new RemoteProcessGroupEntity();
@@ -452,7 +452,7 @@ public class RemoteProcessGroupResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO revision = new RevisionDTO();
         revision.setClientId(clientId.getClientId());
-        revision.setVersion(controllerResponse.getRevision());
+        revision.setVersion(controllerResponse.getVersion());
 
         // create the response entity
         final RemoteProcessGroupEntity entity = new RemoteProcessGroupEntity();
@@ -586,7 +586,7 @@ public class RemoteProcessGroupResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         final RemoteProcessGroupPortEntity entity = new RemoteProcessGroupPortEntity();
@@ -720,7 +720,7 @@ public class RemoteProcessGroupResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         RemoteProcessGroupPortEntity entity = new RemoteProcessGroupPortEntity();
@@ -890,7 +890,7 @@ public class RemoteProcessGroupResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         final RemoteProcessGroupEntity entity = new RemoteProcessGroupEntity();


[61/62] [abbrv] incubator-nifi git commit: Merge branch 'develop' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into develop

Posted by ma...@apache.org.
Merge branch 'develop' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into develop


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/c201aa1d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/c201aa1d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/c201aa1d

Branch: refs/heads/NIFI-25
Commit: c201aa1d088e6e041fce8a25ca974c5759e5d61c
Parents: e18c0a7 c1959b3
Author: Mark Payne <ma...@hotmail.com>
Authored: Fri Apr 10 09:39:20 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Fri Apr 10 09:39:20 2015 -0400

----------------------------------------------------------------------
 .../java/org/apache/nifi/util/EscapeUtils.java  |  42 +++
 .../nifi/web/api/dto/DocumentedTypeDTO.java     |  14 -
 .../nifi/web/api/dto/PropertyDescriptorDTO.java |   9 +-
 .../org/apache/nifi/web/NiFiServiceFacade.java  |   3 +-
 .../nifi/web/StandardNiFiContentAccess.java     |  10 +-
 .../nifi/web/StandardNiFiServiceFacade.java     |   4 +-
 .../apache/nifi/web/api/ControllerResource.java |   7 +-
 .../web/api/config/NotFoundExceptionMapper.java |  48 +++
 .../org/apache/nifi/web/api/dto/DtoFactory.java | 135 +-------
 .../nifi/web/controller/ControllerFacade.java   |  43 ++-
 .../org/apache/nifi/web/util/SnippetUtils.java  |   6 +-
 .../src/main/resources/nifi-web-api-context.xml |   1 +
 .../nifi/web/ContentViewerController.java       |  18 +-
 .../src/main/webapp/WEB-INF/jsp/header.jsp      |  20 +-
 .../src/main/webapp/js/hexview/hexview.js       |   4 +-
 .../nifi-web/nifi-web-docs/pom.xml              |   6 +
 .../nifi/web/docs/DocumentationController.java  |   5 +-
 .../main/webapp/WEB-INF/jsp/documentation.jsp   |  20 +-
 .../src/main/webapp/js/application.js           |   7 +-
 .../nifi-framework/nifi-web/nifi-web-ui/pom.xml |   6 +
 .../main/webapp/WEB-INF/pages/message-page.jsp  |   6 +-
 .../src/main/webapp/css/settings.css            |  15 -
 .../propertytable/jquery.propertytable.css      |  43 +++
 .../propertytable/jquery.propertytable.js       | 337 ++++++++++++++++---
 .../js/nf/canvas/nf-controller-service.js       | 186 +++++-----
 .../js/nf/canvas/nf-processor-configuration.js  | 136 +++++---
 .../webapp/js/nf/canvas/nf-reporting-task.js    | 135 +++++---
 .../src/main/webapp/js/nf/canvas/nf-settings.js | 227 ++-----------
 .../nifi-standard-content-viewer/pom.xml        |   5 +
 .../src/main/webapp/WEB-INF/jsp/codemirror.jsp  |   7 +-
 .../nifi-update-attribute-ui/pom.xml            |   5 +
 .../src/main/webapp/WEB-INF/jsp/worksheet.jsp   |   8 +-
 32 files changed, 854 insertions(+), 664 deletions(-)
----------------------------------------------------------------------



[14/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java
new file mode 100644
index 0000000..8d51a58
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebConfigurationContext.java
@@ -0,0 +1,736 @@
+/*
+ * 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.web;
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLEncoder;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.apache.nifi.action.Action;
+import org.apache.nifi.action.Component;
+import org.apache.nifi.action.Operation;
+import org.apache.nifi.action.component.details.ExtensionDetails;
+import org.apache.nifi.action.details.ConfigureDetails;
+import org.apache.nifi.admin.service.AuditService;
+import org.apache.nifi.cluster.manager.NodeResponse;
+import org.apache.nifi.cluster.manager.impl.WebClusterManager;
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.web.security.user.NiFiUserDetails;
+import org.apache.nifi.web.security.user.NiFiUserUtils;
+import org.apache.nifi.user.NiFiUser;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.api.dto.ProcessorConfigDTO;
+import org.apache.nifi.web.api.dto.ProcessorDTO;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.entity.ProcessorEntity;
+import org.apache.nifi.web.util.WebUtils;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import com.sun.jersey.core.util.MultivaluedMapImpl;
+import org.apache.nifi.controller.ControllerServiceLookup;
+import org.apache.nifi.controller.reporting.ReportingTaskProvider;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
+import org.apache.nifi.web.api.dto.ReportingTaskDTO;
+import org.apache.nifi.web.api.entity.ControllerServiceEntity;
+import org.apache.nifi.web.api.entity.ReportingTaskEntity;
+import org.apache.nifi.web.util.ClientResponseUtils;
+
+/**
+ * Implements the NiFiWebConfigurationContext interface to support a context in both
+ * standalone and clustered environments.
+ */
+public class StandardNiFiWebConfigurationContext implements NiFiWebConfigurationContext {
+
+    private static final Logger logger = LoggerFactory.getLogger(StandardNiFiWebConfigurationContext.class);
+    public static final String CLIENT_ID_PARAM = "clientId";
+    public static final String REVISION_PARAM = "revision";
+    public static final String VERBOSE_PARAM = "verbose";
+
+    private NiFiProperties properties;
+    private NiFiServiceFacade serviceFacade;
+    private WebClusterManager clusterManager;
+    private ControllerServiceLookup controllerServiceLookup;
+    private ReportingTaskProvider reportingTaskProvider;
+    private AuditService auditService;
+
+    @Override
+    public ControllerService getControllerService(String serviceIdentifier) {
+        return controllerServiceLookup.getControllerService(serviceIdentifier);
+    }
+
+    @Override
+    @PreAuthorize("hasAnyRole('ROLE_DFM')")
+    public void saveActions(final NiFiWebRequestContext requestContext, final Collection<ConfigurationAction> configurationActions) {
+        Objects.requireNonNull(configurationActions, "Actions cannot be null.");
+
+        // ensure the path could be 
+        if (requestContext.getExtensionType() == null) {
+            throw new IllegalArgumentException("The UI extension type must be specified.");
+        }
+        
+        Component componentType = null;
+        switch (requestContext.getExtensionType()) {
+            case ProcessorConfiguration:
+                componentType = Component.Processor;
+                break;
+            case ControllerServiceConfiguration:
+                componentType = Component.ControllerService;
+                break;
+            case ReportingTaskConfiguration:
+                componentType = Component.ReportingTask;
+                break;
+        }
+
+        if (componentType == null) {
+            throw new IllegalArgumentException("UI extension type must support Processor, ControllerService, or ReportingTask configuration.");
+        }
+        
+        // - when running standalone or cluster ncm - actions from custom UIs are stored locally
+        // - clustered nodes do not serve custom UIs directly to users so they should never be invoking this method
+        final Date now = new Date();
+        final Collection<Action> actions = new HashSet<>(configurationActions.size());
+        for (final ConfigurationAction configurationAction : configurationActions) {
+            final ExtensionDetails extensionDetails = new ExtensionDetails();
+            extensionDetails.setType(configurationAction.getType());
+
+            final ConfigureDetails configureDetails = new ConfigureDetails();
+            configureDetails.setName(configurationAction.getName());
+            configureDetails.setPreviousValue(configurationAction.getPreviousValue());
+            configureDetails.setValue(configurationAction.getValue());
+
+            final Action action = new Action();
+            action.setTimestamp(now);
+            action.setSourceId(configurationAction.getId());
+            action.setSourceName(configurationAction.getName());
+            action.setSourceType(componentType);
+            action.setOperation(Operation.Configure);
+            action.setUserDn(getCurrentUserDn());
+            action.setUserName(getCurrentUserName());
+            action.setComponentDetails(extensionDetails);
+            action.setActionDetails(configureDetails);
+            actions.add(action);
+        }
+
+        if (!actions.isEmpty()) {
+            try {
+                // record the operations
+                auditService.addActions(actions);
+            } catch (Throwable t) {
+                logger.warn("Unable to record actions: " + t.getMessage());
+                if (logger.isDebugEnabled()) {
+                    logger.warn(StringUtils.EMPTY, t);
+                }
+            }
+        }
+    }
+
+    @Override
+    public String getCurrentUserDn() {
+        String userDn = NiFiUser.ANONYMOUS_USER_DN;
+
+        final NiFiUser user = NiFiUserUtils.getNiFiUser();
+        if (user != null) {
+            userDn = user.getDn();
+        }
+
+        return userDn;
+    }
+
+    @Override
+    public String getCurrentUserName() {
+        String userName = NiFiUser.ANONYMOUS_USER_DN;
+
+        final NiFiUser user = NiFiUserUtils.getNiFiUser();
+        if (user != null) {
+            userName = user.getUserName();
+        }
+
+        return userName;
+    }
+
+    @Override
+    public ComponentDetails getComponentDetails(final NiFiWebRequestContext requestContext) throws ResourceNotFoundException, ClusterRequestException {
+        final String id = requestContext.getId();
+
+        if (StringUtils.isBlank(id)) {
+            throw new ResourceNotFoundException(String.format("Configuration request context config did not have a component ID."));
+        }
+
+        // ensure the path could be 
+        if (requestContext.getExtensionType() == null) {
+            throw new IllegalArgumentException("The UI extension type must be specified.");
+        }
+        
+        // get the component facade for interacting directly with that type of object
+        ComponentFacade componentFacade = null;
+        switch (requestContext.getExtensionType()) {
+            case ProcessorConfiguration:
+                componentFacade = new ProcessorFacade();
+                break;
+            case ControllerServiceConfiguration: 
+                componentFacade = new ControllerServiceFacade();
+                break;
+            case ReportingTaskConfiguration:
+                componentFacade = new ReportingTaskFacade();
+                break;
+        }
+        
+        if (componentFacade == null) {
+            throw new IllegalArgumentException("UI extension type must support Processor, ControllerService, or ReportingTask configuration.");
+        }
+        
+        return componentFacade.getComponentDetails(requestContext);
+    }
+
+    @Override
+    @PreAuthorize("hasAnyRole('ROLE_DFM')")
+    public ComponentDetails setAnnotationData(final NiFiWebConfigurationRequestContext requestContext, final String annotationData)
+            throws ResourceNotFoundException, InvalidRevisionException, ClusterRequestException {
+
+        final String id = requestContext.getId();
+
+        if (StringUtils.isBlank(id)) {
+            throw new ResourceNotFoundException(String.format("Configuration request context did not have a component ID."));
+        }
+        
+        // ensure the path could be 
+        if (requestContext.getExtensionType() == null) {
+            throw new IllegalArgumentException("The UI extension type must be specified.");
+        }
+
+        // get the component facade for interacting directly with that type of object
+        ComponentFacade componentFacade = null;
+        switch (requestContext.getExtensionType()) {
+            case ProcessorConfiguration:
+                componentFacade = new ProcessorFacade();
+                break;
+            case ControllerServiceConfiguration: 
+                componentFacade = new ControllerServiceFacade();
+                break;
+            case ReportingTaskConfiguration:
+                componentFacade = new ReportingTaskFacade();
+                break;
+        }
+        
+        if (componentFacade == null) {
+            throw new IllegalArgumentException("UI extension type must support Processor, ControllerService, or ReportingTask configuration.");
+        }
+        
+        return componentFacade.setAnnotationData(requestContext, annotationData);
+    }
+
+    /**
+     * Facade over accessing different types of NiFi components.
+     */
+    private interface ComponentFacade {
+        /**
+         * Gets the component details using the specified request context.
+         * 
+         * @param requestContext
+         * @return 
+         */
+        ComponentDetails getComponentDetails(NiFiWebRequestContext requestContext);
+        
+        /**
+         * Sets the annotation data using the specified request context.
+         * 
+         * @param requestContext
+         * @param annotationData
+         * @return 
+         */
+        ComponentDetails setAnnotationData(NiFiWebConfigurationRequestContext requestContext, String annotationData);
+    }
+    
+    /**
+     * Interprets the request/response with the underlying Processor model.
+     */
+    private class ProcessorFacade implements ComponentFacade {
+        @Override
+        public ComponentDetails getComponentDetails(final NiFiWebRequestContext requestContext) {
+            final String id = requestContext.getId();
+            
+            final ProcessorDTO processor;
+            if (properties.isClusterManager()) {
+                // create the request URL
+                URI requestUrl;
+                try {
+                    String path = "/nifi-api/cluster/processors/" + URLEncoder.encode(id, "UTF-8");
+                    requestUrl = new URI(requestContext.getScheme(), null, "localhost", 0, path, null, null);
+                } catch (final URISyntaxException | UnsupportedEncodingException use) {
+                    throw new ClusterRequestException(use);
+                }
+
+                // set the request parameters
+                MultivaluedMap<String, String> parameters = new MultivaluedMapImpl();
+                parameters.add(VERBOSE_PARAM, "true");
+
+                // replicate request
+                NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, requestUrl, parameters, getHeaders(requestContext));
+
+                // check for issues replicating request
+                checkResponse(nodeResponse, id);
+
+                // return processor
+                ProcessorEntity entity = (ProcessorEntity) nodeResponse.getUpdatedEntity();
+                if (entity == null) {
+                    entity = nodeResponse.getClientResponse().getEntity(ProcessorEntity.class);
+                }
+                processor = entity.getProcessor();
+            } else {
+                processor = serviceFacade.getProcessor(id);
+            }
+
+            // return the processor info
+            return getComponentConfiguration(processor);
+        }
+
+        @Override
+        public ComponentDetails setAnnotationData(final NiFiWebConfigurationRequestContext requestContext, final String annotationData) {
+            final Revision revision = requestContext.getRevision();
+            final String id = requestContext.getId();
+            
+            final ProcessorDTO processor;
+            if (properties.isClusterManager()) {
+                // create the request URL
+                URI requestUrl;
+                try {
+                    String path = "/nifi-api/cluster/processors/" + URLEncoder.encode(id, "UTF-8");
+                    requestUrl = new URI(requestContext.getScheme(), null, "localhost", 0, path, null, null);
+                } catch (final URISyntaxException | UnsupportedEncodingException use) {
+                    throw new ClusterRequestException(use);
+                }
+
+                // create the revision
+                RevisionDTO revisionDto = new RevisionDTO();
+                revisionDto.setClientId(revision.getClientId());
+                revisionDto.setVersion(revision.getVersion());
+
+                // create the processor entity
+                ProcessorEntity processorEntity = new ProcessorEntity();
+                processorEntity.setRevision(revisionDto);
+
+                // create the processor dto
+                ProcessorDTO processorDto = new ProcessorDTO();
+                processorEntity.setProcessor(processorDto);
+                processorDto.setId(id);
+
+                // create the processor configuration with the given annotation data
+                ProcessorConfigDTO configDto = new ProcessorConfigDTO();
+                processorDto.setConfig(configDto);
+                configDto.setAnnotationData(annotationData);
+
+                // set the content type to json
+                final Map<String, String> headers = getHeaders(requestContext);
+                headers.put("Content-Type", "application/json");
+
+                // replicate request
+                NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.PUT, requestUrl, processorEntity, headers);
+
+                // check for issues replicating request
+                checkResponse(nodeResponse, id);
+                
+                // return processor
+                ProcessorEntity entity = (ProcessorEntity) nodeResponse.getUpdatedEntity();
+                if (entity == null) {
+                    entity = nodeResponse.getClientResponse().getEntity(ProcessorEntity.class);
+                }
+                processor = entity.getProcessor();
+            } else {
+                final ConfigurationSnapshot<ProcessorDTO> response = serviceFacade.setProcessorAnnotationData(revision, id, annotationData);
+                processor = response.getConfiguration();
+            }
+            
+            // return the processor info
+            return getComponentConfiguration(processor);
+        }
+        
+        private ComponentDetails getComponentConfiguration(final ProcessorDTO processor) {
+            final ProcessorConfigDTO processorConfig = processor.getConfig();
+            return new ComponentDetails.Builder()
+                    .id(processor.getId())
+                    .name(processor.getName())
+                    .type(processor.getType())
+                    .state(processor.getState())
+                    .annotationData(processorConfig.getAnnotationData())
+                    .properties(processorConfig.getProperties())
+                    .validateErrors(processor.getValidationErrors()).build();
+        }
+    }
+    
+    /**
+     * Interprets the request/response with the underlying ControllerService model.
+     */
+    private class ControllerServiceFacade implements ComponentFacade {
+        @Override
+        public ComponentDetails getComponentDetails(final NiFiWebRequestContext requestContext) {
+            final String id = requestContext.getId();
+            final ControllerServiceDTO controllerService;
+            
+            // if the lookup has the service that means we are either a node or
+            // the ncm and the service is available there only
+            if (controllerServiceLookup.getControllerService(id) != null) {
+                controllerService = serviceFacade.getControllerService(id);
+            } else {
+                // if this is a standalone instance the service should have been found above... there should
+                // no cluster to replicate the request to
+                if (!properties.isClusterManager()) {
+                    throw new ResourceNotFoundException(String.format("Controller service[%s] could not be found on this NiFi.", id));
+                }
+                
+                // create the request URL
+                URI requestUrl;
+                try {
+                    String path = "/nifi-api/controller/controller-services/node/" + URLEncoder.encode(id, "UTF-8");
+                    requestUrl = new URI(requestContext.getScheme(), null, "localhost", 0, path, null, null);
+                } catch (final URISyntaxException | UnsupportedEncodingException use) {
+                    throw new ClusterRequestException(use);
+                }
+
+                // set the request parameters
+                MultivaluedMap<String, String> parameters = new MultivaluedMapImpl();
+
+                // replicate request
+                NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, requestUrl, parameters, getHeaders(requestContext));
+
+                // check for issues replicating request
+                checkResponse(nodeResponse, id);
+
+                // return controller service
+                ControllerServiceEntity entity = (ControllerServiceEntity) nodeResponse.getUpdatedEntity();
+                if (entity == null) {
+                    entity = nodeResponse.getClientResponse().getEntity(ControllerServiceEntity.class);
+                }
+                controllerService = entity.getControllerService();
+            }
+
+            // return the controller service info
+            return getComponentConfiguration(controllerService);
+        }
+
+        @Override
+        public ComponentDetails setAnnotationData(final NiFiWebConfigurationRequestContext requestContext, final String annotationData) {
+            final Revision revision = requestContext.getRevision();
+            final String id = requestContext.getId();
+            
+            final ControllerServiceDTO controllerService;
+            if (controllerServiceLookup.getControllerService(id) != null) {
+                final ControllerServiceDTO controllerServiceDto = new ControllerServiceDTO();
+                controllerServiceDto.setId(id);
+                controllerServiceDto.setAnnotationData(annotationData);
+                
+                final ConfigurationSnapshot<ControllerServiceDTO> response = serviceFacade.updateControllerService(revision, controllerServiceDto);
+                controllerService = response.getConfiguration();
+            } else {
+                // if this is a standalone instance the service should have been found above... there should
+                // no cluster to replicate the request to
+                if (!properties.isClusterManager()) {
+                    throw new ResourceNotFoundException(String.format("Controller service[%s] could not be found on this NiFi.", id));
+                }
+                
+                // since this PUT request can be interpreted as a request to create a controller service
+                // we need to be sure that this service exists on the node before the request is replicated.
+                // this is done by attempting to get the details. if the service doesn't exist it will
+                // throw a ResourceNotFoundException
+                getComponentDetails(requestContext);
+                
+                // create the request URL
+                URI requestUrl;
+                try {
+                    String path = "/nifi-api/controller/controller-services/node/" + URLEncoder.encode(id, "UTF-8");
+                    requestUrl = new URI(requestContext.getScheme(), null, "localhost", 0, path, null, null);
+                } catch (final URISyntaxException | UnsupportedEncodingException use) {
+                    throw new ClusterRequestException(use);
+                }
+
+                // create the revision
+                RevisionDTO revisionDto = new RevisionDTO();
+                revisionDto.setClientId(revision.getClientId());
+                revisionDto.setVersion(revision.getVersion());
+
+                // create the controller service entity
+                ControllerServiceEntity controllerServiceEntity = new ControllerServiceEntity();
+                controllerServiceEntity.setRevision(revisionDto);
+
+                // create the controller service dto
+                ControllerServiceDTO controllerServiceDto = new ControllerServiceDTO();
+                controllerServiceEntity.setControllerService(controllerServiceDto);
+                controllerServiceDto.setId(id);
+                controllerServiceDto.setAnnotationData(annotationData);
+
+                // set the content type to json
+                final Map<String, String> headers = getHeaders(requestContext);
+                headers.put("Content-Type", "application/json");
+
+                // replicate request
+                NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.PUT, requestUrl, controllerServiceEntity, headers);
+
+                // check for issues replicating request
+                checkResponse(nodeResponse, id);
+                
+                // return controller service
+                ControllerServiceEntity entity = (ControllerServiceEntity) nodeResponse.getUpdatedEntity();
+                if (entity == null) {
+                    entity = nodeResponse.getClientResponse().getEntity(ControllerServiceEntity.class);
+                }
+                controllerService = entity.getControllerService();
+            }
+            
+            // return the controller service info
+            return getComponentConfiguration(controllerService);
+        }
+        
+        private ComponentDetails getComponentConfiguration(final ControllerServiceDTO controllerService) {
+            return new ComponentDetails.Builder()
+                    .id(controllerService.getId())
+                    .name(controllerService.getName())
+                    .type(controllerService.getType())
+                    .state(controllerService.getState())
+                    .annotationData(controllerService.getAnnotationData())
+                    .properties(controllerService.getProperties())
+                    .validateErrors(controllerService.getValidationErrors()).build();
+        }
+    }
+    
+    /**
+     * Interprets the request/response with the underlying ControllerService model.
+     */
+    private class ReportingTaskFacade implements ComponentFacade {
+        @Override
+        public ComponentDetails getComponentDetails(final NiFiWebRequestContext requestContext) {
+            final String id = requestContext.getId();
+            final ReportingTaskDTO reportingTask;
+            
+            // if the provider has the service that means we are either a node or
+            // the ncm and the service is available there only
+            if (reportingTaskProvider.getReportingTaskNode(id) != null) {
+                reportingTask = serviceFacade.getReportingTask(id);
+            } else {
+                // if this is a standalone instance the task should have been found above... there should
+                // no cluster to replicate the request to
+                if (!properties.isClusterManager()) {
+                    throw new ResourceNotFoundException(String.format("Reporting task[%s] could not be found on this NiFi.", id));
+                }
+                
+                // create the request URL
+                URI requestUrl;
+                try {
+                    String path = "/nifi-api/controller/reporting-tasks/node/" + URLEncoder.encode(id, "UTF-8");
+                    requestUrl = new URI(requestContext.getScheme(), null, "localhost", 0, path, null, null);
+                } catch (final URISyntaxException | UnsupportedEncodingException use) {
+                    throw new ClusterRequestException(use);
+                }
+
+                // set the request parameters
+                MultivaluedMap<String, String> parameters = new MultivaluedMapImpl();
+
+                // replicate request
+                NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, requestUrl, parameters, getHeaders(requestContext));
+
+                // check for issues replicating request
+                checkResponse(nodeResponse, id);
+
+                // return reporting task
+                ReportingTaskEntity entity = (ReportingTaskEntity) nodeResponse.getUpdatedEntity();
+                if (entity == null) {
+                    entity = nodeResponse.getClientResponse().getEntity(ReportingTaskEntity.class);
+                }
+                reportingTask = entity.getReportingTask();
+            }
+
+            // return the reporting task info
+            return getComponentConfiguration(reportingTask);
+        }
+
+        @Override
+        public ComponentDetails setAnnotationData(final NiFiWebConfigurationRequestContext requestContext, final String annotationData) {
+            final Revision revision = requestContext.getRevision();
+            final String id = requestContext.getId();
+            
+            final ReportingTaskDTO reportingTask;
+            if (reportingTaskProvider.getReportingTaskNode(id) != null) {
+                final ReportingTaskDTO reportingTaskDto = new ReportingTaskDTO();
+                reportingTaskDto.setId(id);
+                reportingTaskDto.setAnnotationData(annotationData);
+                
+                final ConfigurationSnapshot<ReportingTaskDTO> response = serviceFacade.updateReportingTask(revision, reportingTaskDto);
+                reportingTask = response.getConfiguration();
+            } else {
+                // if this is a standalone instance the task should have been found above... there should
+                // no cluster to replicate the request to
+                if (!properties.isClusterManager()) {
+                    throw new ResourceNotFoundException(String.format("Reporting task[%s] could not be found on this NiFi.", id));
+                }
+                
+                // since this PUT request can be interpreted as a request to create a reporting task
+                // we need to be sure that this task exists on the node before the request is replicated.
+                // this is done by attempting to get the details. if the service doesn't exist it will
+                // throw a ResourceNotFoundException
+                getComponentDetails(requestContext);
+                
+                // create the request URL
+                URI requestUrl;
+                try {
+                    String path = "/nifi-api/controller/reporting-tasks/node/" + URLEncoder.encode(id, "UTF-8");
+                    requestUrl = new URI(requestContext.getScheme(), null, "localhost", 0, path, null, null);
+                } catch (final URISyntaxException | UnsupportedEncodingException use) {
+                    throw new ClusterRequestException(use);
+                }
+
+                // create the revision
+                RevisionDTO revisionDto = new RevisionDTO();
+                revisionDto.setClientId(revision.getClientId());
+                revisionDto.setVersion(revision.getVersion());
+
+                // create the reporting task entity
+                ReportingTaskEntity reportingTaskEntity = new ReportingTaskEntity();
+                reportingTaskEntity.setRevision(revisionDto);
+
+                // create the reporting task dto
+                ReportingTaskDTO reportingTaskDto = new ReportingTaskDTO();
+                reportingTaskEntity.setReportingTask(reportingTaskDto);
+                reportingTaskDto.setId(id);
+                reportingTaskDto.setAnnotationData(annotationData);
+
+                // set the content type to json
+                final Map<String, String> headers = getHeaders(requestContext);
+                headers.put("Content-Type", "application/json");
+
+                // replicate request
+                NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.PUT, requestUrl, reportingTaskEntity, headers);
+
+                // check for issues replicating request
+                checkResponse(nodeResponse, id);
+                
+                // return reporting task
+                ReportingTaskEntity entity = (ReportingTaskEntity) nodeResponse.getUpdatedEntity();
+                if (entity == null) {
+                    entity = nodeResponse.getClientResponse().getEntity(ReportingTaskEntity.class);
+                }
+                reportingTask = entity.getReportingTask();
+            }
+            
+            // return the processor info
+            return getComponentConfiguration(reportingTask);
+        }
+        
+        private ComponentDetails getComponentConfiguration(final ReportingTaskDTO reportingTask) {
+            return new ComponentDetails.Builder()
+                    .id(reportingTask.getId())
+                    .name(reportingTask.getName())
+                    .type(reportingTask.getType())
+                    .state(reportingTask.getState())
+                    .annotationData(reportingTask.getAnnotationData())
+                    .properties(reportingTask.getProperties())
+                    .validateErrors(reportingTask.getValidationErrors()).build();
+        }
+    }
+    
+    /**
+     * Gets the headers for the request to replicate to each node while
+     * clustered.
+     *
+     * @param config
+     * @return
+     */
+    private Map<String, String> getHeaders(final NiFiWebRequestContext config) {
+        final Map<String, String> headers = new HashMap<>();
+        headers.put("Accept", "application/json,application/xml");
+        if (StringUtils.isNotBlank(config.getProxiedEntitiesChain())) {
+            headers.put("X-ProxiedEntitiesChain", config.getProxiedEntitiesChain());
+        }
+
+        // add the user's authorities (if any) to the headers
+        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (authentication != null) {
+            final Object userDetailsObj = authentication.getPrincipal();
+            if (userDetailsObj instanceof NiFiUserDetails) {
+                // serialize user details object
+                final String hexEncodedUserDetails = WebUtils.serializeObjectToHex((Serializable) userDetailsObj);
+
+                // put serialized user details in header
+                headers.put("X-ProxiedEntityUserDetails", hexEncodedUserDetails);
+            }
+        }
+        return headers;
+    }
+    
+    /**
+     * Checks the specified response and drains the stream appropriately.
+     * 
+     * @param nodeResponse
+     * @param revision
+     * @param id 
+     */
+    private void checkResponse(final NodeResponse nodeResponse, final String id) {
+        if (nodeResponse.hasThrowable()) {
+            ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse());
+            throw new ClusterRequestException(nodeResponse.getThrowable());
+        } else if (nodeResponse.getClientResponse().getStatus() == Response.Status.CONFLICT.getStatusCode()) {
+            ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse());
+            throw new InvalidRevisionException(String.format("Conflict: the flow may have been updated by another user."));
+        } else if (nodeResponse.getClientResponse().getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
+            ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse());
+            throw new ResourceNotFoundException("Unable to find component with id: " + id);
+        } else if (nodeResponse.getClientResponse().getStatus() != Response.Status.OK.getStatusCode()) {
+            ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse());
+            throw new ClusterRequestException("Method resulted in an unsuccessful HTTP response code: " + nodeResponse.getClientResponse().getStatus());
+        }
+    }
+
+    public void setClusterManager(WebClusterManager clusterManager) {
+        this.clusterManager = clusterManager;
+    }
+
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+    public void setServiceFacade(NiFiServiceFacade serviceFacade) {
+        this.serviceFacade = serviceFacade;
+    }
+
+    public void setAuditService(AuditService auditService) {
+        this.auditService = auditService;
+    }
+
+    public void setControllerServiceLookup(ControllerServiceLookup controllerServiceLookup) {
+        this.controllerServiceLookup = controllerServiceLookup;
+    }
+
+    public void setReportingTaskProvider(ReportingTaskProvider reportingTaskProvider) {
+        this.reportingTaskProvider = reportingTaskProvider;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java
index d9fa9e3..eaf457e 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiWebContext.java
@@ -35,7 +35,7 @@ import javax.ws.rs.core.Response;
 import org.apache.nifi.action.Action;
 import org.apache.nifi.action.Component;
 import org.apache.nifi.action.Operation;
-import org.apache.nifi.action.component.details.ProcessorDetails;
+import org.apache.nifi.action.component.details.ExtensionDetails;
 import org.apache.nifi.action.details.ConfigureDetails;
 import org.apache.nifi.admin.service.AuditService;
 import org.apache.nifi.cluster.manager.NodeResponse;
@@ -49,7 +49,6 @@ import org.apache.nifi.web.api.dto.ProcessorConfigDTO;
 import org.apache.nifi.web.api.dto.ProcessorDTO;
 import org.apache.nifi.web.api.dto.RevisionDTO;
 import org.apache.nifi.web.api.entity.ProcessorEntity;
-import org.apache.nifi.web.controller.ControllerFacade;
 import org.apache.nifi.web.util.WebUtils;
 
 import org.apache.commons.lang3.StringUtils;
@@ -60,12 +59,14 @@ import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 
 import com.sun.jersey.core.util.MultivaluedMapImpl;
+import org.apache.nifi.controller.ControllerServiceLookup;
 import org.apache.nifi.web.util.ClientResponseUtils;
 
 /**
  * Implements the NiFiWebContext interface to support a context in both
  * standalone and clustered environments.
  */
+@Deprecated
 public class StandardNiFiWebContext implements NiFiWebContext {
 
     private static final Logger logger = LoggerFactory.getLogger(StandardNiFiWebContext.class);
@@ -76,16 +77,12 @@ public class StandardNiFiWebContext implements NiFiWebContext {
     private NiFiProperties properties;
     private NiFiServiceFacade serviceFacade;
     private WebClusterManager clusterManager;
-    private ControllerFacade controllerFacade;
+    private ControllerServiceLookup controllerServiceLookup;
     private AuditService auditService;
 
     @Override
     public ControllerService getControllerService(String serviceIdentifier) {
-        if (properties.isClusterManager()) {
-            return clusterManager.getControllerService(serviceIdentifier);
-        } else {
-            return controllerFacade.getControllerService(serviceIdentifier);
-        }
+        return controllerServiceLookup.getControllerService(serviceIdentifier);
     }
 
     @Override
@@ -98,7 +95,7 @@ public class StandardNiFiWebContext implements NiFiWebContext {
         final Date now = new Date();
         final Collection<Action> actions = new HashSet<>(processorActions.size());
         for (final ProcessorConfigurationAction processorAction : processorActions) {
-            final ProcessorDetails processorDetails = new ProcessorDetails();
+            final ExtensionDetails processorDetails = new ExtensionDetails();
             processorDetails.setType(processorAction.getProcessorType());
 
             final ConfigureDetails configureDetails = new ConfigureDetails();
@@ -199,7 +196,10 @@ public class StandardNiFiWebContext implements NiFiWebContext {
             }
 
             // return processor
-            final ProcessorEntity entity = nodeResponse.getClientResponse().getEntity(ProcessorEntity.class);
+            ProcessorEntity entity = (ProcessorEntity) nodeResponse.getUpdatedEntity();
+            if (entity == null) {
+                entity = nodeResponse.getClientResponse().getEntity(ProcessorEntity.class);
+            }
             processor = entity.getProcessor();
         } else {
             processor = serviceFacade.getProcessor(processorId);
@@ -325,12 +325,12 @@ public class StandardNiFiWebContext implements NiFiWebContext {
         this.serviceFacade = serviceFacade;
     }
 
-    public void setControllerFacade(ControllerFacade controllerFacade) {
-        this.controllerFacade = controllerFacade;
-    }
-
     public void setAuditService(AuditService auditService) {
         this.auditService = auditService;
     }
 
+    public void setControllerServiceLookup(ControllerServiceLookup controllerServiceLookup) {
+        this.controllerServiceLookup = controllerServiceLookup;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
index 1b9ae7d..787fffa 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
@@ -178,53 +178,55 @@ public abstract class ApplicationResource {
 
         // get cluster context from threadlocal
         ClusterContext clusterCtx = ClusterContextThreadLocal.getContext();
+        if (clusterCtx != null) {
+            
+            // serialize cluster context
+            String serializedClusterContext = WebUtils.serializeObjectToHex(clusterCtx);
+            if (serializedClusterContext.length() > CLUSTER_CONTEXT_HEADER_VALUE_MAX_BYTES) {
+                /*
+                 * Actions is the only field that can vary in size. If we have no
+                 * actions and we exceeded the header size, then basic assumptions
+                 * about the cluster context have been violated.
+                 */
+                if (clusterCtx.getActions().isEmpty()) {
+                    throw new IllegalStateException(
+                            String.format("Serialized Cluster context size '%d' is too big for response header", serializedClusterContext.length()));
+                }
 
-        // serialize cluster context
-        String serializedClusterContext = WebUtils.serializeObjectToHex(clusterCtx);
-        if (serializedClusterContext.length() > CLUSTER_CONTEXT_HEADER_VALUE_MAX_BYTES) {
-            /*
-             * Actions is the only field that can vary in size. If we have no
-             * actions and we exceeded the header size, then basic assumptions
-             * about the cluster context have been violated.
-             */
-            if (clusterCtx.getActions().isEmpty()) {
-                throw new IllegalStateException(
-                        String.format("Serialized Cluster context size '%d' is too big for response header", serializedClusterContext.length()));
-            }
+                // use the first action as the prototype for creating the "batch" action
+                Action prototypeAction = clusterCtx.getActions().get(0);
 
-            // use the first action as the prototype for creating the "batch" action
-            Action prototypeAction = clusterCtx.getActions().get(0);
+                // log the batched actions
+                StringBuilder loggedActions = new StringBuilder();
+                createBatchedActionLogStatement(loggedActions, clusterCtx.getActions());
+                logger.info(loggedActions.toString());
 
-            // log the batched actions
-            StringBuilder loggedActions = new StringBuilder();
-            createBatchedActionLogStatement(loggedActions, clusterCtx.getActions());
-            logger.info(loggedActions.toString());
+                // remove current actions and replace with batch action
+                clusterCtx.getActions().clear();
 
-            // remove current actions and replace with batch action
-            clusterCtx.getActions().clear();
+                // create the batch action
+                Action batchAction = new Action();
+                batchAction.setOperation(Operation.Batch);
 
-            // create the batch action
-            Action batchAction = new Action();
-            batchAction.setOperation(Operation.Batch);
+                // copy values from prototype action 
+                batchAction.setTimestamp(prototypeAction.getTimestamp());
+                batchAction.setUserDn(prototypeAction.getUserDn());
+                batchAction.setUserName(prototypeAction.getUserName());
+                batchAction.setSourceId(prototypeAction.getSourceId());
+                batchAction.setSourceName(prototypeAction.getSourceName());
+                batchAction.setSourceType(prototypeAction.getSourceType());
 
-            // copy values from prototype action 
-            batchAction.setTimestamp(prototypeAction.getTimestamp());
-            batchAction.setUserDn(prototypeAction.getUserDn());
-            batchAction.setUserName(prototypeAction.getUserName());
-            batchAction.setSourceId(prototypeAction.getSourceId());
-            batchAction.setSourceName(prototypeAction.getSourceName());
-            batchAction.setSourceType(prototypeAction.getSourceType());
+                // add batch action
+                clusterCtx.getActions().add(batchAction);
 
-            // add batch action
-            clusterCtx.getActions().add(batchAction);
+                // create the final serialized copy of the cluster context
+                serializedClusterContext = WebUtils.serializeObjectToHex(clusterCtx);
+            }
 
-            // create the final serialized copy of the cluster context
-            serializedClusterContext = WebUtils.serializeObjectToHex(clusterCtx);
+            // put serialized cluster context in response header
+            response.header(WebClusterManager.CLUSTER_CONTEXT_HTTP_HEADER, serializedClusterContext);
         }
 
-        // put serialized cluster context in response header
-        response.header(WebClusterManager.CLUSTER_CONTEXT_HTTP_HEADER, serializedClusterContext);
-
         return response;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ClusterResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ClusterResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ClusterResource.java
index e87f388..3a74782 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ClusterResource.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ClusterResource.java
@@ -379,7 +379,7 @@ public class ClusterResource extends ApplicationResource {
             // update the revision
             RevisionDTO updatedRevision = new RevisionDTO();
             updatedRevision.setClientId(revision.getClientId());
-            updatedRevision.setVersion(controllerResponse.getRevision());
+            updatedRevision.setVersion(controllerResponse.getVersion());
 
             // generate the response entity
             final ProcessorEntity entity = new ProcessorEntity();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java
index a941444..5d233f7 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java
@@ -450,7 +450,7 @@ public class ConnectionResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // create the response entity
         ConnectionEntity entity = new ConnectionEntity();
@@ -684,7 +684,7 @@ public class ConnectionResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // create the response entity
         ConnectionEntity entity = new ConnectionEntity();
@@ -742,7 +742,7 @@ public class ConnectionResource extends ApplicationResource {
         // create the revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(clientId.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // create the response entity
         final ConnectionEntity entity = new ConnectionEntity();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
index 3afe0e1..98f17d5 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
@@ -72,6 +72,8 @@ import org.apache.nifi.web.api.request.ClientIdParameter;
 import org.apache.nifi.web.api.request.IntegerParameter;
 import org.apache.nifi.web.api.request.LongParameter;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.web.api.entity.ControllerServiceTypesEntity;
+import org.apache.nifi.web.api.entity.ReportingTaskTypesEntity;
 import org.codehaus.enunciate.jaxrs.TypeHint;
 import org.springframework.security.access.prepost.PreAuthorize;
 
@@ -149,7 +151,7 @@ public class ControllerResource extends ApplicationResource {
     }
 
     /**
-     * Locates the Template sub-resource.
+     * Locates the Snippets sub-resource.
      *
      * @return
      */
@@ -157,6 +159,26 @@ public class ControllerResource extends ApplicationResource {
     public SnippetResource getSnippetResource() {
         return resourceContext.getResource(SnippetResource.class);
     }
+    
+    /**
+     * Locates the Controller Services sub-resource.
+     *
+     * @return
+     */
+    @Path("/controller-services")
+    public ControllerServiceResource getControllerServiceResource() {
+        return resourceContext.getResource(ControllerServiceResource.class);
+    }
+    
+    /**
+     * Locates the Reporting Tasks sub-resource.
+     *
+     * @return
+     */
+    @Path("/reporting-tasks")
+    public ReportingTaskResource getReportingTaskResource() {
+        return resourceContext.getResource(ReportingTaskResource.class);
+    }
 
     /**
      * Locates the Group sub-resource.
@@ -303,7 +325,7 @@ public class ControllerResource extends ApplicationResource {
         // create the revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(clientId.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // create the response entity
         final ProcessGroupEntity controllerEntity = new ProcessGroupEntity();
@@ -325,11 +347,6 @@ public class ControllerResource extends ApplicationResource {
     @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
     @TypeHint(Entity.class)
     public Response getRevision() {
-        // replicate if cluster manager
-        if (properties.isClusterManager()) {
-            return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
-        }
-
         // create the current revision
         final RevisionDTO revision = serviceFacade.getRevision();
 
@@ -595,7 +612,7 @@ public class ControllerResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // create the response entity
         final ControllerConfigurationEntity entity = new ControllerConfigurationEntity();
@@ -713,6 +730,72 @@ public class ControllerResource extends ApplicationResource {
         // generate the response
         return clusterContext(generateOkResponse(entity)).build();
     }
+    
+    /**
+     * Retrieves the types of controller services that this NiFi supports.
+     *
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @return A controllerServicesTypesEntity.
+     */
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/controller-service-types")
+    @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
+    @TypeHint(ControllerServiceTypesEntity.class)
+    public Response getControllerServiceTypes(@QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) {
+
+        // replicate if cluster manager
+        if (properties.isClusterManager()) {
+            return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
+        }
+
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+
+        // create response entity
+        final ControllerServiceTypesEntity entity = new ControllerServiceTypesEntity();
+        entity.setRevision(revision);
+        entity.setControllerServiceTypes(serviceFacade.getControllerServiceTypes());
+
+        // generate the response
+        return clusterContext(generateOkResponse(entity)).build();
+    }
+    
+    /**
+     * Retrieves the types of reporting tasks that this NiFi supports.
+     *
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @return A controllerServicesTypesEntity.
+     */
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/reporting-task-types")
+    @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
+    @TypeHint(ReportingTaskTypesEntity.class)
+    public Response getReportingTaskTypes(@QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId) {
+
+        // replicate if cluster manager
+        if (properties.isClusterManager()) {
+            return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
+        }
+
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+
+        // create response entity
+        final ReportingTaskTypesEntity entity = new ReportingTaskTypesEntity();
+        entity.setRevision(revision);
+        entity.setReportingTaskTypes(serviceFacade.getReportingTaskTypes());
+
+        // generate the response
+        return clusterContext(generateOkResponse(entity)).build();
+    }
 
     /**
      * Retrieves the types of prioritizers that this NiFi supports.


[10/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/StandardOptimisticLockingManager.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/StandardOptimisticLockingManager.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/StandardOptimisticLockingManager.java
index 8da6d23..23ef8eb 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/StandardOptimisticLockingManager.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/StandardOptimisticLockingManager.java
@@ -16,6 +16,14 @@
  */
 package org.apache.nifi.web;
 
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import org.apache.nifi.cluster.context.ClusterContext;
+import org.apache.nifi.cluster.context.ClusterContextThreadLocal;
+import org.apache.nifi.web.security.user.NiFiUserUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 /**
  * Implements the OptimisticLockingManager interface.
  *
@@ -23,55 +31,129 @@ package org.apache.nifi.web;
  */
 public class StandardOptimisticLockingManager implements OptimisticLockingManager {
 
+    private static final Logger logger = LoggerFactory.getLogger(StandardOptimisticLockingManager.class);
+    
+    private static final String INVALID_REVISION_ERROR = "Given revision %s does not match current revision %s.";
+    private static final String SYNC_ERROR = "This NiFi instance has been updated by '%s'. Please refresh to synchronize the view.";
+    
     private Revision currentRevision = new Revision(0L, "");
-
     private String lastModifier = "unknown";
+    private final Lock lock = new ReentrantLock();
+    
+    private void lock() {
+        lock.lock();
+    }
+    
+    private void unlock() {
+        lock.unlock();
+    }
 
-    @Override
-    public Revision checkRevision(Revision revision) throws InvalidRevisionException {
-        if (currentRevision.equals(revision) == false) {
-            throw new InvalidRevisionException(String.format("Given revision %s does not match current revision %s.", revision, currentRevision));
-        } else {
-            currentRevision = revision.increment(revision.getClientId());
-            return currentRevision;
+    private void checkRevision(final Revision revision) {
+        final FlowModification lastMod = getLastModification();
+        
+        // with lock, verify revision
+        boolean approved = lastMod.getRevision().equals(revision);
+
+        if (!approved) {
+            logger.debug("Revision check failed because current revision is " + lastMod.getRevision() + " but supplied revision is " + revision);
+            
+            if (lastMod.getRevision().getClientId() == null || lastMod.getRevision().getClientId().trim().isEmpty() || lastMod.getRevision().getVersion() == null) {
+                throw new InvalidRevisionException(String.format(INVALID_REVISION_ERROR, revision, lastMod.getRevision()));
+            } else {
+                throw new InvalidRevisionException(String.format(SYNC_ERROR, lastMod.getLastModifier()));
+            }
         }
     }
-
-    @Override
-    public boolean isCurrent(Revision revision) {
-        return currentRevision.equals(revision);
+    
+    private Revision updateRevision(final Revision updatedRevision) {
+        // record the current modification
+        setLastModification(new FlowModification(updatedRevision, NiFiUserUtils.getNiFiUserName()));
+        
+        // return the revision
+        return updatedRevision;
     }
 
     @Override
-    public Revision getRevision() {
-        return currentRevision;
-    }
+    public <T> ConfigurationSnapshot<T> configureFlow(Revision revision, ConfigurationRequest<T> configurationRequest) {
+        lock();
+        try {
+            // check the revision
+            checkRevision(revision);
 
-    @Override
-    public void setRevision(Revision revision) {
-        currentRevision = revision;
-    }
+            // execute the configuration request
+            final T result = configurationRequest.execute();
 
-    @Override
-    public Revision incrementRevision() {
-        currentRevision = currentRevision.increment();
-        return currentRevision;
-    }
+            // update the revision
+            final Revision newRevision = updateRevision(incrementRevision(revision.getClientId()));
 
-    @Override
-    public Revision incrementRevision(String clientId) {
-        currentRevision = currentRevision.increment(clientId);
-        return currentRevision;
+            // build the result
+            return new ConfigurationSnapshot(newRevision.getVersion(), result);
+        } finally {
+            unlock();
+        }
     }
 
     @Override
-    public String getLastModifier() {
-        return lastModifier;
+    public void setRevision(UpdateRevision updateRevision) {
+        lock();
+        try {
+            final Revision updatedRevision = updateRevision.execute(getLastModification().getRevision());
+            
+            // update the revision
+            if (updatedRevision != null) {
+                updateRevision(updatedRevision);
+            }
+        } finally {
+            unlock();
+        }
     }
-
+    
     @Override
-    public void setLastModifier(String lastModifier) {
-        this.lastModifier = lastModifier;
+    public FlowModification getLastModification() {
+        lock();
+        try {
+            final Revision revision;
+            final ClusterContext ctx = ClusterContextThreadLocal.getContext();
+            if (ctx == null || ctx.getRevision() == null) {
+                revision = currentRevision;
+            } else {
+                revision = ctx.getRevision();
+            }
+            
+            return new FlowModification(revision, lastModifier);
+        } finally {
+            unlock();
+        }
     }
-
+    
+    private void setLastModification(final FlowModification lastModification) {
+        lock();
+        try {
+            // record the last modifier
+            lastModifier = lastModification.getLastModifier();
+            
+            // record the updated revision in the cluster context if possible
+            final ClusterContext ctx = ClusterContextThreadLocal.getContext();
+            if (ctx != null) {
+                ctx.setRevision(lastModification.getRevision());
+            } else {
+                currentRevision = lastModification.getRevision();
+            }
+        } finally {
+            unlock();
+        }
+    }
+    
+    private Revision incrementRevision(String clientId) {
+        final Revision current = getLastModification().getRevision();
+        
+        final long incrementedVersion;
+        if (current.getVersion() == null) {
+            incrementedVersion = 0;
+        } else {
+            incrementedVersion = current.getVersion() + 1;
+        }
+        return new Revision(incrementedVersion, clientId);
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/UpdateRevision.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/UpdateRevision.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/UpdateRevision.java
new file mode 100644
index 0000000..e691bbe
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/UpdateRevision.java
@@ -0,0 +1,31 @@
+/*
+ * 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.web;
+
+/**
+ * Represents an action that will result in an updated revision.
+ */
+public interface UpdateRevision {
+
+    /**
+     * Executes the action that will result in an updated revision
+     * 
+     * @param currentRevision       The current revision
+     * @return                      The updated revision
+     */
+    Revision execute(Revision currentRevision);
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserUtils.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserUtils.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserUtils.java
index 0209cf7..acf37dc 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserUtils.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/user/NiFiUserUtils.java
@@ -79,4 +79,14 @@ public final class NiFiUserUtils {
 
         return user;
     }
+    
+    public static String getNiFiUserName() {
+        // get the nifi user to extract the username
+        NiFiUser user = NiFiUserUtils.getNiFiUser();
+        if (user == null) {
+            return "unknown";
+        } else {
+            return user.getUserName();
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
index 34ba05e..f2d4861 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
@@ -265,12 +265,10 @@
                                                 <include>${staging.dir}/js/nf/canvas/nf-storage.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-snippet.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-canvas-toolbox.js</include>
-                                                <include>${staging.dir}/js/nf/canvas/nf-custom-processor-ui.js</include>
+                                                <include>${staging.dir}/js/nf/canvas/nf-custom-ui.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-registration.js</include>
-                                                <include>${staging.dir}/js/nf/canvas/nf-processor-property-table.js</include>
-                                                <include>${staging.dir}/js/nf/canvas/nf-processor-property-text-editor.js</include>
-                                                <include>${staging.dir}/js/nf/canvas/nf-processor-property-nfel-editor.js</include>
-                                                <include>${staging.dir}/js/nf/canvas/nf-processor-property-combo-editor.js</include>
+                                                <include>${staging.dir}/js/nf/canvas/nf-controller-service.js</include>
+                                                <include>${staging.dir}/js/nf/canvas/nf-reporting-task.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-processor-configuration.js</include>
                                                 <include>${staging.dir}/js/nf/nf-processor-details.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-process-group-configuration.js</include>
@@ -406,6 +404,8 @@
                                             <insertNewLine>true</insertNewLine>
                                             <output>${project.build.directory}/${project.build.finalName}/css/nf-canvas-all.css</output>
                                             <includes>
+                                                <include>${staging.dir}/css/reporting-task.css</include>
+                                                <include>${staging.dir}/css/controller-service.css</include>
                                                 <include>${staging.dir}/css/processor-configuration.css</include>
                                                 <include>${staging.dir}/css/processor-details.css</include>
                                                 <include>${staging.dir}/css/process-group-configuration.css</include>
@@ -420,6 +420,8 @@
                                                 <include>${staging.dir}/css/registration.css</include>
                                                 <include>${staging.dir}/css/dialog.css</include>
                                                 <include>${staging.dir}/css/new-processor-dialog.css</include>
+                                                <include>${staging.dir}/css/new-controller-service-dialog.css</include>
+                                                <include>${staging.dir}/css/new-reporting-task-dialog.css</include>
                                                 <include>${staging.dir}/css/graph.css</include>
                                                 <include>${staging.dir}/css/header.css</include>
                                                 <include>${staging.dir}/css/main.css</include>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
index 056a2af..fd2bc17 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
@@ -22,12 +22,10 @@ nf.canvas.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js?
 <script type="text/javascript" src="js/nf/canvas/nf-storage.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-snippet.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-canvas-toolbox.js?${project.version}"></script>\n\
-<script type="text/javascript" src="js/nf/canvas/nf-custom-processor-ui.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/canvas/nf-custom-ui.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-registration.js?${project.version}"></script>\n\
-<script type="text/javascript" src="js/nf/canvas/nf-processor-property-table.js?${project.version}"></script>\n\
-<script type="text/javascript" src="js/nf/canvas/nf-processor-property-text-editor.js?${project.version}"></script>\n\
-<script type="text/javascript" src="js/nf/canvas/nf-processor-property-nfel-editor.js?${project.version}"></script>\n\
-<script type="text/javascript" src="js/nf/canvas/nf-processor-property-combo-editor.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/canvas/nf-controller-service.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/canvas/nf-reporting-task.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-processor-configuration.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/nf-processor-details.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-process-group-configuration.js?${project.version}"></script>\n\

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
index ba65475..c81bb9d 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
@@ -29,6 +29,8 @@
         <link rel="stylesheet" href="js/jquery/nfeditor/languages/nfel.css?${project.version}" type="text/css" />
         <link rel="stylesheet" href="js/jquery/tabbs/jquery.tabbs.css?${project.version}" type="text/css" />
         <link rel="stylesheet" href="js/jquery/combo/jquery.combo.css?${project.version}" type="text/css" />
+        <link rel="stylesheet" href="js/jquery/propertytable/jquery.propertytable.css?${project.version}" type="text/css" />
+        <link rel="stylesheet" href="js/jquery/tagcloud/jquery.tagcloud.css?${project.version}" type="text/css" />
         <link rel="stylesheet" href="js/jquery/modal/jquery.modal.css?${project.version}" type="text/css" />
         <link rel="stylesheet" href="js/jquery/qtip2/jquery.qtip.min.css?" type="text/css" />
         <link rel="stylesheet" href="js/jquery/ui-smoothness/jquery-ui-1.10.4.min.css" type="text/css" />
@@ -45,6 +47,8 @@
         <script type="text/javascript" src="js/jquery/jquery.tab.js"></script>
         <script type="text/javascript" src="js/jquery/tabbs/jquery.tabbs.js?${project.version}"></script>
         <script type="text/javascript" src="js/jquery/combo/jquery.combo.js?${project.version}"></script>
+        <script type="text/javascript" src="js/jquery/propertytable/jquery.propertytable.js?${project.version}"></script>
+        <script type="text/javascript" src="js/jquery/tagcloud/jquery.tagcloud.js?${project.version}"></script>
         <script type="text/javascript" src="js/jquery/modal/jquery.modal.js?${project.version}"></script>
         <script type="text/javascript" src="js/jquery/minicolors/jquery.minicolors.min.js"></script>
         <script type="text/javascript" src="js/jquery/qtip2/jquery.qtip.min.js"></script>
@@ -76,13 +80,16 @@
         <jsp:include page="/WEB-INF/partials/ok-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/yes-no-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/status-history-dialog.jsp"/>
+        <jsp:include page="/WEB-INF/partials/canvas/disable-controller-service-dialog.jsp"/>
+        <jsp:include page="/WEB-INF/partials/canvas/enable-controller-service-dialog.jsp"/>
+        <jsp:include page="/WEB-INF/partials/canvas/new-controller-service-dialog.jsp"/>
+        <jsp:include page="/WEB-INF/partials/canvas/new-reporting-task-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/new-processor-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/new-port-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/new-process-group-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/new-remote-process-group-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/new-template-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/instantiate-template-dialog.jsp"/>
-        <jsp:include page="/WEB-INF/partials/canvas/new-processor-property-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/fill-color-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/connections-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/flow-status.jsp"/>
@@ -96,6 +103,8 @@
         <jsp:include page="/WEB-INF/partials/canvas/navigation.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/settings-content.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/shell.jsp"/>
+        <jsp:include page="/WEB-INF/partials/canvas/controller-service-configuration.jsp"/>
+        <jsp:include page="/WEB-INF/partials/canvas/reporting-task-configuration.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/processor-configuration.jsp"/>
         <jsp:include page="/WEB-INF/partials/processor-details.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/process-group-configuration.jsp"/>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
index fdfc806..032509b 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
@@ -25,6 +25,7 @@
         ${nf.summary.style.tags}
         <link rel="stylesheet" href="js/jquery/tabbs/jquery.tabbs.css?${project.version}" type="text/css" />
         <link rel="stylesheet" href="js/jquery/combo/jquery.combo.css?${project.version}" type="text/css" />
+        <link rel="stylesheet" href="js/jquery/propertytable/jquery.propertytable.css?${project.version}" type="text/css" />
         <link rel="stylesheet" href="js/jquery/modal/jquery.modal.css?${project.version}" type="text/css" />
         <link rel="stylesheet" href="js/codemirror/lib/codemirror.css" type="text/css" />
         <link rel="stylesheet" href="js/codemirror/addon/hint/show-hint.css" type="text/css" />
@@ -40,6 +41,7 @@
         <script type="text/javascript" src="js/jquery/jquery.center.js"></script>
         <script type="text/javascript" src="js/jquery/tabbs/jquery.tabbs.js?${project.version}"></script>
         <script type="text/javascript" src="js/jquery/combo/jquery.combo.js?${project.version}"></script>
+        <script type="text/javascript" src="js/jquery/propertytable/jquery.propertytable.js?${project.version}"></script>
         <script type="text/javascript" src="js/jquery/modal/jquery.modal.js?${project.version}"></script>
         <script type="text/javascript" src="js/jquery/jquery.ellipsis.js"></script>
         <script type="text/javascript" src="js/jquery/jquery.each.js"></script>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp
index 8eea811..f312327 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/canvas-header.jsp
@@ -31,7 +31,7 @@
                 <div id="counters-link" class="utility-button" title="Counters"></div>
                 <div id="history-link" class="utility-button" title="Flow Configuration History"></div>
                 <div id="provenance-link" class="utility-button" title="Data Provenance"></div>
-                <div id="flow-settings-link" class="utility-button" title="Flow Settings"></div>
+                <div id="flow-settings-link" class="utility-button" title="Controller Settings"></div>
                 <div id="templates-link" class="utility-button" title="Templates"></div>
                 <div id="users-link" class="utility-button" title="Users"><div id="has-pending-accounts" class="hidden"></div></div>
                 <div id="cluster-link" class="utility-button" title="Cluster"></div>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/controller-service-configuration.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/controller-service-configuration.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/controller-service-configuration.jsp
new file mode 100644
index 0000000..533aa54
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/controller-service-configuration.jsp
@@ -0,0 +1,90 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="controller-service-configuration">
+    <div class="controller-service-configuration-tab-container">
+        <div id="controller-service-configuration-tabs"></div>
+        <div id="controller-service-configuration-tabs-content">
+            <div id="controller-service-standard-settings-tab-content" class="configuration-tab">
+                <div class="settings-left">
+                    <div class="setting">
+                        <div class="setting-name">Name</div>
+                        <div class="controller-service-editable setting-field">
+                            <input type="text" id="controller-service-name" name="controller-service-name" class="setting-input"/>
+                            <div class="controller-service-enabled-container">
+                                <div id="controller-service-enabled" class="nf-checkbox checkbox-unchecked"></div>
+                                <span> Enabled</span>
+                            </div>
+                        </div>
+                        <div class="controller-service-read-only setting-field hidden">
+                            <span id="read-only-controller-service-name"></span>
+                        </div>
+                    </div>
+                    <div class="setting">
+                        <div class="setting-name">Id</div>
+                        <div class="setting-field">
+                            <span id="controller-service-id"></span>
+                        </div>
+                    </div>
+                    <div class="setting">
+                        <div class="setting-name">Type</div>
+                        <div class="setting-field">
+                            <span id="controller-service-type"></span>
+                        </div>
+                    </div>
+                    <div id="controller-service-availability-setting-container" class="setting hidden">
+                        <div class="availability-setting">
+                            <div class="setting-name">
+                                Availability
+                                <img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="Where this controller service is available."/>
+                            </div>
+                            <div class="setting-field">
+                                <div id="controller-service-availability"></div>
+                            </div>
+                        </div>
+                        <div class="clear"></div>
+                    </div>
+                </div>
+                <div class="spacer">&nbsp;</div>
+                <div class="settings-right">
+                    <div class="setting">
+                        <div class="setting-name">
+                            Referencing Components
+                            <img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="Other components referencing this controller service."/>
+                        </div>
+                        <div class="setting-field">
+                            <div id="controller-service-referencing-components"></div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div id="controller-service-properties-tab-content" class="configuration-tab">
+                <div id="controller-service-properties"></div>
+            </div>
+            <div id="controller-service-comments-tab-content" class="configuration-tab">
+                <textarea cols="30" rows="4" id="controller-service-comments" name="controller-service-comments" class="controller-service-editable setting-input"></textarea>
+                <div class="setting controller-service-read-only hidden">
+                    <div class="setting-name">Comments</div>
+                    <div class="setting-field">
+                        <span id="read-only-controller-service-comments"></span>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<div id="new-controller-service-property-container"></div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/disable-controller-service-dialog.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/disable-controller-service-dialog.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/disable-controller-service-dialog.jsp
new file mode 100644
index 0000000..dc76282
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/disable-controller-service-dialog.jsp
@@ -0,0 +1,71 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="disable-controller-service-dialog">
+    <div class="dialog-content">
+        <div class="settings-left">
+            <div id="disable-controller-service-service-container" class="setting">
+                <div class="setting-name">Service</div>
+                <div class="setting-field">
+                    <span id="disable-controller-service-id" class="hidden"></span>
+                    <span id="disable-controller-service-name"></span>
+                </div>
+            </div>
+            <div id="disable-controller-service-scope-container" class="setting">
+                <div class="setting-name">Scope</div>
+                <div class="setting-field">
+                    Service and referencing components
+                    <img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="Referencing components must be disabled/stopped in order to disable this service."/>
+                </div>
+            </div>
+            <div id="disable-controller-service-progress-container" class="setting hidden">
+                <div id="disable-progress-label" class="setting-name"></div>
+                <div class="setting-field">
+                    <ol id="disable-controller-service-progress">
+                        <li>
+                            Stopping referencing processors and reporting tasks
+                            <div id="disable-referencing-schedulable" class="disable-referencing-components"></div>
+                            <div class="clear"></div>
+                        </li>
+                        <li>
+                            Disabling referencing controller services
+                            <div id="disable-referencing-services" class="disable-referencing-components"></div>
+                            <div class="clear"></div>
+                        </li>
+                        <li>
+                            Disabling this controller service
+                            <div id="disable-controller-service" class="disable-referencing-components"></div>
+                            <div class="clear"></div>
+                        </li>
+                    </ol>
+                </div>
+            </div>
+        </div>
+        <div class="spacer">&nbsp;</div>
+        <div class="settings-right">
+            <div class="setting">
+                <div class="setting-name">
+                    Referencing Components
+                    <img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="Other components referencing this controller service."/>
+                </div>
+                <div class="setting-field">
+                    <div id="disable-controller-service-referencing-components"></div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/enable-controller-service-dialog.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/enable-controller-service-dialog.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/enable-controller-service-dialog.jsp
new file mode 100644
index 0000000..14fe658
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/enable-controller-service-dialog.jsp
@@ -0,0 +1,70 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="enable-controller-service-dialog">
+    <div class="dialog-content">
+        <div class="settings-left">
+            <div id="enable-controller-service-service-container" class="setting">
+                <div class="setting-name">Service</div>
+                <div class="setting-field">
+                    <span id="enable-controller-service-id" class="hidden"></span>
+                    <span id="enable-controller-service-name"></span>
+                </div>
+            </div>
+            <div id="enable-controller-service-scope-container" class="setting">
+                <div class="setting-name">Scope</div>
+                <div class="setting-field">
+                    <div id="enable-controller-service-scope"></div>
+                </div>
+            </div>
+            <div id="enable-controller-service-progress-container" class="setting hidden">
+                <div id="enable-progress-label" class="setting-name"></div>
+                <div class="setting-field">
+                    <ol id="enable-controller-service-progress">
+                        <li>
+                            Enabling this controller service
+                            <div id="enable-controller-service" class="enable-referencing-components"></div>
+                            <div class="clear"></div>
+                        </li>
+                        <li class="referencing-component">
+                            Enabling referencing controller services
+                            <div id="enable-referencing-services" class="enable-referencing-components"></div>
+                            <div class="clear"></div>
+                        </li>
+                        <li class="referencing-component">
+                            Starting referencing processors and reporting tasks
+                            <div id="enable-referencing-schedulable" class="enable-referencing-components"></div>
+                            <div class="clear"></div>
+                        </li>
+                    </ol>
+                </div>
+            </div>
+        </div>
+        <div class="spacer">&nbsp;</div>
+        <div class="settings-right">
+            <div class="setting">
+                <div class="setting-name">
+                    Referencing Components
+                    <img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="Other components referencing this controller service."/>
+                </div>
+                <div class="setting-field">
+                    <div id="enable-controller-service-referencing-components"></div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-controller-service-dialog.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-controller-service-dialog.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-controller-service-dialog.jsp
new file mode 100644
index 0000000..eb54ace
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-controller-service-dialog.jsp
@@ -0,0 +1,53 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="new-controller-service-dialog">
+    <div class="dialog-content">
+        <div id="controller-service-type-filter-controls">
+            <div id="controller-service-type-filter-container">
+                <input type="text" id="controller-service-type-filter"/>
+                <div id="controller-service-type-filter-options"></div>
+            </div>
+            <div id="controller-service-type-filter-status">
+                Displaying&nbsp;<span id="displayed-controller-service-types"></span>&nbsp;of&nbsp;<span id="total-controller-service-types"></span>
+            </div>
+        </div>
+        <div id="controller-service-tag-cloud-container">
+            <div class="setting">
+                <div class="setting-name">Tags</div>
+                <div class="setting-field">
+                    <div id="controller-service-tag-cloud"></div>
+                </div>
+            </div>
+        </div>
+        <div id="controller-service-types-container">
+            <div id="controller-service-types-table" class="unselectable"></div>
+            <div id="controller-service-description-container" class="hidden">
+                <div id="controller-service-type-name" class="ellipsis"></div>
+                <div id="controller-service-type-description" class="ellipsis multiline"></div>
+                <span class="hidden" id="selected-controller-service-name"></span>
+                <span class="hidden" id="selected-controller-service-type"></span>
+            </div>
+        </div>
+        <div class="clear"></div>
+        <div id="controller-service-availability-container" class="hidden">
+            <div class="setting-name availability-label">Available on</div>
+            <div id="controller-service-availability-combo"></div>
+            <div class="clear"></div>
+        </div>
+    </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-processor-dialog.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-processor-dialog.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-processor-dialog.jsp
index 29b57c9..df7766c 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-processor-dialog.jsp
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-processor-dialog.jsp
@@ -26,13 +26,11 @@
                 Displaying&nbsp;<span id="displayed-processor-types"></span>&nbsp;of&nbsp;<span id="total-processor-types"></span>
             </div>
         </div>
-        <div id="tag-cloud-container">
+        <div id="processor-tag-cloud-container">
             <div class="setting">
                 <div class="setting-name">Tags</div>
                 <div class="setting-field">
-                    <ul id="tag-cloud"></ul>
-                    <div id="tag-cloud-separator"></div>
-                    <ul id="tag-filter"></ul>
+                    <div id="processor-tag-cloud"></div>
                 </div>
             </div>
         </div>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-processor-property-dialog.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-processor-property-dialog.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-processor-property-dialog.jsp
deleted file mode 100644
index 9f58c2d..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-processor-property-dialog.jsp
+++ /dev/null
@@ -1,34 +0,0 @@
-<%--
- 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.
---%>
-<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
-<div id="processor-property-dialog" class="dialog">
-    <div>
-        <div class="setting-name">Property name</div>
-        <div class="setting-field" id="new-property-name-container">
-            <input id="new-property-name" name="new-property-name" type="text"/>
-        </div>
-        <div class="setting-name" style="margin-top: 36px;">Property value</div>
-        <div class="setting-field">
-            <div id="new-property-value"></div>
-        </div>
-    </div>
-    <div id="new-property-button-container">
-        <div id="new-property-ok" class="button button-normal">Ok</div>
-        <div id="new-property-cancel" class="button button-normal">Cancel</div>
-        <div class="clear"></div>
-    </div>
-</div>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-reporting-task-dialog.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-reporting-task-dialog.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-reporting-task-dialog.jsp
new file mode 100644
index 0000000..cfb3992
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-reporting-task-dialog.jsp
@@ -0,0 +1,53 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="new-reporting-task-dialog">
+    <div class="dialog-content">
+        <div id="reporting-task-type-filter-controls">
+            <div id="controller-service-type-filter-container">
+                <input type="text" id="reporting-task-type-filter"/>
+                <div id="reporting-task-type-filter-options"></div>
+            </div>
+            <div id="reporting-task-type-filter-status">
+                Displaying&nbsp;<span id="displayed-reporting-task-types"></span>&nbsp;of&nbsp;<span id="total-reporting-task-types"></span>
+            </div>
+        </div>
+        <div id="reporting-task-tag-cloud-container">
+            <div class="setting">
+                <div class="setting-name">Tags</div>
+                <div class="setting-field">
+                    <div id="reporting-task-tag-cloud"></div>
+                </div>
+            </div>
+        </div>
+        <div id="reporting-task-types-container">
+            <div id="reporting-task-types-table" class="unselectable"></div>
+            <div id="reporting-task-description-container" class="hidden">
+                <div id="reporting-task-type-name" class="ellipsis"></div>
+                <div id="reporting-task-type-description" class="ellipsis multiline"></div>
+                <span class="hidden" id="selected-reporting-task-name"></span>
+                <span class="hidden" id="selected-reporting-task-type"></span>
+            </div>
+        </div>
+        <div class="clear"></div>
+        <div id="reporting-task-availability-container" class="hidden">
+            <div class="setting-name availability-label">Available on</div>
+            <div id="reporting-task-availability-combo"></div>
+            <div class="clear"></div>
+        </div>
+    </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/processor-configuration.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/processor-configuration.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/processor-configuration.jsp
index 9d9da2a..3516de7 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/processor-configuration.jsp
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/processor-configuration.jsp
@@ -19,7 +19,7 @@
     <div class="processor-configuration-tab-container">
         <div id="processor-configuration-tabs"></div>
         <div id="processor-configuration-tabs-content">
-            <div id="configuration-standard-settings-tab-content" class="configuration-tab">
+            <div id="processor-standard-settings-tab-content" class="configuration-tab">
                 <div class="settings-left">
                     <div class="setting">
                         <div class="setting-name">Name</div>
@@ -71,7 +71,7 @@
                                 <img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="The level at which this processor will generate bulletins."/>
                             </div>
                             <div class="setting-field">
-                                <div type="text" id="bulletin-level-combo"></div>
+                                <div id="bulletin-level-combo"></div>
                             </div>
                         </div>
                         <div class="clear"></div>
@@ -90,7 +90,7 @@
                     </div>
                 </div>
             </div>
-            <div id="configuration-scheduling-tab-content" class="configuration-tab">
+            <div id="processor-scheduling-tab-content" class="configuration-tab">
                 <div class="settings-left">
                     <div class="setting">
                         <div class="scheduling-strategy-setting">
@@ -194,20 +194,13 @@
                     </div>
                 </div>
             </div>
-            <div id="configuration-processor-properties-tab-content" class="configuration-tab">
-                <div id="processor-properties-header">
-                    <div id="required-property-note">Required field</div>
-                    <div id="add-property">
-                        <div id="add-property-icon" class="add-icon-bg"></div>
-                        <div id="add-text">New property</div>
-                    </div>
-                    <div class="clear"></div>
-                </div>
+            <div id="processor-properties-tab-content" class="configuration-tab">
                 <div id="processor-properties"></div>
             </div>
-            <div id="configuration-comments-tab-content" class="configuration-tab">
+            <div id="processor-comments-tab-content" class="configuration-tab">
                 <textarea cols="30" rows="4" id="processor-comments" name="processor-comments" class="setting-input"></textarea>
             </div>
         </div>
     </div>
-</div>
\ No newline at end of file
+</div>
+<div id="new-processor-property-container"></div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/registration.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/registration.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/registration.jsp
index 4a965e4..56b3236 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/registration.jsp
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/registration.jsp
@@ -20,7 +20,7 @@
         <p id="register-title" class="message-pane-title">You are not authorized to access this data flow</p>
         <p id="register-content" class="message-pane-content">
         <div>
-            <div id="expand-registration-button" class="registration-collapsed pointer"></div>
+            <div id="expand-registration-button" class="collapsed pointer"></div>
             <span id="expand-registration-text" class="link">Request Access</span>
         </div>
         <div id="registration-form" class="settings hidden">

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/reporting-task-configuration.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/reporting-task-configuration.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/reporting-task-configuration.jsp
new file mode 100644
index 0000000..8d3e84d
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/reporting-task-configuration.jsp
@@ -0,0 +1,107 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="reporting-task-configuration">
+    <div class="reporting-task-configuration-tab-container">
+        <div id="reporting-task-configuration-tabs"></div>
+        <div id="reporting-task-configuration-tabs-content">
+            <div id="reporting-task-standard-settings-tab-content" class="configuration-tab">
+                <div class="settings-left">
+                    <div class="setting">
+                        <div class="setting-name">Name</div>
+                        <div class="reporting-task-editable setting-field">
+                            <input type="text" id="reporting-task-name" name="reporting-task-name"/>
+                            <div class="reporting-task-enabled-container">
+                                <div id="reporting-task-enabled" class="nf-checkbox checkbox-unchecked"></div>
+                                <span> Enabled</span>
+                            </div>
+                        </div>
+                        <div class="reporting-task-read-only setting-field hidden">
+                            <span id="read-only-reporting-task-name"></span>
+                        </div>
+                    </div>
+                    <div class="setting">
+                        <div class="setting-name">Id</div>
+                        <div class="setting-field">
+                            <span id="reporting-task-id"></span>
+                        </div>
+                    </div>
+                    <div class="setting">
+                        <div class="setting-name">Type</div>
+                        <div class="setting-field">
+                            <span id="reporting-task-type"></span>
+                        </div>
+                    </div>
+                    <div id="reporting-task-availability-setting-container" class="setting hidden">
+                        <div class="availability-setting">
+                            <div class="setting-name">
+                                Availability
+                                <img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="Where this controller service is available."/>
+                            </div>
+                            <div class="setting-field">
+                                <div id="reporting-task-availability"></div>
+                            </div>
+                        </div>
+                        <div class="clear"></div>
+                    </div>
+                </div>
+                <div class="spacer">&nbsp;</div>
+                <div class="settings-right">
+                    <div class="setting">
+                        <div class="setting-name">
+                            Scheduling strategy
+                            <img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="The strategy used to schedule this reporting task."/>
+                        </div>
+                        <div class="reporting-task-editable setting-field">
+                            <div id="reporting-task-scheduling-strategy-combo"></div>
+                        </div>
+                        <div class="reporting-task-read-only setting-field hidden">
+                            <span id="read-only-reporting-task-scheduling-strategy"></span>
+                        </div>
+                    </div>
+                    <div class="setting">
+                        <div class="setting-name">
+                            Run schedule
+                            <img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="The amount of time that should elapse between task executions."/>
+                        </div>
+                        <div class="reporting-task-editable setting-field">
+                            <input type="text" id="reporting-task-timer-driven-scheduling-period" class="reporting-task-scheduling-period"/>
+                            <input type="text" id="reporting-task-cron-driven-scheduling-period" class="reporting-task-scheduling-period"/>
+                        </div>
+                        <div class="reporting-task-read-only setting-field hidden">
+                            <span id="read-only-reporting-task-scheduling-period"></span>
+                        </div>
+                    </div>
+                </div>
+                <div class="clear"></div>
+            </div>
+            <div id="reporting-task-properties-tab-content" class="configuration-tab">
+                <div id="reporting-task-properties"></div>
+            </div>
+            <div id="reporting-task-comments-tab-content" class="configuration-tab">
+                <textarea cols="30" rows="4" id="reporting-task-comments" name="reporting-task-comments" class="reporting-task-editable setting-input"></textarea>
+                <div class="setting reporting-task-read-only hidden">
+                    <div class="setting-name">Comments</div>
+                    <div class="setting-field">
+                        <span id="read-only-reporting-task-comments"></span>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<div id="new-reporting-task-property-container"></div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/settings-content.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/settings-content.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/settings-content.jsp
index d9b2f29..67e61de 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/settings-content.jsp
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/settings-content.jsp
@@ -18,43 +18,77 @@
 <div id="settings">
     <div id="settings-header-text">NiFi Settings</div>
     <div id="settings-container">
-        <div id="general-settings">
-            <div class="setting">
-                <div class="setting-name">Data flow name</div>
-                <div class="setting-field">
-                    <input type="text" id="data-flow-title-field" name="data-flow-title" class="setting-input"/>
-                    <span id="archive-flow-link" class="link">Back-up flow</span>
-                    <img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="Archives the flow configuration."/>
-                </div>
+        <div id="settings-tabs-container">
+            <div id="settings-tabs"></div>
+            <div id="settings-refresh-button" class="pointer" title="Refresh"></div>
+            <div id="settings-last-refreshed-container">
+                Last updated:&nbsp;<span id="settings-last-refreshed"></span>
             </div>
-            <div class="setting">
-                <div class="setting-name">Data flow comments</div>
-                <div class="setting-field">
-                    <textarea id="data-flow-comments-field" name="data-flow-comments" class="setting-input"></textarea>
+            <div id="settings-refresh-required-icon" class="hidden"></div>
+            <div id="settings-loading-container" class="loading-container"></div>
+            <div id="new-service-or-task" class="add-icon-bg"></div>
+            <div class="clear"></div>
+        </div>
+        <div id="settings-tab-background"></div>
+        <div id="settings-tabs-content">
+            <div id="general-settings-tab-content" class="configuration-tab">
+                <div id="general-settings">
+                    <div class="setting">
+                        <div class="setting-name">Data flow name</div>
+                        <div class="editable setting-field">
+                            <input type="text" id="data-flow-title-field" name="data-flow-title" class="setting-input"/>
+                            <span id="archive-flow-link" class="link">Back-up flow</span>
+                            <img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="Archives the flow configuration."/>
+                        </div>
+                        <div class="read-only setting-field">
+                            <span id="read-only-data-flow-title-field"></span>
+                        </div>
+                    </div>
+                    <div class="setting">
+                        <div class="setting-name">Data flow comments</div>
+                        <div class="editable setting-field">
+                            <textarea id="data-flow-comments-field" name="data-flow-comments" class="setting-input"></textarea>
+                        </div>
+                        <div class="read-only setting-field">
+                            <span id="read-only-data-flow-comments-field"></span>
+                        </div>
+                    </div>
+                    <div class="setting">
+                        <div class="setting-name">
+                            Maximum timer driven thread count
+                            <img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="The maximum number of threads for timer driven processors available to the system."/>
+                        </div>
+                        <div class="editable setting-field">
+                            <input type="text" id="maximum-timer-driven-thread-count-field" class="setting-input"/>
+                        </div>
+                        <div class="read-only setting-field">
+                            <span id="read-only-maximum-timer-driven-thread-count-field"></span>
+                        </div>
+                    </div>
+                    <div class="setting">
+                        <div class="setting-name">
+                            Maximum event driven thread count
+                            <img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="The maximum number of threads for event driven processors available to the system."/>
+                        </div>
+                        <div class="editable setting-field">
+                            <input type="text" id="maximum-event-driven-thread-count-field" class="setting-input"/>
+                        </div>
+                        <div class="read-only setting-field">
+                            <span id="read-only-maximum-event-driven-thread-count-field"></span>
+                        </div>
+                    </div>
+                    <div id="settings-buttons" class="editable">
+                        <div id="settings-save" class="button">Apply</div>
+                        <div class="clear"></div>
+                    </div>
                 </div>
             </div>
-            <div class="setting">
-                <div class="setting-name">
-                    Maximum timer driven thread count
-                    <img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="The maximum number of threads for timer driven processors available to the system."/>
-                </div>
-                <div class="setting-field">
-                    <input type="text" id="maximum-timer-driven-thread-count-field" class="setting-input"/>
-                </div>
+            <div id="controller-services-tab-content" class="configuration-tab">
+                <div id="controller-services-table" class="settings-table"></div>
             </div>
-            <div class="setting">
-                <div class="setting-name">
-                    Maximum event driven thread count
-                    <img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="The maximum number of threads for event driven processors available to the system."/>
-                </div>
-                <div class="setting-field">
-                    <input type="text" id="maximum-event-driven-thread-count-field" class="setting-input"/>
-                </div>
+            <div id="reporting-tasks-tab-content" class="configuration-tab">
+                <div id="reporting-tasks-table" class="settings-table"></div>
             </div>
         </div>
-        <div id="settings-buttons">
-            <div id="settings-cancel" class="button">Cancel</div>
-            <div id="settings-save" class="button">Apply</div>
-        </div>
     </div>
 </div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/processor-details.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/processor-details.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/processor-details.jsp
index abbd4c4..31df7c9 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/processor-details.jsp
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/processor-details.jsp
@@ -138,10 +138,6 @@
                 </div>
             </div>
             <div id="details-processor-properties-tab-content" class="details-tab">
-                <div id="read-only-processor-properties-header">
-                    <div id="read-only-required-property-note">Required field</div>
-                    <div class="clear"></div>
-                </div>
                 <div id="read-only-processor-properties"></div>
             </div>
             <div id="details-processor-comments-tab-content" class="details-tab">

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/about.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/about.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/about.css
index 5fd923b..df48ee8 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/about.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/about.css
@@ -22,7 +22,6 @@
     font-size: 8pt;
     z-index: 1201;
     display: none;
-    border: 1px solid #eee;
 }
 
 #nf-about-pic {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css
index cac2a3d..a8866ad 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css
@@ -20,6 +20,8 @@
 @import url(process-group-configuration.css);
 @import url(process-group-details.css);
 @import url(remote-process-group-configuration.css);
+@import url(controller-service.css);
+@import url(reporting-task.css);
 @import url(port-configuration.css);
 @import url(port-details.css);
 @import url(label-configuration.css);
@@ -29,6 +31,8 @@
 @import url(registration.css);
 @import url(dialog.css);
 @import url(new-processor-dialog.css);
+@import url(new-controller-service-dialog.css);
+@import url(new-reporting-task-dialog.css);
 @import url(graph.css);
 @import url(header.css);
 @import url(main.css);

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/connection-configuration.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/connection-configuration.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/connection-configuration.css
index 0e8ae66..5bcf010 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/connection-configuration.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/connection-configuration.css
@@ -19,7 +19,6 @@
     display: none;
     width: 700px;
     height: 400px;
-    border: 1px solid #eee;
 }
 
 div.connection-configuration-tab-container {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/connection-details.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/connection-details.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/connection-details.css
index 8d84cc4..3a3fdb9 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/connection-details.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/connection-details.css
@@ -23,7 +23,6 @@
     display: none;
     width: 700px;
     height: 400px;
-    border: 1px solid #eee;
 }
 
 div.connection-details-tab-container {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/controller-service.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/controller-service.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/controller-service.css
new file mode 100644
index 0000000..acc8487
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/controller-service.css
@@ -0,0 +1,269 @@
+/*
+ * 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.
+ */
+
+/*
+    Controller service configuration styles.
+*/
+
+#controller-service-configuration {
+    position: absolute;
+    overflow: hidden;
+    width: 800px;
+    height: 450px;
+    font-size: 10px;
+    z-index: 1301;
+    display: none;
+}
+
+div.controller-service-configuration-tab-container {
+    margin-top: -10px;
+    padding: 5px 11px;
+}
+
+#controller-service-configuration-advanced {
+    display: none;
+}
+
+#controller-service-configuration-tabs {
+    background-color: transparent;
+    width: 778px;
+    height: 21px;
+    border-bottom: 3px solid #666;
+}
+
+#controller-service-configuration div.configuration-tab {
+    height: 320px;
+    overflow: auto;
+    padding: 10px;
+    background: #eee url(../images/bgTabContainer.png) repeat-x;
+    display: none;
+}
+
+/* controller-service settings */
+
+#controller-service-configuration div.settings-left {
+    float: left;
+    width: 330px;
+}
+
+#controller-service-configuration div.settings-right {
+    float: left;
+    width: 382px;
+}
+
+#controller-service-configuration div.spacer {
+    float: left;
+    margin-right: 40px;
+}
+
+#controller-service-configuration .setting-input {
+    font-size: 11px !important;
+}
+
+#controller-service-name {
+    width: 250px;
+    float: left;
+}
+
+#controller-service-enabled {
+    width: 12px;
+    height: 12px;
+    float: left;
+    margin-right: 4px;
+}
+
+div.controller-service-enabled-container {
+    float: left;
+    margin-top: 5px;
+    margin-left: 10px;
+}
+
+div.availability-setting {
+    float: left;
+    width: 140px;
+}
+
+/*
+    Service references
+*/
+
+#controller-service-referencing-components {
+    border: 0 solid #CCCCCC;
+    height: 280px;
+    overflow: auto;
+    padding: 2px;
+    width: 376px;
+}
+
+div.referencing-component-block {
+    margin-bottom: 6px;
+}
+
+span.referencing-component-title {
+    margin-left: 5px;
+    font-weight: bold
+}
+
+span.referencing-component-count {
+    margin-left: 5px;
+    color: #aaa;
+}
+
+ul.referencing-component-listing {
+    margin-left: 20px;
+    margin-bottom: 6px;
+}
+
+ul.referencing-component-listing li {
+    margin-bottom: 6px;
+    white-space: nowrap;
+}
+
+div.referencing-component-state {
+    margin-top: -2px;
+}
+
+span.service.expansion-button {
+    margin-right: 4px;
+    margin-top: 2px;
+}
+
+span.referencing-component-active-thread-count {
+    margin-left: 5px;
+}
+
+span.referencing-component-name {
+    margin-left: 5px;
+    margin-right: 5px;
+}
+
+span.referencing-component-type {
+    color: #aaa;
+    font-style: italic;
+}
+
+div.referencing-component-references {
+    margin-left: 20px;
+    margin-top: 6px;
+    clear: left;
+}
+
+/*
+    Comments
+*/
+
+#controller-service-comments {
+    height: 250px;
+    width: 748px !important;
+    margin-top: 10px;
+}
+
+/*
+    Disable dialog
+*/
+
+#disable-controller-service-dialog {
+    z-index: 1301;
+    display: none;
+    width: 800px;
+    height: 450px;
+    line-height: normal;
+}
+
+#disable-controller-service-dialog div.settings-left {
+    float: left;
+    width: 300px;
+}
+
+#disable-controller-service-dialog div.settings-right {
+    float: left;
+    width: 435px;
+}
+
+#disable-controller-service-dialog div.spacer {
+    float: left;
+    margin-right: 40px;
+}
+
+#disable-controller-service-referencing-components {
+    border: 0 solid #CCCCCC;
+    padding: 2px;
+    width: 429px;
+    height: 335px;
+    overflow: auto;
+    white-space: nowrap;
+}
+
+/*
+    Enable dialog
+*/
+
+#enable-controller-service-dialog {
+    z-index: 1301;
+    display: none;
+    width: 800px;
+    height: 450px;
+    line-height: normal;
+}
+
+#enable-controller-service-dialog div.settings-left {
+    float: left;
+    width: 300px;
+}
+
+#enable-controller-service-dialog div.settings-right {
+    float: left;
+    width: 435px;
+}
+
+#enable-controller-service-dialog div.spacer {
+    float: left;
+    margin-right: 40px;
+}
+
+#enable-controller-service-scope {
+    float: left;
+    width: 225px;
+    height: 18px;
+    line-height: 18px;
+}
+
+#enable-controller-service-referencing-components {
+    border: 0 solid #ccc;
+    padding: 2px;
+    width: 429px;
+    height: 335px;
+    overflow: auto;
+    white-space: nowrap;
+}
+
+/* enable, disable progress */
+
+div.disable-referencing-components, div.enable-referencing-components {
+    width: 16px;
+    height: 16px;
+    background-color: transparent;
+    float: right;
+}
+
+#enable-controller-service-progress, #disable-controller-service-progress {
+    margin-top: 2px;
+}
+
+#enable-controller-service-progress li, #disable-controller-service-progress li {
+    line-height: 16px;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
index 1b658be..88555e7 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
@@ -23,7 +23,6 @@
     display: none;
     width: 350px;
     height: 150px;
-    border: 1px solid #eee;
 }
 
 #new-port-name {
@@ -35,7 +34,6 @@
     display: none;
     width: 350px;
     height: 150px;
-    border: 1px solid #eee;
 }
 
 #new-process-group-name {
@@ -47,7 +45,6 @@
     display: none;
     width: 350px;
     height: 150px;
-    border: 1px solid #eee;
 }
 
 #new-remote-process-group-uri {
@@ -59,29 +56,17 @@
     display: none;
     width: 350px;
     height: 300px;
-    border: 1px solid #eee;
 }
 
 .new-template-field {
     width: 320px;
 }
 
-#processor-property-dialog {
-    z-index: 1301;
-    display: none;
-    border: 1px solid #eee;
-    padding: 10px;
-    border: 3px solid #365C6A;
-    box-shadow: 4px 4px 6px rgba(0, 0, 0, 0.9);
-    cursor: move;
-}
-
 #alias-dialog {
     z-index: 1301;
     display: none;
     width: 350px;
     height: 195px;
-    border: 1px solid #eee;
 }
 
 #fill-color-dialog {
@@ -89,7 +74,6 @@
     display: none;
     width: 195px;
     height: 400px;
-    border: 1px solid #eee;
 }
 
 #fill-color-value {
@@ -156,7 +140,6 @@
     display: none;
     height: 365px;
     width: 646px;
-    border: 1px solid #eee;
 }
 
 div.connections-component-name {
@@ -264,7 +247,6 @@ div.go-to-link {
     display: none;
     height: 450px;
     width: 420px;
-    border: 1px solid #eee;
 }
 
 #instantiate-template-dialog {
@@ -272,7 +254,6 @@ div.go-to-link {
     display: none;
     width: 355px;
     height: 120px;
-    border: 1px solid #eee;
 }
 
 #available-templates {
@@ -286,7 +267,6 @@ div.go-to-link {
     display: none;
     width: 350px;
     height: 195px;
-    border: 1px solid #eee;
 }
 
 #nf-ok-dialog {
@@ -294,14 +274,12 @@ div.go-to-link {
     display: none;
     height: 225px;
     width: 375px;
-    border: 1px solid #eee;
 }
 
 #nf-ok-dialog-content {
     width: 355px;
     height: 130px;
     overflow: auto;
-    margin-top: -10px;
 }
 
 #nf-yes-no-dialog {
@@ -309,7 +287,6 @@ div.go-to-link {
     display: none;
     height: 175px;
     width: 350px;
-    border: 1px solid #eee;
 }
 
 #nf-yes-no-dialog-content {
@@ -352,4 +329,4 @@ img.icon-info {
     position: relative;
     left: 3px;
     top: 1px;
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/label-configuration.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/label-configuration.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/label-configuration.css
index 1b4850b..30af32a 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/label-configuration.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/label-configuration.css
@@ -20,7 +20,6 @@
 
 #label-configuration {
     font-size: 10px;
-    border: 1px solid #eee;
     z-index: 1301;
     display: none;
     padding: 10px;


[06/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
new file mode 100644
index 0000000..7fb3eba
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
@@ -0,0 +1,1730 @@
+/*
+ * 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.
+ */
+
+/* global nf, d3 */
+
+nf.ControllerService = (function () {
+
+    var config = {
+        edit: 'edit',
+        readOnly: 'read-only',
+        serviceOnly: 'SERVICE_ONLY',
+        serviceAndReferencingComponents: 'SERVICE_AND_REFERENCING_COMPONENTS'
+    };
+
+    /**
+     * Handle any expected controller service configuration errors.
+     * 
+     * @argument {object} xhr       The XmlHttpRequest
+     * @argument {string} status    The status of the request
+     * @argument {string} error     The error
+     */
+    var handleControllerServiceConfigurationError = function (xhr, status, error) {
+        if (xhr.status === 400) {
+            var errors = xhr.responseText.split('\n');
+
+            var content;
+            if (errors.length === 1) {
+                content = $('<span></span>').text(errors[0]);
+            } else {
+                content = nf.Common.formatUnorderedList(errors);
+            }
+
+            nf.Dialog.showOkDialog({
+                dialogContent: content,
+                overlayBackground: false,
+                headerText: 'Configuration Error'
+            });
+        } else {
+            nf.Common.handleAjaxError(xhr, status, error);
+        }
+    };
+
+    /**
+     * Determines whether the user has made any changes to the controller service configuration
+     * that needs to be saved.
+     */
+    var isSaveRequired = function () {
+        var details = $('#controller-service-configuration').data('controllerServiceDetails');
+
+        // determine if any controller service settings have changed
+
+        if ($('#controller-service-name').val() !== details['name']) {
+            return true;
+        }
+        if ($('#controller-service-comments').val() !== details['comments']) {
+            return true;
+        }
+        
+        if ($('#controller-service-enabled').hasClass('checkbox-checked') && details['state'] === 'DISABLED') {
+            return true;
+        } else if ($('#controller-service-enabled').hasClass('checkbox-unchecked') && details['state'] === 'ENABLED') {
+            return true;
+        }
+        
+        // defer to the properties
+        return $('#controller-service-properties').propertytable('isSaveRequired');
+    };
+
+    /**
+     * Marshals the data that will be used to update the contr oller service's configuration.
+     */
+    var marshalDetails = function () {
+        // properties
+        var properties = $('#controller-service-properties').propertytable('marshalProperties');
+
+        // create the controller service dto
+        var controllerServiceDto = {};
+        controllerServiceDto['id'] = $('#controller-service-id').text();
+        controllerServiceDto['name'] = $('#controller-service-name').val();
+        controllerServiceDto['comments'] = $('#controller-service-comments').val();
+        
+        // set the properties
+        if ($.isEmptyObject(properties) === false) {
+            controllerServiceDto['properties'] = properties;
+        }
+        
+        // mark the controller service enabled if appropriate
+        if ($('#controller-service-enabled').hasClass('checkbox-unchecked')) {
+            controllerServiceDto['state'] = 'DISABLED';
+        } else if ($('#controller-service-enabled').hasClass('checkbox-checked')) {
+            controllerServiceDto['state'] = 'ENABLED';
+        }
+
+        // create the controller service entity
+        var controllerServiceEntity = {};
+        controllerServiceEntity['revision'] = nf.Client.getRevision();
+        controllerServiceEntity['controllerService'] = controllerServiceDto;
+
+        // return the marshaled details
+        return controllerServiceEntity;
+    };
+
+    /**
+     * Validates the specified details.
+     * 
+     * @argument {object} details       The details to validate
+     */
+    var validateDetails = function (details) {
+        return true;
+    };
+    
+    /**
+     * Reloads the specified controller service. It's referencing and referenced
+     * components are NOT reloaded.
+     * 
+     * @param {string} id
+     */
+    var reloadControllerService = function (id) {
+        // get the table and update the row accordingly
+        var controllerServiceGrid = $('#controller-services-table').data('gridInstance');
+        var controllerServiceData = controllerServiceGrid.getData();
+        var controllerService = controllerServiceData.getItemById(id);
+        
+        // this may happen if controller service A references another controller
+        // service B that has been removed. attempting to enable/disable/remove A
+        // will attempt to reload B which is no longer a known service
+        if (nf.Common.isUndefined(controllerService)) {
+            return $.Deferred(function (deferred) {
+                deferred.reject();
+            }).promise();
+        }
+        
+        return $.ajax({
+            type: 'GET',
+            url: controllerService.uri,
+            dataType: 'json'
+        }).done(function (response) {
+            renderControllerService(response.controllerService);
+        }).fail(nf.Common.handleAjaxError);
+    };
+    
+    /**
+     * Renders the specified controller service.
+     * 
+     * @param {object} controllerService
+     */
+    var renderControllerService = function (controllerService) {
+        // get the table and update the row accordingly
+        var controllerServiceGrid = $('#controller-services-table').data('gridInstance');
+        var controllerServiceData = controllerServiceGrid.getData();
+        controllerServiceData.updateItem(controllerService.id, controllerService);
+    };
+    
+    /**
+     * Reloads the specified controller services and all of its referencing 
+     * and referenced components.
+     * 
+     * @param {type} controllerService
+     */
+    var reloadControllerServiceAndReferencingComponents = function (controllerService) {
+        reloadControllerService(controllerService.id).done(function(response) {
+            reloadControllerServiceReferences(response.controllerService);
+        });
+    };
+    
+    /**
+     * Reloads components that reference this controller service as well as
+     * other services that this controller service references.
+     * 
+     * @param {object} controllerService
+     */
+    var reloadControllerServiceReferences = function (controllerService) {
+        // reload all dependent processors if they are currently visible
+        $.each(controllerService.referencingComponents, function (_, reference) {
+            if (reference.referenceType === 'Processor') {
+                // reload the processor on the canvas if appropriate
+                if (nf.Canvas.getGroupId() === reference.groupId) {
+                    var processor = nf.Processor.get(reference.id);
+                    nf.Processor.reload(processor.component);
+                }
+                
+                // update the current active thread count
+                $('div.' + reference.id + '-active-threads').text(reference.activeThreadCount);
+                
+                // update the current state of this processor
+                var referencingComponentState = $('div.' + reference.id + '-state');
+                if (referencingComponentState.length) {
+                    updateReferencingSchedulableComponentState(referencingComponentState, reference);
+                }
+            } else if (reference.referenceType === 'ReportingTask') {
+                // reload the referencing reporting tasks
+                nf.ReportingTask.reload(reference.id);
+                
+                // update the current active thread count
+                $('div.' + reference.id + '-active-threads').text(reference.activeThreadCount);
+                
+                // update the current state of this reporting task
+                var referencingComponentState = $('div.' + reference.id + '-state');
+                if (referencingComponentState.length) {
+                    updateReferencingSchedulableComponentState(referencingComponentState, reference);
+                }
+            } else {
+                // reload the referencing services
+                reloadControllerService(reference.id);
+                
+                // update the current state of this service
+                var referencingComponentState = $('div.' + reference.id + '-state');
+                if (referencingComponentState.length) {
+                    updateReferencingServiceState(referencingComponentState, reference);
+                }
+                
+                // consider it's referencing components if appropriate
+                if (reference.referenceCycle === false) {
+                    reloadControllerServiceReferences(reference);
+                }
+            }
+        });
+
+        // see if this controller service references another controller service
+        // in order to update the referenced service referencing components
+        nf.ControllerService.reloadReferencedServices(controllerService);
+    };   
+    
+    /**
+     * Adds a border to the controller service referencing components if necessary.
+     * 
+     * @argument {jQuery} referenceContainer 
+     */
+    var updateReferencingComponentsBorder = function (referenceContainer) {
+        // determine if it is too big
+        var tooBig = referenceContainer.get(0).scrollHeight > referenceContainer.innerHeight() ||
+                referenceContainer.get(0).scrollWidth > referenceContainer.innerWidth();
+        
+        // draw the border if necessary
+        if (referenceContainer.is(':visible') && tooBig) {
+            referenceContainer.css('border-width', '1px');
+        } else {
+            referenceContainer.css('border-width', '0px');
+        }
+    };
+    
+    /**
+     * Updates the referencingComponentState using the specified referencingComponent.
+     * 
+     * @param {jQuery} referencingComponentState
+     * @param {object} referencingComponent
+     */
+    var updateReferencingSchedulableComponentState = function (referencingComponentState, referencingComponent) {
+        referencingComponentState.removeClass('disabled stopped running invalid').addClass(function() {
+            var icon = $(this);
+
+            var state = referencingComponent.state.toLowerCase();
+            if (state === 'stopped' && !nf.Common.isEmpty(referencingComponent.validationErrors)) {
+                state = 'invalid';
+
+                // add tooltip for the warnings
+                var list = nf.Common.formatUnorderedList(referencingComponent.validationErrors);
+                if (icon.data('qtip')) {
+                    icon.qtip('option', 'content.text', list);
+                } else {
+                    icon.qtip($.extend({
+                        content: list
+                    }, nf.CanvasUtils.config.systemTooltipConfig));
+                }
+            } else if (icon.data('qtip')) {
+                icon.qtip('destroy');
+            }
+            return state;
+        });
+    };
+    
+    /**
+     * Updates the referencingServiceState using the specified referencingService.
+     * 
+     * @param {jQuery} referencingServiceState
+     * @param {object} referencingService
+     */
+    var updateReferencingServiceState = function (referencingServiceState, referencingService) {
+        referencingServiceState.removeClass('disabled enabled invalid').addClass(function() {
+            var icon = $(this);
+
+            var state = referencingService.state === 'ENABLED' ? 'enabled' : 'disabled';
+            if (state === 'disabled' && !nf.Common.isEmpty(referencingService.validationErrors)) {
+                state = 'invalid';
+
+                // add tooltip for the warnings
+                var list = nf.Common.formatUnorderedList(referencingService.validationErrors);
+                if (icon.data('qtip')) {
+                    icon.qtip('option', 'content.text', list);
+                } else {
+                    icon.qtip($.extend({
+                        content: list
+                    }, nf.CanvasUtils.config.systemTooltipConfig));
+                }
+            } else if (icon.data('qtip')) {
+                icon.qtip('destroy');
+            }
+            return state;
+        });
+    };
+    
+    /**
+     * Adds the specified reference for this controller service.
+     * 
+     * @param {jQuery} referenceContainer 
+     * @param {array} referencingComponents
+     */
+    var createReferencingComponents = function (referenceContainer, referencingComponents) {
+        if (nf.Common.isEmpty(referencingComponents)) {
+            referenceContainer.append('<div class="unset">No referencing components.</div>');
+            return;
+        }
+        
+        // toggles the visibility of a container
+        var toggle = function (twist, container) {
+            if (twist.hasClass('expanded')) {
+                twist.removeClass('expanded').addClass('collapsed');
+                container.hide();
+            } else {
+                twist.removeClass('collapsed').addClass('expanded');
+                container.show();
+            }
+        };
+        
+        var processors = $('<ul class="referencing-component-listing clear"></ul>');
+        var services = $('<ul class="referencing-component-listing clear"></ul>');
+        var tasks = $('<ul class="referencing-component-listing clear"></ul>');
+        $.each(referencingComponents, function (_, referencingComponent) {
+            if (referencingComponent.referenceType === 'Processor') {
+                var processorLink = $('<span class="referencing-component-name link"></span>').text(referencingComponent.name).on('click', function () {
+                    // show the component
+                    nf.CanvasUtils.showComponent(referencingComponent.groupId, referencingComponent.id);
+                    
+                    // close the dialog and shell
+                    referenceContainer.closest('.dialog').modal('hide');
+                    $('#shell-close-button').click();
+                });
+
+                // state
+                var processorState = $('<div class="referencing-component-state"></div>').addClass(referencingComponent.id + '-state');
+                updateReferencingSchedulableComponentState(processorState, referencingComponent);
+                
+                // type
+                var processorType = $('<span class="referencing-component-type"></span>').text(nf.Common.substringAfterLast(referencingComponent.type, '.'));
+                
+                // active thread count
+                var processorActiveThreadCount = $('<span class="referencing-component-active-thread-count"></span>').addClass(referencingComponent.id + '-active-threads');
+                if (nf.Common.isDefinedAndNotNull(referencingComponent.activeThreadCount) && referencingComponent.activeThreadCount > 0) {
+                    processorActiveThreadCount.text('(' + referencingComponent.activeThreadCount + ')');
+                }
+                
+                // processor
+                var processorItem = $('<li></li>').append(processorState).append(processorLink).append(processorType).append(processorActiveThreadCount);
+                processors.append(processorItem);
+            } else if (referencingComponent.referenceType === 'ControllerService') {
+                var serviceLink = $('<span class="referencing-component-name link"></span>').text(referencingComponent.name).on('click', function () {
+                    var controllerServiceGrid = $('#controller-services-table').data('gridInstance');
+                    var controllerServiceData = controllerServiceGrid.getData();
+                    
+                    // select the selected row
+                    var row = controllerServiceData.getRowById(referencingComponent.id);
+                    controllerServiceGrid.setSelectedRows([row]);
+                    
+                    // close the dialog and shell
+                    referenceContainer.closest('.dialog').modal('hide');
+                });
+                
+                // container for this service's references
+                var referencingServiceReferencesContainer = $('<div class="referencing-component-references hidden"></div>');
+                var serviceTwist = $('<span class="service expansion-button collapsed pointer"></span>').on('click', function() {
+                    if (serviceTwist.hasClass('collapsed')) {
+                        var controllerServiceGrid = $('#controller-services-table').data('gridInstance');
+                        var controllerServiceData = controllerServiceGrid.getData();
+                        var referencingService = controllerServiceData.getItemById(referencingComponent.id);
+                        
+                        // create the markup for the references
+                        createReferencingComponents(referencingServiceReferencesContainer, referencingService.referencingComponents);
+                    } else {
+                        referencingServiceReferencesContainer.empty();
+                    }
+                    
+                    // toggle visibility
+                    toggle(serviceTwist, referencingServiceReferencesContainer);
+                    
+                    // update borders as necessary
+                    updateReferencingComponentsBorder(referenceContainer);
+                });
+                
+                // state
+                var serviceState = $('<div class="referencing-component-state"></div>').addClass(referencingComponent.id + '-state');
+                updateReferencingServiceState(serviceState, referencingComponent);
+                
+                // type
+                var serviceType = $('<span class="referencing-component-type"></span>').text(nf.Common.substringAfterLast(referencingComponent.type, '.'));
+                
+                // service
+                var serviceItem = $('<li></li>').append(serviceTwist).append(serviceState).append(serviceLink).append(serviceType).append(referencingServiceReferencesContainer);
+                
+                services.append(serviceItem);
+            } else if (referencingComponent.referenceType === 'ReportingTask') {
+                var reportingTaskLink = $('<span class="referencing-component-name link"></span>').text(referencingComponent.name).on('click', function () {
+                    var reportingTaskGrid = $('#reporting-tasks-table').data('gridInstance');
+                    var reportingTaskData = reportingTaskGrid.getData();
+                    
+                    // select the selected row
+                    var row = reportingTaskData.getRowById(referencingComponent.id);
+                    reportingTaskGrid.setSelectedRows([row]);
+                    
+                    // select the reporting task tab
+                    $('#settings-tabs').find('li:last').click();
+                    
+                    // close the dialog and shell
+                    referenceContainer.closest('.dialog').modal('hide');
+                });
+                
+                // state
+                var reportingTaskState = $('<div class="referencing-component-state"></div>').addClass(referencingComponent.id + '-state');
+                updateReferencingSchedulableComponentState(reportingTaskState, referencingComponent);
+                
+                // type
+                var reportingTaskType = $('<span class="referencing-component-type"></span>').text(nf.Common.substringAfterLast(referencingComponent.type, '.'));
+                
+                // active thread count
+                var reportingTaskActiveThreadCount = $('<span class="referencing-component-active-thread-count"></span>').addClass(referencingComponent.id + '-active-threads');
+                if (nf.Common.isDefinedAndNotNull(referencingComponent.activeThreadCount) && referencingComponent.activeThreadCount > 0) {
+                    reportingTaskActiveThreadCount.text('(' + referencingComponent.activeThreadCount + ')');
+                }
+                
+                // reporting task
+                var reportingTaskItem = $('<li></li>').append(reportingTaskState).append(reportingTaskLink).append(reportingTaskType).append(reportingTaskActiveThreadCount);
+                tasks.append(reportingTaskItem);
+            }
+        });
+        
+        // create the collapsable listing for each type
+        var createReferenceBlock = function (titleText, list) {
+            if (list.is(':empty')) {
+                list.remove();
+                return;
+            }
+            
+            var twist = $('<span class="expansion-button expanded"></span>');
+            var title = $('<span class="referencing-component-title"></span>').text(titleText);
+            var count = $('<span class="referencing-component-count"></span>').text('(' + list.children().length + ')');
+            
+            // create the reference block
+            $('<div class="referencing-component-block pointer unselectable"></div>').on('click', function () {
+                // toggle this block
+                toggle(twist, list);
+                
+                // update the border if necessary
+                updateReferencingComponentsBorder(referenceContainer);
+            }).append(twist).append(title).append(count).appendTo(referenceContainer);
+            
+            // add the listing
+            list.appendTo(referenceContainer);
+        };
+        
+        // create blocks for each type of component
+        createReferenceBlock('Processors', processors);
+        createReferenceBlock('Reporting Tasks', tasks);
+        createReferenceBlock('Controller Services', services);
+    };
+    
+    /**
+     * Sets whether the specified controller service is enabled.
+     * 
+     * @param {object} controllerService
+     * @param {boolean} enabled
+     * @param {function} pollCondition
+     */
+    var setEnabled = function (controllerService, enabled, pollCondition) {
+        var revision = nf.Client.getRevision();
+        
+        var updated = $.ajax({
+            type: 'PUT',
+            url: controllerService.uri,
+            data: {
+                clientId: revision.clientId,
+                version: revision.version,
+                state: enabled === true ? 'ENABLED' : 'DISABLED'
+            },
+            dataType: 'json'
+        }).done(function (response) {
+            nf.Client.setRevision(response.revision);
+        }).fail(nf.Common.handleAjaxError);
+        
+        // wait unil the polling of each service finished
+        return $.Deferred(function(deferred) {
+            updated.done(function() {
+                var serviceUpdated = pollService(controllerService, function (service) {
+                    // the condition is met once the service is ENABLED/DISABLED
+                    if (enabled) {
+                        return service.state === 'ENABLED';
+                    } else {
+                        return service.state === 'DISABLED';
+                    }
+                }, pollCondition);
+
+                // once the service has updated, resolve and render the updated service
+                serviceUpdated.done(function () {
+                    deferred.resolve();
+                }).fail(function() {
+                    deferred.reject();
+                });
+            }).fail(function() {
+                deferred.reject();
+            });
+        }).promise();
+    };
+    
+    /**
+     * Gets the id's of all controller services referencing the specified controller service.
+     * 
+     * @param {object} controllerService
+     */
+    var getReferencingControllerServiceIds = function (controllerService) {
+        var ids = d3.set();
+        ids.add(controllerService.id);
+        
+        var checkReferencingServices = function (referencingComponents) {
+            $.each(referencingComponents, function (_, referencingComponent) {
+                if (referencingComponent.referenceType === 'ControllerService') {
+                    // add the id
+                    ids.add(referencingComponent.id);
+                    
+                    // consider it's referencing components if appropriate
+                    if (referencingComponent.referenceCycle === false) {
+                        checkReferencingServices(referencingComponent.referencingComponents);
+                    }
+                }
+            });
+        };
+
+        // check the referencing servcies
+        checkReferencingServices(controllerService.referencingComponents);
+        return ids;
+    };
+    
+    /**
+     * Updates the scheduled state of the processors/reporting tasks referencing
+     * the specified controller service.
+     * 
+     * @param {object} controllerService
+     * @param {boolean} running
+     * @param {function} pollCondition
+     */
+    var updateReferencingSchedulableComponents = function (controllerService, running, pollCondition) {
+        var revision = nf.Client.getRevision();
+        
+        // issue the request to update the referencing components
+        var updated = $.ajax({
+            type: 'PUT',
+            url: controllerService.uri + '/references',
+            data: {
+                clientId: revision.clientId,
+                version: revision.version,
+                state: running ? 'RUNNING' : 'STOPPED'
+            },
+            dataType: 'json'
+        }).done(function (response) {
+            nf.Client.setRevision(response.revision);
+        }).fail(nf.Common.handleAjaxError);
+        
+        // wait unil the polling of each service finished
+        return $.Deferred(function(deferred) {
+            updated.done(function(response) {
+                // update the controller service
+                controllerService.referencingComponents = response.controllerServiceReferencingComponents;
+                
+                // if we're just starting schedulable components we're done when the update is finished
+                if (running) {
+                    deferred.resolve();
+                } else {
+                    // identify all referencing services
+                    var services = getReferencingControllerServiceIds(controllerService);
+
+                    // get the controller service grid
+                    var controllerServiceGrid = $('#controller-services-table').data('gridInstance');
+                    var controllerServiceData = controllerServiceGrid.getData();
+
+                    // start polling for each controller service
+                    var polling = [];
+                    services.forEach(function(controllerServiceId) {
+                        var referencingService = controllerServiceData.getItemById(controllerServiceId);
+                        polling.push(stopReferencingSchedulableComponents(referencingService, pollCondition));
+                    });
+                    
+                    // wait until polling has finished
+                    $.when.apply(window, polling).done(function () {
+                        deferred.resolve();
+                    }).fail(function() {
+                        deferred.reject();
+                    });
+                }
+            }).fail(function() {
+                deferred.reject();
+            });
+        }).promise();
+    };
+    
+    /**
+     * Polls the specified services referencing components to see if the
+     * specified condition is satisfied.
+     * 
+     * @param {object} controllerService
+     * @param {function} completeCondition
+     * @param {function} pollCondition
+     */
+    var pollService = function (controllerService, completeCondition, pollCondition) {
+        // we want to keep polling until the condition is met
+        return $.Deferred(function(deferred) {
+            var current = 2;
+            var getTimeout = function () {
+                var val = current;
+                
+                // update the current timeout for the next time
+                current = Math.max(current * 2, 8);
+                
+                return val * 1000;
+            };
+            
+            // polls for the current status of the referencing components
+            var poll = function() {
+                $.ajax({
+                    type: 'GET',
+                    url: controllerService.uri,
+                    dataType: 'json'
+                }).done(function (response) {
+                    conditionMet(response.controllerService);
+                }).fail(function (xhr, status, error) {
+                    deferred.reject();
+                    nf.Common.handleAjaxError(xhr, status, error);
+                });
+            };
+            
+            // tests to if the condition has been met
+            var conditionMet = function (service) {
+                if (completeCondition(service)) {
+                    deferred.resolve();
+                } else {
+                    if (typeof pollCondition === 'function' && pollCondition()) {
+                        setTimeout(poll(), getTimeout());
+                    } else {
+                        deferred.reject();
+                    }
+                }
+            };
+            
+            // poll for the status of the referencing components
+            conditionMet(controllerService);
+        }).promise();
+    };
+    
+    /**
+     * Continues to poll the specified controller service until all referencing schedulable 
+     * components are stopped (not scheduled and 0 active threads).
+     * 
+     * @param {object} controllerService
+     * @param {function} pollCondition
+     */
+    var stopReferencingSchedulableComponents = function (controllerService, pollCondition) {
+        // continue to poll the service until all schedulable components have stopped
+        return pollService(controllerService, function (service) {
+            var referencingComponents = service.referencingComponents;
+            
+            var stillRunning = false;
+            $.each(referencingComponents, function(_, referencingComponent) {
+                if (referencingComponent.referenceType === 'Processor' || referencingComponent.referenceType === 'ReportingTask') {
+                    if (referencingComponent.state !== 'STOPPED' || referencingComponent.activeThreadCount > 0) {
+                        stillRunning = true;
+                    }
+                    
+                    // update the current active thread count
+                    $('div.' + referencingComponent.id + '-active-threads').text(referencingComponent.activeThreadCount);
+                    
+                    // update the current state of this component
+                    var referencingComponentState = $('div.' + referencingComponent.id + '-state');
+                    updateReferencingSchedulableComponentState(referencingComponentState, referencingComponent);
+                }
+            });
+
+            // condition is met once all referencing are not running
+            return stillRunning === false;
+        }, pollCondition);
+    };
+    
+    /**
+     * Continues to poll until all referencing services are enabled.
+     * 
+     * @param {object} controllerService
+     * @param {function} pollCondition
+     */
+    var enableReferencingServices = function (controllerService, pollCondition) {
+        // continue to poll the service until all referencing services are enabled
+        return pollService(controllerService, function (service) {
+            var referencingComponents = service.referencingComponents;
+            
+            var notEnabled = false;
+            $.each(referencingComponents, function(_, referencingComponent) {
+                if (referencingComponent.referenceType === 'ControllerService') {
+                    if (referencingComponent.state !== 'ENABLED') {
+                        notEnabled = true;
+                    } 
+                        
+                    // update the state of the referencing service
+                    var referencingServiceState = $('div.' + referencingComponent.id + '-state');
+                    updateReferencingServiceState(referencingServiceState, referencingComponent);
+                }
+            });
+
+            // condition is met once all referencing are not disabled
+            return notEnabled === false;
+        }, pollCondition);
+    };
+    
+    /**
+     * Continues to poll until all referencing services are disabled.
+     * 
+     * @param {object} controllerService
+     * @param {function} pollCondition
+     */
+    var disableReferencingServices = function (controllerService, pollCondition) {
+        // continue to poll the service until all referencing services are disabled
+        return pollService(controllerService, function (service) {
+            var referencingComponents = service.referencingComponents;
+            
+            var notDisabled = false;
+            $.each(referencingComponents, function(_, referencingComponent) {
+                if (referencingComponent.referenceType === 'ControllerService') {
+                    if (referencingComponent.state !== 'DISABLED') {
+                        notDisabled = true;
+                    } 
+                        
+                    // update the state of the referencing service
+                    var referencingServiceState = $('div.' + referencingComponent.id + '-state');
+                    updateReferencingServiceState(referencingServiceState, referencingComponent);
+                }
+            });
+
+            // condition is met once all referencing are not enabled
+            return notDisabled === false;
+        }, pollCondition);
+    };
+    
+    /**
+     * Updates the referencing services with the specified state.
+     * 
+     * @param {object} controllerService
+     * @param {boolean} enabled
+     * @param {function} pollCondition
+     */
+    var updateReferencingServices = function (controllerService, enabled, pollCondition) {
+        var revision = nf.Client.getRevision();
+        
+        // issue the request to update the referencing components
+        var updated = $.ajax({
+            type: 'PUT',
+            url: controllerService.uri + '/references',
+            data: {
+                clientId: revision.clientId,
+                version: revision.version,
+                state: enabled ? 'ENABLED' : 'DISABLED'
+            },
+            dataType: 'json'
+        }).done(function (response) {
+            nf.Client.setRevision(response.revision);
+        }).fail(nf.Common.handleAjaxError);
+        
+        // wait unil the polling of each service finished
+        return $.Deferred(function(deferred) {
+            updated.done(function(response) {
+                // update the controller service
+                controllerService.referencingComponents = response.controllerServiceReferencingComponents;
+                
+                // identify all referencing services
+                var services = getReferencingControllerServiceIds(controllerService);
+
+                // get the controller service grid
+                var controllerServiceGrid = $('#controller-services-table').data('gridInstance');
+                var controllerServiceData = controllerServiceGrid.getData();
+
+                // start polling for each controller service
+                var polling = [];
+                services.forEach(function(controllerServiceId) {
+                    var referencingService = controllerServiceData.getItemById(controllerServiceId);
+                    
+                    if (enabled) {
+                        polling.push(enableReferencingServices(referencingService, pollCondition));
+                    } else {
+                        polling.push(disableReferencingServices(referencingService, pollCondition));
+                    }
+                });
+
+                $.when.apply(window, polling).done(function () {
+                    deferred.resolve();
+                }).fail(function() {
+                    deferred.reject();
+                });
+            }).fail(function() {
+                deferred.reject();
+            });
+        }).promise();
+    };
+    
+    /**
+     * Shows the dialog for disabling a controller service.
+     * 
+     * @argument {object} controllerService The controller service to disable
+     */
+    var showDisableControllerServiceDialog = function (controllerService) {
+        // populate the disable controller service dialog
+        $('#disable-controller-service-id').text(controllerService.id);
+        $('#disable-controller-service-name').text(controllerService.name);
+        
+        // load the controller referencing components list
+        var referencingComponentsContainer = $('#disable-controller-service-referencing-components');
+        createReferencingComponents(referencingComponentsContainer, controllerService.referencingComponents);
+        
+        // show the dialog
+        $('#disable-controller-service-dialog').modal('show');
+        
+        // update the border if necessary
+        updateReferencingComponentsBorder(referencingComponentsContainer);
+    };
+    
+    /**
+     * Shows the dialog for enabling a controller service.
+     * 
+     * @param {object} controllerService
+     */
+    var showEnableControllerServiceDialog = function (controllerService) {
+        // populate the disable controller service dialog
+        $('#enable-controller-service-id').text(controllerService.id);
+        $('#enable-controller-service-name').text(controllerService.name);
+        
+        // load the controller referencing components list
+        var referencingComponentsContainer = $('#enable-controller-service-referencing-components');
+        createReferencingComponents(referencingComponentsContainer, controllerService.referencingComponents);
+        
+        // show the dialog
+        $('#enable-controller-service-dialog').modal('show');
+        
+        // update the border if necessary
+        updateReferencingComponentsBorder(referencingComponentsContainer);
+    };
+    
+    /**
+     * Used to handle closing a modal dialog
+     */
+    var closeModal = function() {
+        $(this).modal('hide');
+    };
+    
+    /**
+     * Handles the disable action of the disable controller service dialog.
+     */
+    var disableHandler = function() {
+        var disableDialog = $(this);
+        var canceled = false;
+                            
+        // only provide a cancel option
+        disableDialog.modal('setButtonModel', [{
+            buttonText: 'Cancel',
+            handler: {
+                click: function () {
+                    canceled = true;
+                }
+            }
+        }]);
+
+        // show the progress
+        $('#disable-controller-service-service-container').hide();
+        $('#disable-controller-service-scope-container').hide();
+        $('#disable-controller-service-progress-container').show();
+
+        // get the controller service
+        var controllerServiceId = $('#disable-controller-service-id').text();
+        var controllerServiceGrid = $('#controller-services-table').data('gridInstance');
+        var controllerServiceData = controllerServiceGrid.getData();
+        var controllerService = controllerServiceData.getItemById(controllerServiceId);
+
+        // whether or not to continue polling
+        var continuePolling = function () {
+            return canceled === false;
+        };
+        
+        // sets the close button on the dialog
+        var setCloseButton = function () {
+            disableDialog.modal('setButtonModel', [{
+                buttonText: 'Close',
+                handler: {
+                    click: closeModal
+                }
+            }]);
+        };
+
+        $('#disable-progress-label').text('Steps to disable ' + controllerService.name);
+        var disableReferencingSchedulable = $('#disable-referencing-schedulable').addClass('ajax-loading');
+
+        $.Deferred(function (deferred) {
+            // stop all referencing schedulable components
+            var stopped = updateReferencingSchedulableComponents(controllerService, false, continuePolling);
+
+            // once everything has stopped
+            stopped.done(function () {
+                disableReferencingSchedulable.removeClass('ajax-loading').addClass('ajax-complete');
+                var disableReferencingServices = $('#disable-referencing-services').addClass('ajax-loading');
+
+                // disable all referencing services
+                var disabled = updateReferencingServices(controllerService, false, continuePolling);
+
+                // everything is disabled
+                disabled.done(function () {
+                    disableReferencingServices.removeClass('ajax-loading').addClass('ajax-complete');
+                    var disableControllerService = $('#disable-controller-service').addClass('ajax-loading');
+
+                    // disable this service
+                    setEnabled(controllerService, false, continuePolling).done(function () {
+                        deferred.resolve();
+                        disableControllerService.removeClass('ajax-loading').addClass('ajax-complete');
+                    }).fail(function () {
+                        deferred.reject();
+                        disableControllerService.removeClass('ajax-loading').addClass('ajax-error');
+                    });
+                }).fail(function () {
+                    deferred.reject();
+                    disableReferencingServices.removeClass('ajax-loading').addClass('ajax-error');
+                });
+            }).fail(function () {
+                deferred.reject();
+                disableReferencingSchedulable.removeClass('ajax-loading').addClass('ajax-error');
+            });
+        }).always(function () {
+            reloadControllerServiceAndReferencingComponents(controllerService);
+            setCloseButton();
+        });
+    };
+    
+    /**
+     * Handles the enable action of the enable controller service dialog.
+     */
+    var enableHandler = function() {
+        var enableDialog = $(this);
+        var canceled = false;
+                            
+        // only provide a cancel option
+        enableDialog.modal('setButtonModel', [{
+            buttonText: 'Cancel',
+            handler: {
+                click: function () {
+                    canceled = true;
+                }
+            }
+        }]);
+
+        // determine if we want to also activate referencing components
+        var scope = $('#enable-controller-service-scope').combo('getSelectedOption').value;
+        if (scope === config.serviceOnly) {
+            $('#enable-controller-service-progress li.referencing-component').hide();
+        }
+
+        // show the progress
+        $('#enable-controller-service-service-container').hide();
+        $('#enable-controller-service-scope-container').hide();
+        $('#enable-controller-service-progress-container').show();
+
+        // get the controller service
+        var controllerServiceId = $('#enable-controller-service-id').text();
+        var controllerServiceGrid = $('#controller-services-table').data('gridInstance');
+        var controllerServiceData = controllerServiceGrid.getData();
+        var controllerService = controllerServiceData.getItemById(controllerServiceId);
+
+        // whether or not to continue polling
+        var continuePolling = function () {
+            return canceled === false;
+        };
+
+        // sets the button to close
+        var setCloseButton = function () {
+            enableDialog.modal('setButtonModel', [{
+                buttonText: 'Close',
+                handler: {
+                    click: closeModal
+                }
+            }]);
+        };
+
+        $('#enable-progress-label').text('Steps to enable ' + controllerService.name);
+        var enableControllerService = $('#enable-controller-service').addClass('ajax-loading');
+
+        $.Deferred(function (deferred) {
+            // enable this controller service
+            var enable = setEnabled(controllerService, true, continuePolling);
+
+            if (scope === config.serviceAndReferencingComponents) {
+                // once the service is enabled, activate all referencing components
+                enable.done(function() {
+                    enableControllerService.removeClass('ajax-loading').addClass('ajax-complete');
+                    var enableReferencingServices = $('#enable-referencing-services').addClass('ajax-loading');
+
+                    // enable the referencing services
+                    var servicesEnabled = updateReferencingServices(controllerService, true, continuePolling);
+
+                    // once all the referencing services are enbled
+                    servicesEnabled.done(function () {
+                        enableReferencingServices.removeClass('ajax-loading').addClass('ajax-complete');
+                        var enableReferencingSchedulable = $('#enable-referencing-schedulable').addClass('ajax-loading');
+
+                        // start all referencing schedulable components
+                        updateReferencingSchedulableComponents(controllerService, true, continuePolling).done(function() {
+                            deferred.resolve();
+                            enableReferencingSchedulable.removeClass('ajax-loading').addClass('ajax-complete');
+                        }).fail(function () {
+                            deferred.reject();
+                            enableReferencingSchedulable.removeClass('ajax-loading').addClass('ajax-error');
+                        });
+                    }).fail(function () {
+                        deferred.reject();
+                        enableReferencingServices.removeClass('ajax-loading').addClass('ajax-error');
+                    });
+                }).fail(function () {
+                    deferred.reject();
+                    enableControllerService.removeClass('ajax-loading').addClass('ajax-error');
+                });
+            } else {
+                enable.done(function() {
+                    deferred.resolve();
+                    enableControllerService.removeClass('ajax-loading').addClass('ajax-complete');
+                }).fail(function () {
+                    deferred.reject();
+                    enableControllerService.removeClass('ajax-loading').addClass('ajax-error');
+                });
+            }
+        }).always(function () {
+            reloadControllerServiceAndReferencingComponents(controllerService);
+            setCloseButton();
+        });
+    };
+    
+    /**
+     * Gets a property descriptor for the controller service currently being configured.
+     * 
+     * @param {type} propertyName
+     */
+    var getControllerServicePropertyDescriptor = function (propertyName) {
+        var details = $('#controller-service-configuration').data('controllerServiceDetails');
+        return $.ajax({
+            type: 'GET',
+            url: details.uri + '/descriptors',
+            data: {
+                propertyName: propertyName
+            },
+            dataType: 'json'
+        }).fail(nf.Common.handleAjaxError);
+    };
+    
+    /**
+     * Identifies the descriptors that identify controller services.
+     * 
+     * @param {object} component
+     */
+    var identifyReferencedServiceDescriptors = function (component) {
+        var referencedServiceDescriptors = [];
+        
+        $.each(component.descriptors, function(_, descriptor) {
+            if (descriptor.identifiesControllerService === true) {
+                referencedServiceDescriptors.push(descriptor);
+            }
+        });
+        
+        return referencedServiceDescriptors;
+    };
+    
+    /**
+     * Identifies descritpors that reference controller services.
+     * 
+     * @param {object} component
+     */
+    var getReferencedServices = function (component) {
+        var referencedServices = [];
+        
+        $.each(identifyReferencedServiceDescriptors(component), function(_, descriptor) {
+            var referencedServiceId = component.properties[descriptor.name];
+
+            // ensure the property is configured
+            if (nf.Common.isDefinedAndNotNull(referencedServiceId) && $.trim(referencedServiceId).length > 0) {
+                referencedServices.push(referencedServiceId);
+            }
+        });
+        
+        return referencedServices;
+    };
+    
+    return {
+        /**
+         * Initializes the controller service configuration dialog.
+         */
+        init: function () {
+            // initialize the configuration dialog tabs
+            $('#controller-service-configuration-tabs').tabbs({
+                tabStyle: 'tab',
+                selectedTabStyle: 'selected-tab',
+                tabs: [{
+                        name: 'Settings',
+                        tabContentId: 'controller-service-standard-settings-tab-content'
+                    }, {
+                        name: 'Properties',
+                        tabContentId: 'controller-service-properties-tab-content'
+                    }, {
+                        name: 'Comments',
+                        tabContentId: 'controller-service-comments-tab-content'
+                    }],
+                select: function () {
+                    // update the property table size in case this is the first time its rendered
+                    if ($(this).text() === 'Properties') {
+                        $('#controller-service-properties').propertytable('resetTableSize');
+                    }
+
+                    // close all fields currently being edited
+                    $('#controller-service-properties').propertytable('saveRow');
+
+                    // show the border around the processor relationships if necessary
+                    var referenceContainer = $('#controller-service-referencing-components');
+                    updateReferencingComponentsBorder(referenceContainer);
+                }
+            });
+            
+            // we clustered we need to show the controls for editing the availability
+            if (nf.Canvas.isClustered()) {
+                $('#controller-service-availability-setting-container').show();
+            }
+
+            // initialize the conroller service configuration dialog
+            $('#controller-service-configuration').data('mode', config.edit).modal({
+                headerText: 'Configure Controller Service',
+                overlayBackground: false,
+                handler: {
+                    close: function () {
+                        // empty the referencing components list
+                        var referencingComponents = $('#controller-service-referencing-components');
+                        nf.Common.cleanUpTooltips(referencingComponents, 'div.referencing-component-state');
+                        referencingComponents.css('border-width', '0').empty();
+                        
+                        // cancel any active edits
+                        $('#controller-service-properties').propertytable('cancelEdit');
+
+                        // clear the tables
+                        $('#controller-service-properties').propertytable('clear');
+                        
+                        // clear the comments
+                        nf.Common.clearField('read-only-controller-service-comments');
+                        
+                        // removed the cached controller service details
+                        $('#controller-service-configuration').removeData('controllerServiceDetails');
+                    }
+                }
+            }).draggable({
+                containment: 'parent',
+                handle: '.dialog-header'
+            });
+
+            // initialize the property table
+            $('#controller-service-properties').propertytable({
+                readOnly: false,
+                newPropertyDialogContainer: '#new-controller-service-property-container',
+                descriptorDeferred: getControllerServicePropertyDescriptor
+            });
+            
+            // initialize the disable service dialog
+            $('#disable-controller-service-dialog').modal({
+                headerText: 'Disable Controller Service',
+                overlayBackground: false,
+                buttons: [{
+                    buttonText: 'Disable',
+                    handler: {
+                        click: disableHandler
+                    }
+                }, {
+                    buttonText: 'Cancel',
+                    handler: {
+                        click: closeModal
+                    }
+                }],
+                handler: {
+                    close: function() {
+                        var disableDialog = $(this);
+                        
+                        // reset visibility
+                        $('#disable-controller-service-service-container').show();
+                        $('#disable-controller-service-scope-container').show();
+                        $('#disable-controller-service-progress-container').hide();
+                        
+                        // clear the dialog
+                        $('#disable-controller-service-id').text('');
+                        $('#disable-controller-service-name').text('');
+                        
+                        // reset progress
+                        $('div.disable-referencing-components').removeClass('ajax-loading ajax-complete ajax-error');
+                        
+                        // referencing components
+                        var referencingComponents = $('#disable-controller-service-referencing-components');
+                        nf.Common.cleanUpTooltips(referencingComponents, 'div.referencing-component-state');
+                        referencingComponents.css('border-width', '0').empty();
+                        
+                        // reset dialog
+                        disableDialog.modal('setButtonModel', [{
+                            buttonText: 'Disable',
+                            handler: {
+                                click: disableHandler
+                            }
+                        }, {
+                            buttonText: 'Cancel',
+                            handler: {
+                                click: closeModal
+                            }
+                        }]);
+                    }
+                }
+            }).draggable({
+                containment: 'parent',
+                handle: '.dialog-header'
+            });
+            
+            // initialize the enable scope combo
+            $('#enable-controller-service-scope').combo({
+                options: [{
+                        text: 'Service only',
+                        value: config.serviceOnly,
+                        description: 'Enable only this controller service'
+                    }, {
+                        text: 'Service and referencing components',
+                        value: config.serviceAndReferencingComponents,
+                        description: 'Enable this controller service and enable/start all referencing components'
+                    }]
+            });
+            
+            // initialize the enable service dialog
+            $('#enable-controller-service-dialog').modal({
+                headerText: 'Enable Controller Service',
+                overlayBackground: false,
+                buttons: [{
+                    buttonText: 'Enable',
+                    handler: {
+                        click: enableHandler
+                    }
+                }, {
+                    buttonText: 'Cancel',
+                    handler: {
+                        click: closeModal
+                    }
+                }],
+                handler: {
+                    close: function() {
+                        var enableDialog = $(this);
+                        
+                        // reset visibility
+                        $('#enable-controller-service-service-container').show();
+                        $('#enable-controller-service-scope-container').show();
+                        $('#enable-controller-service-progress-container').hide();
+                        $('#enable-controller-service-progress li.referencing-component').show();
+                        
+                        // clear the dialog
+                        $('#enable-controller-service-id').text('');
+                        $('#enable-controller-service-name').text('');
+                        
+                        // reset progress
+                        $('div.enable-referencing-components').removeClass('ajax-loading ajax-complete ajax-error');
+                        
+                        // referencing components
+                        var referencingComponents = $('#enable-controller-service-referencing-components');
+                        nf.Common.cleanUpTooltips(referencingComponents, 'div.referencing-component-state');
+                        referencingComponents.css('border-width', '0').empty();
+                        
+                        // reset dialog
+                        enableDialog.modal('setButtonModel', [{
+                            buttonText: 'Enable',
+                            handler: {
+                                click: enableHandler
+                            }
+                        }, {
+                            buttonText: 'Cancel',
+                            handler: {
+                                click: closeModal
+                            }
+                        }]);
+                    }
+                }
+            }).draggable({
+                containment: 'parent',
+                handle: '.dialog-header'
+            });
+        },
+        
+        /**
+         * Shows the configuration dialog for the specified controller service.
+         * 
+         * @argument {object} controllerService      The controller service
+         */
+        showConfiguration: function (controllerService) {
+            var controllerServiceDialog = $('#controller-service-configuration');
+            if (controllerServiceDialog.data('mode') === config.readOnly) {
+                // update the visibility
+                $('#controller-service-configuration .controller-service-read-only').hide();
+                $('#controller-service-configuration .controller-service-editable').show();
+                
+                // initialize the property table
+                $('#controller-service-properties').propertytable('destroy').propertytable({
+                    readOnly: false,
+                    newPropertyDialogContainer: '#new-controller-service-property-container',
+                    descriptorDeferred: getControllerServicePropertyDescriptor
+                });
+                
+                // update the mode
+                controllerServiceDialog.data('mode', config.edit);
+            }
+            
+            // reload the service in case the property descriptors have changed
+            var reloadService = $.ajax({
+                type: 'GET',
+                url: controllerService.uri,
+                dataType: 'json'
+            });
+            
+            // get the controller service history
+            var loadHistory = $.ajax({
+                type: 'GET',
+                url: '../nifi-api/controller/history/controller-services/' + encodeURIComponent(controllerService.id),
+                dataType: 'json'
+            });
+            
+            // once everything is loaded, show the dialog
+            $.when(reloadService, loadHistory).done(function (serviceResponse, historyResponse) {
+                // get the updated controller service
+                controllerService = serviceResponse[0].controllerService;
+                
+                // get the controller service history
+                var controllerServiceHistory = historyResponse[0].componentHistory;
+                
+                // record the controller service details
+                controllerServiceDialog.data('controllerServiceDetails', controllerService);
+
+                // determine if the enabled checkbox is checked or not
+                var controllerServiceEnableStyle = 'checkbox-checked';
+                if (controllerService['state'] === 'DISABLED') {
+                    controllerServiceEnableStyle = 'checkbox-unchecked';
+                }
+
+                // populate the controller service settings
+                nf.Common.populateField('controller-service-id', controllerService['id']);
+                nf.Common.populateField('controller-service-type', nf.Common.substringAfterLast(controllerService['type'], '.'));
+                $('#controller-service-name').val(controllerService['name']);
+                $('#controller-service-enabled').removeClass('checkbox-checked checkbox-unchecked').addClass(controllerServiceEnableStyle);
+                $('#controller-service-comments').val(controllerService['comments']);
+
+                // select the availability when appropriate
+                if (nf.Canvas.isClustered()) {
+                    if (controllerService['availability'] === 'node') {
+                        $('#controller-service-availability').text('Node');
+                    } else {
+                        $('#controller-service-availability').text('Cluster Manager');
+                    }
+                }
+                
+                // get the reference container
+                var referenceContainer = $('#controller-service-referencing-components');
+
+                // load the controller referencing components list
+                createReferencingComponents(referenceContainer, controllerService.referencingComponents);
+
+                var buttons = [{
+                        buttonText: 'Apply',
+                        handler: {
+                            click: function () {
+                                // close all fields currently being edited
+                                $('#controller-service-properties').propertytable('saveRow');
+
+                                // marshal the settings and properties and update the controller service
+                                var updatedControllerService = marshalDetails();
+
+                                // ensure details are valid as far as we can tell
+                                if (validateDetails(updatedControllerService)) {
+                                    var previouslyReferencedServiceIds = [];
+                                    $.each(identifyReferencedServiceDescriptors(controllerService), function (_, descriptor) {
+                                        var modifyingService = !nf.Common.isUndefined(updatedControllerService.controllerService.properties) && !nf.Common.isUndefined(updatedControllerService.controllerService.properties[descriptor.name]);
+                                        var isCurrentlyConfigured = nf.Common.isDefinedAndNotNull(controllerService.properties[descriptor.name]);
+                                        
+                                        // if we are attempting to update a controller service reference
+                                        if (modifyingService && isCurrentlyConfigured) {
+                                            // record the current value if set
+                                            previouslyReferencedServiceIds.push(controllerService.properties[descriptor.name]);
+                                        }
+                                    });
+                                    
+                                    // update the selected component
+                                    $.ajax({
+                                        type: 'PUT',
+                                        data: JSON.stringify(updatedControllerService),
+                                        url: controllerService.uri,
+                                        dataType: 'json',
+                                        processData: false,
+                                        contentType: 'application/json'
+                                    }).done(function (response) {
+                                        if (nf.Common.isDefinedAndNotNull(response.controllerService)) {
+                                            nf.Client.setRevision(response.revision);
+
+                                            // reload the controller service
+                                            renderControllerService(response.controllerService);
+                                            reloadControllerServiceReferences(response.controllerService);
+                                            
+                                            // reload all previously referenced controller services
+                                            $.each(previouslyReferencedServiceIds, function(_, oldServiceReferenceId) {
+                                                reloadControllerService(oldServiceReferenceId);
+                                            });
+
+                                            // close the details panel
+                                            controllerServiceDialog.modal('hide');
+                                        }
+                                    }).fail(handleControllerServiceConfigurationError);
+                                }
+                            }
+                        }
+                    }, {
+                        buttonText: 'Cancel',
+                        handler: {
+                            click: function () {
+                                controllerServiceDialog.modal('hide');
+                            }
+                        }
+                    }];
+
+                // determine if we should show the advanced button
+                if (nf.Common.isDefinedAndNotNull(controllerService.customUiUrl) && controllerService.customUiUrl !== '') {
+                    buttons.push({
+                        buttonText: 'Advanced',
+                        handler: {
+                            click: function () {
+                                var openCustomUi = function () {
+                                    // reset state and close the dialog manually to avoid hiding the faded background
+                                    controllerServiceDialog.modal('hide');
+                                    
+                                    // close the settings dialog since the custom ui is also opened in the shell
+                                    $('#shell-close-button').click();
+                    
+                                    // show the custom ui
+                                    nf.CustomUi.showCustomUi($('#controller-service-id').text(), controllerService.customUiUrl, true).done(function () {
+                                        // once the custom ui is closed, reload the controller service
+                                        reloadControllerServiceAndReferencingComponents(controllerService);
+                                        
+                                        // show the settings
+                                        nf.Settings.showSettings();
+                                    });
+                                };
+
+                                // close all fields currently being edited
+                                $('#controller-service-properties').propertytable('saveRow');
+
+                                // determine if changes have been made
+                                if (isSaveRequired()) {
+                                    // see if those changes should be saved
+                                    nf.Dialog.showYesNoDialog({
+                                        dialogContent: 'Save changes before opening the advanced configuration?',
+                                        overlayBackground: false,
+                                        noHandler: openCustomUi,
+                                        yesHandler: function () {
+                                            // marshal the settings and properties and update the controller service
+                                            var updatedControllerService = marshalDetails();
+
+                                            // ensure details are valid as far as we can tell
+                                            if (validateDetails(updatedControllerService)) {
+                                                var previouslyReferencedServiceIds = [];
+                                                $.each(identifyReferencedServiceDescriptors(controllerService), function (_, descriptor) {
+                                                    var modifyingService = !nf.Common.isUndefined(updatedControllerService.controllerService.properties) && !nf.Common.isUndefined(updatedControllerService.controllerService.properties[descriptor.name]);
+                                                    var isCurrentlyConfigured = nf.Common.isDefinedAndNotNull(controllerService.properties[descriptor.name]);
+
+                                                    // if we are attempting to update a controller service reference
+                                                    if (modifyingService && isCurrentlyConfigured) {
+                                                        
+                                                        // record the current value if set
+                                                        previouslyReferencedServiceIds.push(controllerService.properties[descriptor.name]);
+                                                    }
+                                                });
+
+                                                // update the selected component
+                                                $.ajax({
+                                                    type: 'PUT',
+                                                    data: JSON.stringify(updatedControllerService),
+                                                    url: controllerService.uri,
+                                                    dataType: 'json',
+                                                    processData: false,
+                                                    contentType: 'application/json'
+                                                }).done(function (response) {
+                                                    if (nf.Common.isDefinedAndNotNull(response.controllerService)) {
+                                                        // update the revision
+                                                        nf.Client.setRevision(response.revision);
+
+                                                        // reload all previously referenced controller services
+                                                        $.each(previouslyReferencedServiceIds, function(_, oldServiceReferenceId) {
+                                                            reloadControllerService(oldServiceReferenceId);
+                                                        });
+
+                                                        // open the custom ui
+                                                        openCustomUi();
+                                                    }
+                                                }).fail(handleControllerServiceConfigurationError);
+                                            }
+                                        }
+                                    });
+                                } else {
+                                    // if there were no changes, simply open the custom ui
+                                    openCustomUi();
+                                }
+                            }
+                        }
+                    });
+                }
+
+                // set the button model
+                controllerServiceDialog.modal('setButtonModel', buttons);
+                
+                // load the property table
+                $('#controller-service-properties').propertytable('loadProperties', controllerService.properties, controllerService.descriptors, controllerServiceHistory.propertyHistory);
+
+                // show the details
+                controllerServiceDialog.modal('show');
+
+                // show the border if necessary
+                updateReferencingComponentsBorder(referenceContainer);
+            }).fail(nf.Common.handleAjaxError);
+        }, 
+        
+        /**
+         * Shows the controller service details in a read only dialog.
+         * 
+         * @param {object} controllerService
+         */
+        showDetails: function(controllerService) {
+            var controllerServiceDialog = $('#controller-service-configuration');
+            if (controllerServiceDialog.data('mode') === config.edit) {
+                // update the visibility
+                $('#controller-service-configuration .controller-service-read-only').show();
+                $('#controller-service-configuration .controller-service-editable').hide();
+                
+                // initialize the property table
+                $('#controller-service-properties').propertytable('destroy').propertytable({
+                    readOnly: true
+                });
+                
+                // update the mode
+                controllerServiceDialog.data('mode', config.readOnly);
+            }
+            
+            // reload the service in case the property descriptors have changed
+            var reloadService = $.ajax({
+                type: 'GET',
+                url: controllerService.uri,
+                dataType: 'json'
+            });
+            
+            // get the controller service history
+            var loadHistory = $.ajax({
+                type: 'GET',
+                url: '../nifi-api/controller/history/controller-services/' + encodeURIComponent(controllerService.id),
+                dataType: 'json'
+            });
+            
+            // once everything is loaded, show the dialog
+            $.when(reloadService, loadHistory).done(function (serviceResponse, historyResponse) {
+                // get the updated controller service
+                controllerService = serviceResponse[0].controllerService;
+                
+                // get the controller service history
+                var controllerServiceHistory = historyResponse[0].componentHistory;
+                
+                // record the controller service details
+                controllerServiceDialog.data('controllerServiceDetails', controllerService);
+                
+                // populate the controller service settings
+                nf.Common.populateField('controller-service-id', controllerService['id']);
+                nf.Common.populateField('controller-service-type', nf.Common.substringAfterLast(controllerService['type'], '.'));
+                nf.Common.populateField('read-only-controller-service-name', controllerService['name']);
+                nf.Common.populateField('read-only-controller-service-comments', controllerService['comments']);
+
+                // select the availability when appropriate
+                if (nf.Canvas.isClustered()) {
+                    if (controllerService['availability'] === 'node') {
+                        $('#controller-service-availability').text('Node');
+                    } else {
+                        $('#controller-service-availability').text('Cluster Manager');
+                    }
+                }
+                
+                // get the reference container
+                var referenceContainer = $('#controller-service-referencing-components');
+
+                // load the controller referencing components list
+                createReferencingComponents(referenceContainer, controllerService.referencingComponents);
+                
+                var buttons = [{
+                        buttonText: 'Ok',
+                        handler: {
+                            click: function () {
+                                // hide the dialog
+                                controllerServiceDialog.modal('hide');
+                            }
+                        }
+                    }];
+
+                // determine if we should show the advanced button
+                if (nf.Common.isDefinedAndNotNull(nf.CustomUi) && nf.Common.isDefinedAndNotNull(controllerService.customUiUrl) && controllerService.customUiUrl !== '') {
+                    buttons.push({
+                        buttonText: 'Advanced',
+                        handler: {
+                            click: function () {
+                                // reset state and close the dialog manually to avoid hiding the faded background
+                                controllerServiceDialog.modal('hide');
+                                
+                                // close the settings dialog since the custom ui is also opened in the shell
+                                $('#shell-close-button').click();
+
+                                // show the custom ui
+                                nf.CustomUi.showCustomUi(controllerService.id, controllerService.customUiUrl, false).done(function () {
+                                    nf.Settings.showSettings();
+                                });
+                            }
+                        }
+                    });
+                }
+                
+                // show the dialog
+                controllerServiceDialog.modal('setButtonModel', buttons);
+                
+                // load the property table
+                $('#controller-service-properties').propertytable('loadProperties', controllerService.properties, controllerService.descriptors, controllerServiceHistory.propertyHistory);
+                
+                // show the details
+                controllerServiceDialog.modal('show');
+
+                // show the border if necessary
+                updateReferencingComponentsBorder(referenceContainer);
+            });
+        },
+        
+        /**
+         * Enables the specified controller service.
+         * 
+         * @param {object} controllerService
+         */
+        enable: function(controllerService) {
+            if (nf.Common.isEmpty(controllerService.referencingComponents)) {
+                setEnabled(controllerService, true).always(function () {
+                    reloadControllerServiceAndReferencingComponents(controllerService);
+                });
+            } else {
+                showEnableControllerServiceDialog(controllerService);
+            }
+        },
+        
+        /**
+         * Disables the specified controller service.
+         * 
+         * @param {object} controllerService
+         */
+        disable: function(controllerService) {
+            if (nf.Common.isEmpty(controllerService.referencingComponents)) {
+                setEnabled(controllerService, false).always(function () {
+                    reloadControllerServiceAndReferencingComponents(controllerService);
+                });
+            } else {
+                showDisableControllerServiceDialog(controllerService);
+            }
+        },
+        
+        /**
+         * Reloads the services that the specified comonent references. This is
+         * necessary because the specified component state is reflected in the 
+         * referenced service referencing components.
+         * 
+         * @param {object} component
+         */
+        reloadReferencedServices: function(component) {
+            $.each(getReferencedServices(component), function (_, referencedServiceId) {
+                reloadControllerService(referencedServiceId);
+            });
+        },
+        
+        /**
+         * Deletes the specified controller service.
+         * 
+         * @param {object} controllerService
+         */
+        remove: function(controllerService) {
+            // prompt for removal?
+                    
+            var revision = nf.Client.getRevision();
+            $.ajax({
+                type: 'DELETE',
+                url: controllerService.uri + '?' + $.param({
+                    version: revision.version,
+                    clientId: revision.clientId
+                }),
+                dataType: 'json'
+            }).done(function (response) {
+                nf.Client.setRevision(response.revision);
+
+                // remove the service
+                var controllerServiceGrid = $('#controller-services-table').data('gridInstance');
+                var controllerServiceData = controllerServiceGrid.getData();
+                controllerServiceData.deleteItem(controllerService.id);
+
+                // reload the as necessary
+                reloadControllerServiceReferences(controllerService);
+            }).fail(nf.Common.handleAjaxError);
+        }
+    };
+}());

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-custom-processor-ui.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-custom-processor-ui.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-custom-processor-ui.js
deleted file mode 100644
index 7d98f40..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-custom-processor-ui.js
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.
- */
-nf.CustomProcessorUi = {
-    /**
-     * Shows the custom ui.
-     * 
-     * @argument {string} processorId       The processor id
-     * @argument {string} uri               The uri for the custom ui
-     * @argument {boolean} editable         Whether the custom ui should support editing
-     */
-    showCustomUi: function (processorId, uri, editable) {
-
-        // record the processor id
-        $('#shell-close-button');
-
-        var revision = nf.Client.getRevision();
-
-        // build the customer ui params
-        var customUiParams = {
-            'processorId': processorId,
-            'revision': revision.version,
-            'clientId': revision.clientId,
-            'editable': editable
-        };
-
-        // show the shell
-        return nf.Shell.showPage('..' + uri + '?' + $.param(customUiParams), false);
-    }
-};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-custom-ui.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-custom-ui.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-custom-ui.js
new file mode 100644
index 0000000..365831a
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-custom-ui.js
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+/* global nf */
+
+nf.CustomUi = {
+    /**
+     * Shows the custom ui.
+     * 
+     * @argument {string} id       The component id
+     * @argument {string} uri               The uri for the custom ui
+     * @argument {boolean} editable         Whether the custom ui should support editing
+     */
+    showCustomUi: function (id, uri, editable) {
+
+        // record the processor id
+        $('#shell-close-button');
+
+        var revision = nf.Client.getRevision();
+
+        // build the customer ui params
+        var customUiParams = {
+            'id': id,
+            'processorId': id,                  // deprecated
+            'revision': revision.version,
+            'clientId': revision.clientId,
+            'editable': editable
+        };
+
+        // show the shell
+        return nf.Shell.showPage('..' + uri + '?' + $.param(customUiParams), false);
+    }
+};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-draggable.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-draggable.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-draggable.js
index 1858e60..d44f1ed 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-draggable.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-draggable.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.Draggable = (function () {
 
     var drag;


[39/62] [abbrv] incubator-nifi git commit: NIFI-491: Fixed bug that caused InvocationTargetException.getCause().getCause() instead of InvocationTargetException.getCause(); also if null passed into logger, avoid the NPE that results

Posted by ma...@apache.org.
NIFI-491: Fixed bug that caused InvocationTargetException.getCause().getCause() instead of InvocationTargetException.getCause(); also if null passed into logger, avoid the NPE that results


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/c974ea90
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/c974ea90
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/c974ea90

Branch: refs/heads/NIFI-25
Commit: c974ea90f89186bfd5f12e34f2cec3728d237b89
Parents: a7862a1
Author: Mark Payne <ma...@hotmail.com>
Authored: Tue Apr 7 12:41:31 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Tue Apr 7 12:41:31 2015 -0400

----------------------------------------------------------------------
 .../nifi/controller/scheduling/StandardProcessScheduler.java     | 4 ++--
 .../main/java/org/apache/nifi/processor/SimpleProcessLogger.java | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/c974ea90/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java
index 43e05dd..7725823 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java
@@ -339,8 +339,8 @@ public final class StandardProcessScheduler implements ProcessScheduler {
                             final ProcessorLog procLog = new SimpleProcessLogger(procNode.getIdentifier(), procNode.getProcessor());
 
                             procLog.error("{} failed to invoke @OnScheduled method due to {}; processor will not be scheduled to run for {}",
-                                    new Object[]{procNode.getProcessor(), cause.getCause(), administrativeYieldDuration}, cause.getCause());
-                            LOG.error("Failed to invoke @OnScheduled method due to {}", cause.getCause().toString(), cause.getCause());
+                                    new Object[]{procNode.getProcessor(), cause, administrativeYieldDuration}, cause);
+                            LOG.error("Failed to invoke @OnScheduled method due to {}", cause.toString(), cause);
 
                             ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnUnscheduled.class, procNode.getProcessor(), processContext);
                             ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnStopped.class, procNode.getProcessor(), processContext);

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/c974ea90/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/SimpleProcessLogger.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/SimpleProcessLogger.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/SimpleProcessLogger.java
index 25d8f10..0a345a0 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/SimpleProcessLogger.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/SimpleProcessLogger.java
@@ -243,7 +243,7 @@ public class SimpleProcessLogger implements ProcessorLog {
         for (int i = 0; i < os.length; i++) {
             modifiedArgs[i + 1] = os[i];
         }
-        modifiedArgs[modifiedArgs.length - 1] = t.toString();
+        modifiedArgs[modifiedArgs.length - 1] = (t == null) ? "" : t.toString();
 
         return modifiedArgs;
     }


[02/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
index b49a97b..b818e64 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, parseFloat */
+
 $(document).ready(function () {
     // preload the image for the error page - this is preloaded because the system
     // may be unavailable to return the image when the error page is rendered
@@ -55,6 +58,7 @@ $(document).ready(function () {
 // Define a common utility class used across the entire application.
 nf.Common = {
     config: {
+        sensitiveText: 'Sensitive value set',
         tooltipConfig: {
             style: {
                 classes: 'nifi-tooltip'
@@ -406,7 +410,73 @@ nf.Common = {
     }()),
     
     /**
-     * Creates a form inline in order to post the specified params to the specified URL.
+     * Determines if the specified property is sensitive.
+     * 
+     * @argument {object} propertyDescriptor        The property descriptor
+     */
+    isSensitiveProperty: function (propertyDescriptor) {
+        if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+            return propertyDescriptor.sensitive === true;
+        } else {
+            return false;
+        }
+    },
+
+    /**
+     * Determines if the specified property is required.
+     * 
+     * @param {object} propertyDescriptor           The property descriptor
+     */
+    isRequiredProperty: function (propertyDescriptor) {
+        if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+            return propertyDescriptor.required === true;
+        } else {
+            return false;
+        }
+    },
+
+    /**
+     * Determines if the specified property is required.
+     * 
+     * @param {object} propertyDescriptor           The property descriptor
+     */
+    isDynamicProperty: function (propertyDescriptor) {
+        if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+            return propertyDescriptor.dynamic === true;
+        } else {
+            return false;
+        }
+    },
+
+    /**
+     * Gets the allowable values for the specified property.
+     * 
+     * @argument {object} propertyDescriptor        The property descriptor
+     */
+    getAllowableValues: function (propertyDescriptor) {
+        if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+            return propertyDescriptor.allowableValues;
+        } else {
+            return null;
+        }
+    },
+
+    /**
+     * Returns whether the specified property supports EL.
+     * 
+     * @param {object} propertyDescriptor           The property descriptor
+     */
+    supportsEl: function (propertyDescriptor) {
+        if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+            return propertyDescriptor.supportsEl === true;
+        } else {
+            return false;
+        }
+    },
+    
+    /**
+     * Creates a form inline in order to submit the specified params to the specified URL
+     * using the specified method.
      * 
      * @param {string} url          The URL
      * @param {object} params       An object with the params to include in the submission
@@ -894,4 +964,4 @@ nf.Common = {
         });
         return formattedBulletins;
     }
-};
\ No newline at end of file
+};

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-connection-details.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-connection-details.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-connection-details.js
index ca3304f..409e811 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-connection-details.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-connection-details.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 nf.ConnectionDetails = (function () {
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js
index 174f8bd..2d1183f 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 $(document).ready(function () {
     // setup general button mouse behavior
     nf.Common.addHoverEffect('div.button', 'button-normal', 'button-over');
@@ -35,6 +38,9 @@ $(document).ready(function () {
                 $('#nf-ok-dialog-content').empty();
             }
         }
+    }).draggable({
+        containment: 'parent',
+        handle: '.dialog-header'
     });
 
     // configure the yes/no dialog
@@ -45,6 +51,9 @@ $(document).ready(function () {
                 $('#nf-yes-no-dialog-content').empty();
             }
         }
+    }).draggable({
+        containment: 'parent',
+        handle: '.dialog-header'
     });
 });
 
@@ -68,13 +77,6 @@ nf.Dialog = (function () {
             var content = $('<p></p>').append(options.dialogContent);
             $('#nf-ok-dialog-content').append(content);
 
-            // conditionally show the header text
-            if (nf.Common.isBlank(options.headerText)) {
-                $('#nf-ok-dialog-content').css('margin-top', '-10px');
-            } else {
-                $('#nf-ok-dialog-content').css('margin-top', '0px');
-            }
-
             // show the dialog
             $('#nf-ok-dialog').modal('setHeaderText', options.headerText).modal('setOverlayBackground', options.overlayBackground).modal('show');
         },
@@ -122,7 +124,7 @@ nf.Dialog = (function () {
                 }]);
 
             // show the dialog
-            $('#nf-yes-no-dialog').modal('setOverlayBackground', options.overlayBackground).modal('show');
+            $('#nf-yes-no-dialog').modal('setHeaderText', options.headerText).modal('setOverlayBackground', options.overlayBackground).modal('show');
         }
     };
 }());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js
index a3a2589..76d9e53 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js
@@ -14,146 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-nf.ProcessorDetails = (function () {
-
-    /**
-     * Shows the property value for the specified row and cell.
-     * 
-     * @param {type} row
-     * @param {type} cell
-     */
-    var showPropertyValue = function (row, cell) {
-        // remove any currently open detail dialogs
-        removeAllPropertyDetailDialogs();
-
-        // get the property in question
-        var propertyGrid = $('#read-only-processor-properties').data('gridInstance');
-        var propertyData = propertyGrid.getData();
-        var property = propertyData.getItem(row);
-
-        // ensure there is a value
-        if (nf.Common.isDefinedAndNotNull(property.value)) {
-
-            // get the processor details to insert the description tooltip
-            var details = $('#processor-details').data('processorDetails');
-            var propertyDescriptor = details.config.descriptors[property.property];
-
-            // ensure we're not dealing with a sensitive property
-            if (!isSensitiveProperty(propertyDescriptor)) {
-
-                // get details about the location of the cell
-                var cellNode = $(propertyGrid.getCellNode(row, cell));
-                var offset = cellNode.offset();
-
-                // create the wrapper
-                var wrapper = $('<div class="processor-property-detail"></div>').css({
-                    'z-index': 100000,
-                    'position': 'absolute',
-                    'background': 'white',
-                    'padding': '5px',
-                    'overflow': 'hidden',
-                    'border': '3px solid #365C6A',
-                    'box-shadow': '4px 4px 6px rgba(0, 0, 0, 0.9)',
-                    'cursor': 'move',
-                    'top': offset.top - 5,
-                    'left': offset.left - 5
-                }).appendTo('body');
-
-                var editor = null;
-
-                // so the nfel editor is appropriate
-                if (supportsEl(propertyDescriptor)) {
-                    var languageId = 'nfel';
-                    var editorClass = languageId + '-editor';
-
-                    // prevent dragging over the nf editor
-                    wrapper.draggable({
-                        cancel: 'input, textarea, pre, .button, .' + editorClass,
-                        containment: 'parent'
-                    });
-
-                    // create the editor
-                    editor = $('<div></div>').addClass(editorClass).appendTo(wrapper).nfeditor({
-                        languageId: languageId,
-                        width: cellNode.width(),
-                        content: property.value,
-                        minWidth: 175,
-                        minHeight: 100,
-                        readOnly: true,
-                        resizable: true
-                    });
-                } else {
-                    // prevent dragging over standard components
-                    wrapper.draggable({
-                        containment: 'parent'
-                    });
-
-                    // create the input field
-                    $('<textarea hidefocus rows="5" readonly="readonly"/>').css({
-                        'background': 'white',
-                        'width': cellNode.width() + 'px',
-                        'height': '80px',
-                        'border-width': '0',
-                        'outline': '0',
-                        'overflow-y': 'auto',
-                        'resize': 'both',
-                        'margin-bottom': '28px'
-                    }).text(property.value).appendTo(wrapper);
-                }
-
-                // add an ok button that will remove the entire pop up
-                var ok = $('<div class="button button-normal">Ok</div>').on('click', function () {
-                    // clean up the editor
-                    if (editor !== null) {
-                        editor.nfeditor('destroy');
-                    }
-                    
-                    // clean up the rest
-                    wrapper.hide().remove();
-                });
-                $('<div></div>').css({
-                    'position': 'absolute',
-                    'bottom': '0',
-                    'left': '0',
-                    'right': '0',
-                    'padding': '0 3px 5px'
-                }).append(ok).append('<div class="clear"></div>').appendTo(wrapper);
-            }
-        }
-    };
 
-    /**
-     * Removes all currently open process property detail dialogs.
-     */
-    var removeAllPropertyDetailDialogs = function () {
-        $('body').children('div.processor-property-detail').hide().remove();
-    };
+/* global nf */
 
-    /**
-     * Determines if the specified property is sensitive.
-     * 
-     * @argument {object} propertyDescriptor        The property descriptor
-     */
-    var isSensitiveProperty = function (propertyDescriptor) {
-        if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
-            return propertyDescriptor.sensitive === true;
-        } else {
-            return false;
-        }
-    };
-
-    /**
-     * Returns whether the specified property supports EL.
-     * 
-     * @param {object} propertyDescriptor           The property descriptor
-     */
-    var supportsEl = function (propertyDescriptor) {
-        if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
-            return propertyDescriptor.supportsEl === true;
-        } else {
-            return false;
-        }
-    };
+nf.ProcessorDetails = (function () {
 
     /**
      * Creates an option for the specified relationship name.
@@ -207,10 +71,7 @@ nf.ProcessorDetails = (function () {
                 select: function () {
                     // resize the property grid in case this is the first time its rendered
                     if ($(this).text() === 'Properties') {
-                        var propertyGrid = $('#read-only-processor-properties').data('gridInstance');
-                        if (nf.Common.isDefinedAndNotNull(propertyGrid)) {
-                            propertyGrid.resizeCanvas();
-                        }
+                        $('#read-only-processor-properties').propertytable('resetTableSize');
                     }
 
                     // show the border if processor relationship names if necessary
@@ -230,17 +91,9 @@ nf.ProcessorDetails = (function () {
                         // empty the relationship list
                         $('#read-only-auto-terminate-relationship-names').css('border-width', '0').empty();
 
-                        // get the property grid element
-                        var propertyGridElement = $('#read-only-processor-properties');
+                        // clear the property grid
+                        $('#read-only-processor-properties').propertytable('clear');
             
-                        // clean up any tooltips that may have been generated
-                        nf.Common.cleanUpTooltips(propertyGridElement, 'img.icon-info');
-
-                        // clear the data in the grid
-                        var propertyGrid = propertyGridElement.data('gridInstance');
-                        var propertyData = propertyGrid.getData();
-                        propertyData.setItems([]);
-
                         // clear the processor details
                         nf.Common.clearField('read-only-processor-id');
                         nf.Common.clearField('read-only-processor-type');
@@ -257,9 +110,6 @@ nf.ProcessorDetails = (function () {
                         // removed the cached processor details
                         $('#processor-details').removeData('processorDetails');
                         $('#processor-details').removeData('processorHistory');
-
-                        // remove any currently open detail dialogs
-                        removeAllPropertyDetailDialogs();
                     }
                 }
             });
@@ -271,135 +121,9 @@ nf.ProcessorDetails = (function () {
                 });
             }
 
-            // function for formatting the property name
-            var nameFormatter = function (row, cell, value, columnDef, dataContext) {
-                var nameWidthOffset = 10;
-                var cellContent = $('<div></div>');
-
-                // format the contents
-                var formattedValue = $('<span/>').addClass('table-cell').text(value).appendTo(cellContent);
-                if (dataContext.type === 'required') {
-                    formattedValue.addClass('required');
-                }
-
-                // get the processor details to insert the description tooltip
-                var details = $('#processor-details').data('processorDetails');
-                var propertyDescriptor = details.config.descriptors[dataContext.property];
-
-                // show the property description if applicable
-                if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
-                    if (!nf.Common.isBlank(propertyDescriptor.description) || !nf.Common.isBlank(propertyDescriptor.defaultValue) || !nf.Common.isBlank(propertyDescriptor.supportsEl)) {
-                        $('<img class="icon-info" src="images/iconInfo.png" alt="Info" title="" style="float: right; margin-right: 6px; margin-top: 4px;" />').appendTo(cellContent);
-                        nameWidthOffset = 26; // 10 + icon width (10) + icon margin (6)
-                    }
-                }
-
-                // adjust the width accordingly
-                formattedValue.width(columnDef.width - nameWidthOffset).ellipsis();
-
-                // return the cell content
-                return cellContent.html();
-            };
-
-            // function for formatting the property value
-            var valueFormatter = function (row, cell, value, columnDef, dataContext) {
-                var valueMarkup;
-                if (nf.Common.isDefinedAndNotNull(value)) {
-                    // identify the property descriptor
-                    var processorDetails = $('#processor-details').data('processorDetails');
-                    var propertyDescriptor = processorDetails.config.descriptors[dataContext.property];
-
-                    // determine if the property is sensitive
-                    if (isSensitiveProperty(propertyDescriptor)) {
-                        valueMarkup = '<span class="table-cell sensitive">Sensitive value set</span>';
-                    } else {
-                        if (value === '') {
-                            valueMarkup = '<span class="table-cell blank">Empty string set</span>';
-                        } else {
-                            valueMarkup = '<div class="table-cell value"><pre class="ellipsis">' + nf.Common.escapeHtml(value) + '</pre></div>';
-                        }
-                    }
-                } else {
-                    valueMarkup = '<span class="unset">No value set</span>';
-                }
-
-                // format the contents
-                var content = $(valueMarkup);
-                if (dataContext.type === 'required') {
-                    content.addClass('required');
-                }
-                content.find('.ellipsis').width(columnDef.width - 10).ellipsis();
-
-                // return the appropriate markup
-                return $('<div/>').append(content).html();
-            };
-
-            // initialize the processor type table
-            var processorDetailsColumns = [
-                {id: 'property', field: 'property', name: 'Property', sortable: false, resizable: true, rerenderOnResize: true, formatter: nameFormatter},
-                {id: 'value', field: 'value', name: 'Value', sortable: false, resizable: true, cssClass: 'pointer', rerenderOnResize: true, formatter: valueFormatter}
-            ];
-            var processorDetailsOptions = {
-                forceFitColumns: true,
-                enableTextSelectionOnCells: true,
-                enableCellNavigation: false,
-                enableColumnReorder: false,
-                autoEdit: false
-            };
-
-            // initialize the dataview
-            var processorDetailsData = new Slick.Data.DataView({
-                inlineFilters: false
-            });
-            processorDetailsData.setItems([]);
-
-            // initialize the grid
-            var processorDetailsGrid = new Slick.Grid('#read-only-processor-properties', processorDetailsData, processorDetailsColumns, processorDetailsOptions);
-            processorDetailsGrid.setSelectionModel(new Slick.RowSelectionModel());
-            processorDetailsGrid.onClick.subscribe(function (e, args) {
-                // only consider property values
-                if (args.cell === 1) {
-                    // show the property value in a resizable dialog
-                    showPropertyValue(args.row, args.cell);
-
-                    // prevents standard edit logic
-                    e.stopImmediatePropagation();
-                }
-            });
-
-            // wire up the dataview to the grid
-            processorDetailsData.onRowCountChanged.subscribe(function (e, args) {
-                processorDetailsGrid.updateRowCount();
-                processorDetailsGrid.render();
-            });
-            processorDetailsData.onRowsChanged.subscribe(function (e, args) {
-                processorDetailsGrid.invalidateRows(args.rows);
-                processorDetailsGrid.render();
-            });
-
-            // hold onto an instance of the grid and listen for mouse events to add tooltips where appropriate
-            $('#read-only-processor-properties').data('gridInstance', processorDetailsGrid).on('mouseenter', 'div.slick-cell', function (e) {
-                var infoIcon = $(this).find('img.icon-info');
-                if (infoIcon.length && !infoIcon.data('qtip')) {
-                    var property = $(this).find('span.table-cell').text();
-
-                    // get the processor details to insert the description tooltip
-                    var details = $('#processor-details').data('processorDetails');
-                    var propertyDescriptor = details.config.descriptors[property];
-
-                    // get the processor history
-                    var processorHistory = $('#processor-details').data('processorHistory');
-                    var propertyHistory = processorHistory.propertyHistory[property];
-
-                    // format the tooltip
-                    var tooltip = nf.Common.formatPropertyTooltip(propertyDescriptor, propertyHistory);
-
-                    if (nf.Common.isDefinedAndNotNull(tooltip)) {
-                        infoIcon.qtip($.extend({
-                            content: tooltip
-                        }, nf.Common.config.tooltipConfig));
-                    }
-                }
+            // initialize the properties
+            $('#read-only-processor-properties').propertytable({
+                readOnly: true
             });
         },
         
@@ -466,54 +190,6 @@ nf.ProcessorDetails = (function () {
                     } else {
                         $('#read-only-auto-terminate-relationship-names').append('<div class="unset">This processor has no relationships.</div>');
                     }
-
-                    // get the property grid
-                    var propertyGrid = $('#read-only-processor-properties').data('gridInstance');
-                    var propertyData = propertyGrid.getData();
-                    var properties = details.config.properties;
-                    var descriptors = details.config.descriptors;
-
-                    // generate the processor properties
-                    if (nf.Common.isDefinedAndNotNull(properties)) {
-                        propertyData.beginUpdate();
-
-                        var i = 0;
-                        $.each(properties, function (name, value) {
-                            // get the property descriptor
-                            var descriptor = descriptors[name];
-
-                            // determine the property type
-                            var type = 'userDefined';
-                            var displayName = name;
-                            if (nf.Common.isDefinedAndNotNull(descriptor)) {
-                                if (descriptor.required === true) {
-                                    type = 'required';
-                                } else if (descriptor.dynamic === true) {
-                                    type = 'userDefined';
-                                } else {
-                                    type = 'optional';
-                                }
-                                
-                                // use the display name if possible
-                                displayName = descriptor.displayName;
-                                
-                                // determine the value
-                                if (nf.Common.isNull(value) && nf.Common.isDefinedAndNotNull(descriptor.defaultValue)) {
-                                    value = descriptor.defaultValue;
-                                }
-                            }
-
-                            // add the row
-                            propertyData.addItem({
-                                id: i++,
-                                property: displayName,
-                                value: value,
-                                type: type
-                            });
-                        });
-
-                        propertyData.endUpdate();
-                    }
                 }
             });
 
@@ -523,16 +199,21 @@ nf.ProcessorDetails = (function () {
                 url: '../nifi-api/controller/history/processors/' + encodeURIComponent(processorId),
                 dataType: 'json'
             }).done(function (response) {
-                var processorHistory = response.processorHistory;
+                var processorHistory = response.componentHistory;
 
                 // record the processor history
                 $('#processor-details').data('processorHistory', processorHistory);
             });
 
             // show the dialog once we have the processor and its history
-            $.when(getProcessor, getProcessorHistory).done(function (response) {
-                var processorResponse = response[0];
+            $.when(getProcessor, getProcessorHistory).done(function (processorResponse, historyResponse) {
+                var processorResponse = processorResponse[0];
                 var processor = processorResponse.processor;
+                var historyResponse = historyResponse[0];
+                var history = historyResponse.componentHistory;
+
+                // load the properties
+                $('#read-only-processor-properties').propertytable('loadProperties', processor.config.properties, processor.config.descriptors, history.propertyHistory);
 
                 var buttons = [{
                         buttonText: 'Ok',
@@ -545,7 +226,7 @@ nf.ProcessorDetails = (function () {
                     }];
 
                 // determine if we should show the advanced button
-                if (nf.Common.isDefinedAndNotNull(nf.CustomProcessorUi) && nf.Common.isDefinedAndNotNull(processor.config.customUiUrl) && processor.config.customUiUrl !== '') {
+                if (nf.Common.isDefinedAndNotNull(nf.CustomUi) && nf.Common.isDefinedAndNotNull(processor.config.customUiUrl) && processor.config.customUiUrl !== '') {
                     buttons.push({
                         buttonText: 'Advanced',
                         handler: {
@@ -554,7 +235,7 @@ nf.ProcessorDetails = (function () {
                                 $('#processor-details').modal('hide');
 
                                 // show the custom ui
-                                nf.CustomProcessorUi.showCustomUi(processor.id, processor.config.customUiUrl, false);
+                                nf.CustomUi.showCustomUi(processor.id, processor.config.customUiUrl, false);
                             }
                         }
                     });

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-shell.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-shell.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-shell.js
index 85ca0bf..bac57f6 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-shell.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-shell.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 $(document).ready(function () {
     // configure the dialog
     $('#shell-dialog').modal({

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-status-history.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-status-history.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-status-history.js
index 55a436a..5229398 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-status-history.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-status-history.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.StatusHistory = (function () {
     var config = {
         clusterInstanceId: 'cluster-instance-id',

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js
index 583fe7e..42c49cf 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-lineage.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.ProvenanceLineage = (function () {
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js
index 5e3bab4..9451a34 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, top, Slick */
+
 nf.ProvenanceTable = (function () {
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js
index 33338a5..df1a682 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, top */
+
 $(document).ready(function () {
     // initialize the status page
     nf.Provenance.init();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js
index 77f5e31..2bd94d5 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js
@@ -14,6 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+/* global nf, top, Slick */
+
 nf.SummaryTable = (function () {
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js
index 4f80241..97f8626 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 $(document).ready(function () {
     // initialize the summary page
     nf.Summary.init();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js
index 2dcdb68..5f213fd 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates-table.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, Slick */
+
 nf.TemplatesTable = (function () {
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js
index 0602c37..b55bee2 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/templates/nf-templates.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, top */
+
 $(document).ready(function () {
     // initialize the templates page
     nf.Templates.init();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users-table.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users-table.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users-table.js
index 7718221..7ab4a76 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users-table.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/users/nf-users-table.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, Slick */
+
 nf.UsersTable = (function () {
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/pom.xml
index 1218f6c..d9098f3 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/pom.xml
@@ -12,8 +12,7 @@
   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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+--><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.apache.nifi</groupId>
@@ -33,6 +32,7 @@
         <module>nifi-web-ui</module>
         <module>nifi-jetty</module>
         <module>nifi-web-content-access</module>
+        <module>nifi-ui-extension</module>
     </modules>
     <dependencyManagement>
         <dependencies>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestDetectDuplicate.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestDetectDuplicate.java b/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestDetectDuplicate.java
index 4a74416..eed0d36 100644
--- a/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestDetectDuplicate.java
+++ b/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestDetectDuplicate.java
@@ -32,8 +32,10 @@ import org.apache.nifi.distributed.cache.client.Deserializer;
 import org.apache.nifi.distributed.cache.client.DistributedMapCacheClient;
 import org.apache.nifi.distributed.cache.client.DistributedMapCacheClientService;
 import org.apache.nifi.distributed.cache.client.Serializer;
+import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.reporting.InitializationException;
 import org.apache.nifi.util.MockControllerServiceInitializationContext;
+import org.apache.nifi.util.MockProcessorLog;
 import org.apache.nifi.util.TestRunner;
 import org.apache.nifi.util.TestRunners;
 import org.junit.Test;
@@ -111,7 +113,8 @@ public class TestDetectDuplicate {
     private DistributedMapCacheClientImpl createClient() throws InitializationException {
 
         final DistributedMapCacheClientImpl client = new DistributedMapCacheClientImpl();
-        MockControllerServiceInitializationContext clientInitContext = new MockControllerServiceInitializationContext(client, "client");
+        final ComponentLog logger = new MockProcessorLog("client", client);
+        MockControllerServiceInitializationContext clientInitContext = new MockControllerServiceInitializationContext(client, "client", logger);
         client.initialize(clientInitContext);
 
         return client;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-client-service/src/main/java/org/apache/nifi/distributed/cache/client/DistributedMapCacheClientService.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-client-service/src/main/java/org/apache/nifi/distributed/cache/client/DistributedMapCacheClientService.java b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-client-service/src/main/java/org/apache/nifi/distributed/cache/client/DistributedMapCacheClientService.java
index feb6199..92bda8f 100644
--- a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-client-service/src/main/java/org/apache/nifi/distributed/cache/client/DistributedMapCacheClientService.java
+++ b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-client-service/src/main/java/org/apache/nifi/distributed/cache/client/DistributedMapCacheClientService.java
@@ -24,6 +24,7 @@ import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
 import org.apache.nifi.annotation.documentation.SeeAlso;
 import org.apache.nifi.annotation.lifecycle.OnEnabled;
 import org.apache.nifi.components.PropertyDescriptor;
@@ -42,6 +43,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @SeeAlso(classNames={"org.apache.nifi.distributed.cache.server.map.DistributedMapCacheServer", "org.apache.nifi.ssl.StandardSSLContextService"})
+@CapabilityDescription("Provides the ability to communicate with a DistributedMapCacheServer. This can be used in order to share a Map "
+        + "between nodes in a NiFi cluster")
 public class DistributedMapCacheClientService extends AbstractControllerService implements DistributedMapCacheClient {
 
     private static final Logger logger = LoggerFactory.getLogger(DistributedMapCacheClientService.class);
@@ -61,16 +64,15 @@ public class DistributedMapCacheClientService extends AbstractControllerService
             .build();
     public static final PropertyDescriptor SSL_CONTEXT_SERVICE = new PropertyDescriptor.Builder()
             .name("SSL Context Service")
-            .description(
-                    "If specified, indicates the SSL Context Service that is used to communicate with the remote server. If not specified, communications will not be encrypted")
+            .description("If specified, indicates the SSL Context Service that is used to communicate with the "
+            		+ "remote server. If not specified, communications will not be encrypted")
             .required(false)
-            .addValidator(StandardValidators.createControllerServiceExistsValidator(SSLContextService.class))
-            .defaultValue(null)
+            .identifiesControllerService(SSLContextService.class)
             .build();
     public static final PropertyDescriptor COMMUNICATIONS_TIMEOUT = new PropertyDescriptor.Builder()
             .name("Communications Timeout")
-            .description(
-                    "Specifies how long to wait when communicating with the remote server before determining that there is a communications failure if data cannot be sent or received")
+            .description("Specifies how long to wait when communicating with the remote server before determining that "
+            		+ "there is a communications failure if data cannot be sent or received")
             .required(true)
             .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
             .defaultValue("30 secs")
@@ -96,8 +98,7 @@ public class DistributedMapCacheClientService extends AbstractControllerService
     }
 
     @Override
-    public <K, V> boolean putIfAbsent(final K key, final V value, final Serializer<K> keySerializer, final Serializer<V> valueSerializer)
-            throws IOException {
+    public <K, V> boolean putIfAbsent(final K key, final V value, final Serializer<K> keySerializer, final Serializer<V> valueSerializer) throws IOException {
         return withCommsSession(new CommsAction<Boolean>() {
             @Override
             public Boolean execute(final CommsSession session) throws IOException {
@@ -133,8 +134,7 @@ public class DistributedMapCacheClientService extends AbstractControllerService
     }
 
     @Override
-    public <K, V> V getAndPutIfAbsent(final K key, final V value, final Serializer<K> keySerializer, final Serializer<V> valueSerializer,
-            final Deserializer<V> valueDeserializer) throws IOException {
+    public <K, V> V getAndPutIfAbsent(final K key, final V value, final Serializer<K> keySerializer, final Serializer<V> valueSerializer, final Deserializer<V> valueDeserializer) throws IOException {
         return withCommsSession(new CommsAction<V>() {
             @Override
             public V execute(final CommsSession session) throws IOException {
@@ -299,7 +299,6 @@ public class DistributedMapCacheClientService extends AbstractControllerService
     }
 
     private static interface CommsAction<T> {
-
         T execute(CommsSession commsSession) throws IOException;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-client-service/src/main/java/org/apache/nifi/distributed/cache/client/DistributedSetCacheClientService.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-client-service/src/main/java/org/apache/nifi/distributed/cache/client/DistributedSetCacheClientService.java b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-client-service/src/main/java/org/apache/nifi/distributed/cache/client/DistributedSetCacheClientService.java
index 7f38394..2de4ccb 100644
--- a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-client-service/src/main/java/org/apache/nifi/distributed/cache/client/DistributedSetCacheClientService.java
+++ b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-client-service/src/main/java/org/apache/nifi/distributed/cache/client/DistributedSetCacheClientService.java
@@ -24,6 +24,7 @@ import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
 import org.apache.nifi.annotation.documentation.SeeAlso;
 import org.apache.nifi.annotation.lifecycle.OnEnabled;
 import org.apache.nifi.components.PropertyDescriptor;
@@ -41,7 +42,9 @@ import org.apache.nifi.stream.io.DataOutputStream;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-@SeeAlso(classNames={"org.apache.nifi.distributed.cache.server.map.DistributedMapCacheServer", "org.apache.nifi.ssl.StandardSSLContextService"})
+@SeeAlso(classNames={"org.apache.nifi.distributed.cache.server.DistributedSetCacheServer", "org.apache.nifi.ssl.StandardSSLContextService"})
+@CapabilityDescription("Provides the ability to communicate with a DistributedSetCacheServer. This can be used in order to share a Set "
+        + "between nodes in a NiFi cluster")
 public class DistributedSetCacheClientService extends AbstractControllerService implements DistributedSetCacheClient {
 
     private static final Logger logger = LoggerFactory.getLogger(DistributedMapCacheClientService.class);
@@ -61,16 +64,15 @@ public class DistributedSetCacheClientService extends AbstractControllerService
             .build();
     public static final PropertyDescriptor SSL_CONTEXT_SERVICE = new PropertyDescriptor.Builder()
             .name("SSL Context Service")
-            .description(
-                    "If specified, indicates the SSL Context Service that is used to communicate with the remote server. If not specified, communications will not be encrypted")
+            .description("If specified, indicates the SSL Context Service that is used to communicate with the "
+            		+ "remote server. If not specified, communications will not be encrypted")
             .required(false)
-            .addValidator(StandardValidators.createControllerServiceExistsValidator(SSLContextService.class))
-            .defaultValue(null)
+            .identifiesControllerService(SSLContextService.class)
             .build();
     public static final PropertyDescriptor COMMUNICATIONS_TIMEOUT = new PropertyDescriptor.Builder()
             .name("Communications Timeout")
-            .description(
-                    "Specifices how long to wait when communicating with the remote server before determining that there is a communications failure if data cannot be sent or received")
+            .description("Specifices how long to wait when communicating with the remote server before determining "
+            		+ "that there is a communications failure if data cannot be sent or received")
             .required(true)
             .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
             .defaultValue("30 secs")

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-server/src/main/java/org/apache/nifi/distributed/cache/server/AbstractCacheServer.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-server/src/main/java/org/apache/nifi/distributed/cache/server/AbstractCacheServer.java b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-server/src/main/java/org/apache/nifi/distributed/cache/server/AbstractCacheServer.java
index a9643ab..a6a2458 100644
--- a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-server/src/main/java/org/apache/nifi/distributed/cache/server/AbstractCacheServer.java
+++ b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-server/src/main/java/org/apache/nifi/distributed/cache/server/AbstractCacheServer.java
@@ -163,7 +163,7 @@ public abstract class AbstractCacheServer implements CacheServer {
         stopped = true;
         logger.info("Stopping CacheServer {}", new Object[] { this.identifier });
 
-        if (serverSocketChannel != null) {
+        if (serverSocketChannel != null && serverSocketChannel.isOpen()) {
             serverSocketChannel.close();
         }
         // need to close out the created SocketChannels...this is done by interrupting

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-server/src/main/java/org/apache/nifi/distributed/cache/server/DistributedCacheServer.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-server/src/main/java/org/apache/nifi/distributed/cache/server/DistributedCacheServer.java b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-server/src/main/java/org/apache/nifi/distributed/cache/server/DistributedCacheServer.java
index 0817479..f2e848f 100644
--- a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-server/src/main/java/org/apache/nifi/distributed/cache/server/DistributedCacheServer.java
+++ b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-server/src/main/java/org/apache/nifi/distributed/cache/server/DistributedCacheServer.java
@@ -20,8 +20,8 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.nifi.annotation.lifecycle.OnDisabled;
 import org.apache.nifi.annotation.lifecycle.OnEnabled;
-import org.apache.nifi.annotation.lifecycle.OnShutdown;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.controller.AbstractControllerService;
 import org.apache.nifi.controller.ConfigurationContext;
@@ -42,10 +42,10 @@ public abstract class DistributedCacheServer extends AbstractControllerService {
             .build();
     public static final PropertyDescriptor SSL_CONTEXT_SERVICE = new PropertyDescriptor.Builder()
             .name("SSL Context Service")
-            .description(
-                    "If specified, this service will be used to create an SSL Context that will be used to secure communications; if not specified, communications will not be secure")
+            .description("If specified, this service will be used to create an SSL Context that will be used "
+            		+ "to secure communications; if not specified, communications will not be secure")
             .required(false)
-            .addValidator(StandardValidators.createControllerServiceExistsValidator(SSLContextService.class))
+            .identifiesControllerService(SSLContextService.class)
             .build();
     public static final PropertyDescriptor MAX_CACHE_ENTRIES = new PropertyDescriptor.Builder()
             .name("Maximum Cache Entries")
@@ -77,8 +77,7 @@ public abstract class DistributedCacheServer extends AbstractControllerService {
         properties.add(MAX_CACHE_ENTRIES);
         properties.add(EVICTION_POLICY);
         properties.add(PERSISTENCE_PATH);
-        properties.add(new PropertyDescriptor.Builder().fromPropertyDescriptor(SSL_CONTEXT_SERVICE).allowableValues(
-                getControllerServiceLookup().getControllerServiceIdentifiers(SSLContextService.class)).build());
+        properties.add(SSL_CONTEXT_SERVICE);
         return properties;
     }
 
@@ -90,7 +89,7 @@ public abstract class DistributedCacheServer extends AbstractControllerService {
         }
     }
 
-    @OnShutdown
+    @OnDisabled
     public void shutdownServer() throws IOException {
         if (cacheServer != null) {
             cacheServer.stop();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-standard-services/nifi-http-context-map-bundle/nifi-http-context-map/src/main/java/org/apache/nifi/http/StandardHttpContextMap.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-http-context-map-bundle/nifi-http-context-map/src/main/java/org/apache/nifi/http/StandardHttpContextMap.java b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-http-context-map-bundle/nifi-http-context-map/src/main/java/org/apache/nifi/http/StandardHttpContextMap.java
index c73b98b..5e31270 100644
--- a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-http-context-map-bundle/nifi-http-context-map/src/main/java/org/apache/nifi/http/StandardHttpContextMap.java
+++ b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-http-context-map-bundle/nifi-http-context-map/src/main/java/org/apache/nifi/http/StandardHttpContextMap.java
@@ -31,6 +31,9 @@ import javax.servlet.AsyncContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
 import org.apache.nifi.annotation.lifecycle.OnDisabled;
 import org.apache.nifi.annotation.lifecycle.OnEnabled;
 import org.apache.nifi.components.PropertyDescriptor;
@@ -38,6 +41,12 @@ import org.apache.nifi.controller.AbstractControllerService;
 import org.apache.nifi.controller.ConfigurationContext;
 import org.apache.nifi.processor.util.StandardValidators;
 
+@Tags({"http", "request", "response"})
+@SeeAlso(classNames={
+        "org.apache.nifi.processors.standard.HandleHttpRequest", 
+        "org.apache.nifi.processors.standard.HandleHttpResponse"})
+@CapabilityDescription("Provides the ability to store and retrieve HTTP requests and responses external to a Processor, so that "
+        + "multiple Processors can interact with the same HTTP request.")
 public class StandardHttpContextMap extends AbstractControllerService implements HttpContextMap {
     public static final PropertyDescriptor MAX_OUTSTANDING_REQUESTS = new PropertyDescriptor.Builder()
         .name("Maximum Outstanding Requests")

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/src/main/java/org/apache/nifi/ssl/StandardSSLContextService.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/src/main/java/org/apache/nifi/ssl/StandardSSLContextService.java b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/src/main/java/org/apache/nifi/ssl/StandardSSLContextService.java
index ae2e19c..34f1844 100644
--- a/nifi/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/src/main/java/org/apache/nifi/ssl/StandardSSLContextService.java
+++ b/nifi/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/src/main/java/org/apache/nifi/ssl/StandardSSLContextService.java
@@ -62,7 +62,6 @@ public class StandardSSLContextService extends AbstractControllerService impleme
             .description("The Type of the Truststore. Either JKS or PKCS12")
             .allowableValues(STORE_TYPE_JKS, STORE_TYPE_PKCS12)
             .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
-            .defaultValue(STORE_TYPE_JKS)
             .sensitive(false)
             .build();
     public static final PropertyDescriptor TRUSTSTORE_PASSWORD = new PropertyDescriptor.Builder()
@@ -84,7 +83,6 @@ public class StandardSSLContextService extends AbstractControllerService impleme
             .description("The Type of the Keystore")
             .allowableValues(STORE_TYPE_JKS, STORE_TYPE_PKCS12)
             .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
-            .defaultValue(STORE_TYPE_JKS)
             .sensitive(false)
             .build();
     public static final PropertyDescriptor KEYSTORE_PASSWORD = new PropertyDescriptor.Builder()
@@ -96,6 +94,7 @@ public class StandardSSLContextService extends AbstractControllerService impleme
             .build();
 
     private static final List<PropertyDescriptor> properties;
+    private ConfigurationContext configContext;
 
     static {
         List<PropertyDescriptor> props = new ArrayList<>();
@@ -107,7 +106,6 @@ public class StandardSSLContextService extends AbstractControllerService impleme
         props.add(TRUSTSTORE_TYPE);
         properties = Collections.unmodifiableList(props);
     }
-    private ConfigurationContext configContext;
 
     @OnEnabled
     public void onConfigured(final ConfigurationContext context) throws InitializationException {
@@ -196,7 +194,7 @@ public class StandardSSLContextService extends AbstractControllerService impleme
         if (results.isEmpty()) {
             // verify that the filename, password, and type match
             try {
-                createSSLContext(ClientAuth.REQUIRED);
+                verifySslConfig(validationContext);
             } catch (ProcessException e) {
                 results.add(new ValidationResult.Builder()
                         .subject(getClass().getSimpleName() + " : " + getIdentifier())
@@ -207,6 +205,39 @@ public class StandardSSLContextService extends AbstractControllerService impleme
         }
         return results;
     }
+    
+    private void verifySslConfig(final ValidationContext validationContext) throws ProcessException {
+        try {
+            final String keystoreFile = validationContext.getProperty(KEYSTORE).getValue();
+            if (keystoreFile == null) {
+                SslContextFactory.createTrustSslContext(
+                        validationContext.getProperty(TRUSTSTORE).getValue(),
+                        validationContext.getProperty(TRUSTSTORE_PASSWORD).getValue().toCharArray(),
+                        validationContext.getProperty(TRUSTSTORE_TYPE).getValue());
+                return;
+            }
+            final String truststoreFile = validationContext.getProperty(TRUSTSTORE).getValue();
+            if (truststoreFile == null) {
+                SslContextFactory.createSslContext(
+                        validationContext.getProperty(KEYSTORE).getValue(),
+                        validationContext.getProperty(KEYSTORE_PASSWORD).getValue().toCharArray(),
+                        validationContext.getProperty(KEYSTORE_TYPE).getValue());
+                return;
+            }
+
+            SslContextFactory.createSslContext(
+                    validationContext.getProperty(KEYSTORE).getValue(),
+                    validationContext.getProperty(KEYSTORE_PASSWORD).getValue().toCharArray(),
+                    validationContext.getProperty(KEYSTORE_TYPE).getValue(),
+                    validationContext.getProperty(TRUSTSTORE).getValue(),
+                    validationContext.getProperty(TRUSTSTORE_PASSWORD).getValue().toCharArray(),
+                    validationContext.getProperty(TRUSTSTORE_TYPE).getValue(),
+                    org.apache.nifi.security.util.SslContextFactory.ClientAuth.REQUIRED);
+        } catch (final Exception e) {
+            throw new ProcessException(e);
+        }
+    }
+    
 
     @Override
     public SSLContext createSSLContext(final ClientAuth clientAuth) throws ProcessException {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/java/org/apache/nifi/update/attributes/api/RuleResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/java/org/apache/nifi/update/attributes/api/RuleResource.java b/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/java/org/apache/nifi/update/attributes/api/RuleResource.java
index dc2d27c..61b39af 100644
--- a/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/java/org/apache/nifi/update/attributes/api/RuleResource.java
+++ b/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/java/org/apache/nifi/update/attributes/api/RuleResource.java
@@ -58,11 +58,7 @@ import org.apache.nifi.update.attributes.entity.ConditionEntity;
 import org.apache.nifi.update.attributes.entity.RuleEntity;
 import org.apache.nifi.update.attributes.entity.RulesEntity;
 import org.apache.nifi.update.attributes.serde.CriteriaSerDe;
-import org.apache.nifi.web.HttpServletRequestContextConfig;
 import org.apache.nifi.web.InvalidRevisionException;
-import org.apache.nifi.web.NiFiWebContext;
-import org.apache.nifi.web.NiFiWebContextConfig;
-import org.apache.nifi.web.ProcessorInfo;
 import org.apache.nifi.web.Revision;
 import org.apache.commons.lang3.StringUtils;
 
@@ -70,6 +66,13 @@ import com.sun.jersey.api.NotFoundException;
 
 import org.apache.nifi.update.attributes.FlowFilePolicy;
 import org.apache.nifi.update.attributes.entity.EvaluationContextEntity;
+import org.apache.nifi.web.ComponentDetails;
+import org.apache.nifi.web.HttpServletConfigurationRequestContext;
+import org.apache.nifi.web.HttpServletRequestContext;
+import org.apache.nifi.web.NiFiWebConfigurationContext;
+import org.apache.nifi.web.NiFiWebConfigurationRequestContext;
+import org.apache.nifi.web.NiFiWebRequestContext;
+import org.apache.nifi.web.UiExtensionType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -90,24 +93,19 @@ public class RuleResource {
     @GET
     @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
     @Path("/evaluation-context")
-    public Response getEvaluationContext(
-            @QueryParam("processorId") final String processorId,
-            @QueryParam("clientId") final String clientId,
-            @QueryParam("revision") final Long revision) {
+    public Response getEvaluationContext(@QueryParam("processorId") final String processorId) {
 
         // get the web context
-        final NiFiWebContext nifiWebContext = (NiFiWebContext) servletContext.getAttribute("nifi-web-context");
+        final NiFiWebConfigurationContext nifiWebContext = (NiFiWebConfigurationContext) servletContext.getAttribute("nifi-web-configuration-context");
 
         // build the web context config
-        final NiFiWebContextConfig contextConfig = getWebContextConfig(processorId, revision, clientId);
+        final NiFiWebRequestContext contextConfig = getRequestContext(processorId);
 
         // load the criteria 
         final Criteria criteria = getCriteria(nifiWebContext, contextConfig);
 
         // create the response entity
         final EvaluationContextEntity responseEntity = new EvaluationContextEntity();
-        responseEntity.setClientId(clientId);
-        responseEntity.setRevision(revision);
         responseEntity.setProcessorId(processorId);
         responseEntity.setFlowFilePolicy(criteria.getFlowFilePolicy().name());
         responseEntity.setRuleOrder(criteria.getRuleOrder());
@@ -126,7 +124,7 @@ public class RuleResource {
             final EvaluationContextEntity requestEntity) {
 
         // get the web context
-        final NiFiWebContext nifiWebContext = (NiFiWebContext) servletContext.getAttribute("nifi-web-context");
+        final NiFiWebConfigurationContext configurationContext = (NiFiWebConfigurationContext) servletContext.getAttribute("nifi-web-configuration-context");
 
         // ensure the evaluation context has been specified
         if (requestEntity == null) {
@@ -139,11 +137,11 @@ public class RuleResource {
         }
 
         // build the web context config
-        final NiFiWebContextConfig contextConfig = getWebContextConfig(
+        final NiFiWebConfigurationRequestContext requestContext = getConfigurationRequestContext(
                 requestEntity.getProcessorId(), requestEntity.getRevision(), requestEntity.getClientId());
 
         // load the criteria
-        final Criteria criteria = getCriteria(nifiWebContext, contextConfig);
+        final Criteria criteria = getCriteria(configurationContext, requestContext);
 
         // if a new rule order is specified, attempt to set it
         if (requestEntity.getRuleOrder() != null) {
@@ -164,7 +162,7 @@ public class RuleResource {
         }
 
         // save the criteria
-        saveCriteria(contextConfig, criteria);
+        saveCriteria(requestContext, criteria);
 
         // create the response entity
         final EvaluationContextEntity responseEntity = new EvaluationContextEntity();
@@ -188,7 +186,7 @@ public class RuleResource {
             final RuleEntity requestEntity) {
 
         // get the web context
-        final NiFiWebContext nifiWebContext = (NiFiWebContext) servletContext.getAttribute("nifi-web-context");
+        final NiFiWebConfigurationContext configurationContext = (NiFiWebConfigurationContext) servletContext.getAttribute("nifi-web-configuration-context");
 
         // ensure the rule has been specified
         if (requestEntity == null || requestEntity.getRule() == null) {
@@ -215,12 +213,12 @@ public class RuleResource {
         // generate a new id
         final String uuid = UUID.randomUUID().toString();
 
-        // build the web context config
-        final NiFiWebContextConfig contextConfig = getWebContextConfig(
+        // build the request context
+        final NiFiWebConfigurationRequestContext requestContext = getConfigurationRequestContext(
                 requestEntity.getProcessorId(), requestEntity.getRevision(), requestEntity.getClientId());
 
         // load the criteria
-        final Criteria criteria = getCriteria(nifiWebContext, contextConfig);
+        final Criteria criteria = getCriteria(configurationContext, requestContext);
         final UpdateAttributeModelFactory factory = new UpdateAttributeModelFactory();
 
         // create the new rule
@@ -236,7 +234,7 @@ public class RuleResource {
         criteria.addRule(rule);
 
         // save the criteria
-        saveCriteria(contextConfig, criteria);
+        saveCriteria(requestContext, criteria);
 
         // create the response entity
         final RuleEntity responseEntity = new RuleEntity();
@@ -326,18 +324,16 @@ public class RuleResource {
     public Response getRule(
             @PathParam("id") final String ruleId,
             @QueryParam("processorId") final String processorId,
-            @QueryParam("clientId") final String clientId,
-            @QueryParam("revision") final Long revision,
             @DefaultValue("false") @QueryParam("verbose") final Boolean verbose) {
 
         // get the web context
-        final NiFiWebContext nifiWebContext = (NiFiWebContext) servletContext.getAttribute("nifi-web-context");
+        final NiFiWebConfigurationContext configurationContext = (NiFiWebConfigurationContext) servletContext.getAttribute("nifi-web-configuration-context");
 
         // build the web context config
-        final NiFiWebContextConfig contextConfig = getWebContextConfig(processorId, revision, clientId);
+        final NiFiWebRequestContext requestContext = getRequestContext(processorId);
 
         // load the criteria and get the rule
-        final Criteria criteria = getCriteria(nifiWebContext, contextConfig);
+        final Criteria criteria = getCriteria(configurationContext, requestContext);
         final Rule rule = criteria.getRule(ruleId);
 
         if (rule == null) {
@@ -348,15 +344,13 @@ public class RuleResource {
         final RuleDTO ruleDto = DtoFactory.createRuleDTO(rule);
 
         // prune if appropriate
-        if (!verbose.booleanValue()) {
+        if (!verbose) {
             ruleDto.setConditions(null);
             ruleDto.setActions(null);
         }
 
         // create the response entity
         final RuleEntity responseEntity = new RuleEntity();
-        responseEntity.setClientId(clientId);
-        responseEntity.setRevision(revision);
         responseEntity.setProcessorId(processorId);
         responseEntity.setRule(ruleDto);
 
@@ -370,18 +364,16 @@ public class RuleResource {
     @Path("/rules")
     public Response getRules(
             @QueryParam("processorId") final String processorId,
-            @QueryParam("clientId") final String clientId,
-            @QueryParam("revision") final Long revision,
             @DefaultValue("false") @QueryParam("verbose") final Boolean verbose) {
 
         // get the web context
-        final NiFiWebContext nifiWebContext = (NiFiWebContext) servletContext.getAttribute("nifi-web-context");
+        final NiFiWebConfigurationContext configurationContext = (NiFiWebConfigurationContext) servletContext.getAttribute("nifi-web-configuration-context");
 
         // build the web context config
-        final NiFiWebContextConfig contextConfig = getWebContextConfig(processorId, revision, clientId);
+        final NiFiWebRequestContext requestContext = getRequestContext(processorId);
 
         // load the criteria 
-        final Criteria criteria = getCriteria(nifiWebContext, contextConfig);
+        final Criteria criteria = getCriteria(configurationContext, requestContext);
         final List<Rule> rules = criteria.getRules();
 
         // generate the rules
@@ -393,7 +385,7 @@ public class RuleResource {
                 ruleDtos.add(ruleDto);
 
                 // prune if appropriate
-                if (!verbose.booleanValue()) {
+                if (!verbose) {
                     ruleDto.setConditions(null);
                     ruleDto.setActions(null);
                 }
@@ -402,8 +394,6 @@ public class RuleResource {
 
         // create the response entity
         final RulesEntity responseEntity = new RulesEntity();
-        responseEntity.setClientId(clientId);
-        responseEntity.setRevision(revision);
         responseEntity.setProcessorId(processorId);
         responseEntity.setRules(ruleDtos);
 
@@ -417,19 +407,16 @@ public class RuleResource {
     @Path("/rules/search-results")
     public Response searchRules(
             @QueryParam("processorId") final String processorId,
-            @QueryParam("clientId") final String clientId,
-            @QueryParam("revision") final Long revision,
             @QueryParam("q") final String term) {
 
         // get the web context
-        final NiFiWebContext nifiWebContext = (NiFiWebContext) servletContext.getAttribute("nifi-web-context");
+        final NiFiWebConfigurationContext configurationContext = (NiFiWebConfigurationContext) servletContext.getAttribute("nifi-web-context");
 
         // build the web context config
-        final NiFiWebContextConfig contextConfig = getWebContextConfig(
-                processorId, revision, clientId);
+        final NiFiWebRequestContext requestContext = getRequestContext(processorId);
 
         // load the criteria 
-        final Criteria criteria = getCriteria(nifiWebContext, contextConfig);
+        final Criteria criteria = getCriteria(configurationContext, requestContext);
         final List<Rule> rules = criteria.getRules();
 
         // generate the rules
@@ -455,8 +442,6 @@ public class RuleResource {
 
         // create the response entity
         final RulesEntity responseEntity = new RulesEntity();
-        responseEntity.setClientId(clientId);
-        responseEntity.setRevision(revision);
         responseEntity.setProcessorId(processorId);
         responseEntity.setRules(ruleDtos);
 
@@ -475,7 +460,7 @@ public class RuleResource {
             final RuleEntity requestEntity) {
 
         // get the web context
-        final NiFiWebContext nifiWebContext = (NiFiWebContext) servletContext.getAttribute("nifi-web-context");
+        final NiFiWebConfigurationContext nifiWebContext = (NiFiWebConfigurationContext) servletContext.getAttribute("nifi-web-configuration-context");
 
         // ensure the rule has been specified
         if (requestEntity == null || requestEntity.getRule() == null) {
@@ -508,12 +493,12 @@ public class RuleResource {
         }
 
         // build the web context config
-        final NiFiWebContextConfig contextConfig = getWebContextConfig(
+        final NiFiWebConfigurationRequestContext requestContext = getConfigurationRequestContext(
                 requestEntity.getProcessorId(), requestEntity.getRevision(), requestEntity.getClientId());
 
         // load the criteria
         final UpdateAttributeModelFactory factory = new UpdateAttributeModelFactory();
-        final Criteria criteria = getCriteria(nifiWebContext, contextConfig);
+        final Criteria criteria = getCriteria(nifiWebContext, requestContext);
 
         // attempt to locate the rule
         Rule rule = criteria.getRule(ruleId);
@@ -546,7 +531,7 @@ public class RuleResource {
         }
 
         // save the criteria
-        saveCriteria(contextConfig, criteria);
+        saveCriteria(requestContext, criteria);
 
         // create the response entity
         final RuleEntity responseEntity = new RuleEntity();
@@ -576,14 +561,13 @@ public class RuleResource {
             @QueryParam("revision") final Long revision) {
 
         // get the web context
-        final NiFiWebContext nifiWebContext = (NiFiWebContext) servletContext.getAttribute("nifi-web-context");
+        final NiFiWebConfigurationContext configurationContext = (NiFiWebConfigurationContext) servletContext.getAttribute("nifi-web-configuration-context");
 
         // build the web context config
-        final NiFiWebContextConfig contextConfig = getWebContextConfig(
-                processorId, revision, clientId);
+        final NiFiWebConfigurationRequestContext requestContext = getConfigurationRequestContext(processorId, revision, clientId);
 
         // load the criteria and get the rule
-        final Criteria criteria = getCriteria(nifiWebContext, contextConfig);
+        final Criteria criteria = getCriteria(configurationContext, requestContext);
         final Rule rule = criteria.getRule(ruleId);
 
         if (rule == null) {
@@ -594,7 +578,7 @@ public class RuleResource {
         criteria.deleteRule(rule);
 
         // save the criteria
-        saveCriteria(contextConfig, criteria);
+        saveCriteria(requestContext, criteria);
 
         // create the response entity
         final RulesEntity responseEntity = new RulesEntity();
@@ -607,26 +591,26 @@ public class RuleResource {
         return noCache(response).build();
     }
 
-    private Criteria getCriteria(final NiFiWebContext nifiWebContext, final NiFiWebContextConfig contextConfig) {
-        final ProcessorInfo processorInfo;
+    private Criteria getCriteria(final NiFiWebConfigurationContext configurationContext, final NiFiWebRequestContext requestContext) {
+        final ComponentDetails processorDetails;
 
         try {
             // load the processor configuration
-            processorInfo = nifiWebContext.getProcessor(contextConfig);
+            processorDetails = configurationContext.getComponentDetails(requestContext);
         } catch (final InvalidRevisionException ire) {
             throw new WebApplicationException(invalidRevision(ire.getMessage()));
         } catch (final Exception e) {
-            final String message = String.format("Unable to get UpdateAttribute[id=%s] criteria: %s", contextConfig.getProcessorId(), e);
+            final String message = String.format("Unable to get UpdateAttribute[id=%s] criteria: %s", requestContext.getId(), e);
             logger.error(message, e);
             throw new WebApplicationException(error(message));
         }
 
         Criteria criteria = null;
-        if (processorInfo != null) {
+        if (processorDetails != null) {
             try {
-                criteria = CriteriaSerDe.deserialize(processorInfo.getAnnotationData());
+                criteria = CriteriaSerDe.deserialize(processorDetails.getAnnotationData());
             } catch (final IllegalArgumentException iae) {
-                final String message = String.format("Unable to deserialize existing rules for UpdateAttribute[id=%s]. Deserialization error: %s", contextConfig.getProcessorId(), iae);
+                final String message = String.format("Unable to deserialize existing rules for UpdateAttribute[id=%s]. Deserialization error: %s", requestContext.getId(), iae);
                 logger.error(message, iae);
                 throw new WebApplicationException(error(message));
             }
@@ -639,29 +623,38 @@ public class RuleResource {
         return criteria;
     }
 
-    private void saveCriteria(final NiFiWebContextConfig contextConfig, final Criteria criteria) {
+    private void saveCriteria(final NiFiWebConfigurationRequestContext requestContext, final Criteria criteria) {
         // serialize the criteria
         final String annotationData = CriteriaSerDe.serialize(criteria);
 
         // get the web context
-        final NiFiWebContext nifiWebContext = (NiFiWebContext) servletContext.getAttribute("nifi-web-context");
+        final NiFiWebConfigurationContext configurationContext = (NiFiWebConfigurationContext) servletContext.getAttribute("nifi-web-configuration-context");
 
         try {
             // save the annotation data
-            nifiWebContext.setProcessorAnnotationData(contextConfig, annotationData);
+            configurationContext.setAnnotationData(requestContext, annotationData);
         } catch (final InvalidRevisionException ire) {
             throw new WebApplicationException(invalidRevision(ire.getMessage()));
         } catch (final Exception e) {
-            final String message = String.format("Unable to save UpdateAttribute[id=%s] criteria: %s", contextConfig.getProcessorId(), e);
+            final String message = String.format("Unable to save UpdateAttribute[id=%s] criteria: %s", requestContext.getId(), e);
             logger.error(message, e);
             throw new WebApplicationException(error(message));
         }
     }
-
-    private NiFiWebContextConfig getWebContextConfig(final String processorId, final Long revision, final String clientId) {
-        return new HttpServletRequestContextConfig(request) {
+    
+    private NiFiWebRequestContext getRequestContext(final String processorId) {
+        return new HttpServletRequestContext(UiExtensionType.ProcessorConfiguration, request) {
+            @Override
+            public String getId() {
+                return processorId;
+            }
+        };
+    }
+    
+    private NiFiWebConfigurationRequestContext getConfigurationRequestContext(final String processorId, final Long revision, final String clientId) {
+        return new HttpServletConfigurationRequestContext(UiExtensionType.ProcessorConfiguration, request) {
             @Override
-            public String getProcessorId() {
+            public String getId() {
                 return processorId;
             }
 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/META-INF/nifi-processor
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/META-INF/nifi-processor b/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/META-INF/nifi-processor
deleted file mode 100755
index 6eda457..0000000
--- a/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/META-INF/nifi-processor
+++ /dev/null
@@ -1,15 +0,0 @@
-# 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.
-org.apache.nifi.processors.attributes.UpdateAttribute

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/META-INF/nifi-processor-configuration
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/META-INF/nifi-processor-configuration b/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/META-INF/nifi-processor-configuration
new file mode 100755
index 0000000..6eda457
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/META-INF/nifi-processor-configuration
@@ -0,0 +1,15 @@
+# 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.
+org.apache.nifi.processors.attributes.UpdateAttribute


[51/62] [abbrv] incubator-nifi git commit: NIFI-506: Initial import of HL7 work

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/NotEqualsEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/NotEqualsEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/NotEqualsEvaluator.java
new file mode 100644
index 0000000..b7c1ce2
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/NotEqualsEvaluator.java
@@ -0,0 +1,32 @@
+/*
+ * 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.hl7.query.evaluator.comparison;
+
+import org.apache.nifi.hl7.query.evaluator.Evaluator;
+
+public class NotEqualsEvaluator extends AbstractComparisonEvaluator {
+	
+	public NotEqualsEvaluator(final Evaluator<?> lhs, final Evaluator<?> rhs) {
+		super(lhs, rhs);
+	}
+
+	@Override
+	protected boolean compare(final Object lhs, final Object rhs) {
+		return lhs != null && rhs != null && lhs != rhs && !lhs.equals(rhs) && !lhs.toString().equals(rhs.toString());
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/NotEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/NotEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/NotEvaluator.java
new file mode 100644
index 0000000..58888d9
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/NotEvaluator.java
@@ -0,0 +1,36 @@
+/*
+ * 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.hl7.query.evaluator.comparison;
+
+import java.util.Map;
+
+import org.apache.nifi.hl7.query.evaluator.BooleanEvaluator;
+
+public class NotEvaluator extends BooleanEvaluator {
+	private final BooleanEvaluator subjectEvaluator;
+	
+	public NotEvaluator(final BooleanEvaluator subjectEvaluator) {
+		this.subjectEvaluator = subjectEvaluator;
+	}
+	
+	@Override
+	public Boolean evaluate(final Map<String, Object> objectMap) {
+		final Boolean subjectValue = subjectEvaluator.evaluate(objectMap);
+		return (subjectValue == null || Boolean.TRUE.equals(subjectValue));
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/NotNullEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/NotNullEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/NotNullEvaluator.java
new file mode 100644
index 0000000..a764fef
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/NotNullEvaluator.java
@@ -0,0 +1,65 @@
+/*
+ * 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.hl7.query.evaluator.comparison;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.nifi.hl7.model.HL7Component;
+import org.apache.nifi.hl7.query.evaluator.BooleanEvaluator;
+import org.apache.nifi.hl7.query.evaluator.Evaluator;
+
+public class NotNullEvaluator extends BooleanEvaluator {
+	private final Evaluator<?> subjectEvaluator;
+	
+	public NotNullEvaluator(final Evaluator<?> subjectEvaluator) {
+		this.subjectEvaluator = subjectEvaluator;
+	}
+	
+	@Override
+	public Boolean evaluate(final Map<String, Object> objectMap) {
+		Object subjectValue = subjectEvaluator.evaluate(objectMap);
+		if ( subjectValue == null ) {
+			return false;
+		}
+		
+		return isNotNull(subjectValue);
+	}
+
+	private boolean isNotNull(Object subjectValue) {
+		if ( subjectValue instanceof HL7Component ) {
+			subjectValue = ((HL7Component) subjectValue).getValue();
+		}
+		
+		if ( subjectValue instanceof Collection ) {
+			final Collection<?> collection = (Collection<?>) subjectValue;
+			if ( collection.isEmpty() ) {
+				return false;
+			}
+			
+			for ( final Object obj : collection ) {
+				if ( isNotNull(obj) ) {
+					return true;
+				}
+			}
+			
+			return false;
+		}
+		
+		return subjectValue != null;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/literal/IntegerLiteralEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/literal/IntegerLiteralEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/literal/IntegerLiteralEvaluator.java
new file mode 100644
index 0000000..c6ff6e4
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/literal/IntegerLiteralEvaluator.java
@@ -0,0 +1,36 @@
+/*
+ * 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.hl7.query.evaluator.literal;
+
+import java.util.Map;
+
+import org.apache.nifi.hl7.query.evaluator.IntegerEvaluator;
+
+public class IntegerLiteralEvaluator extends IntegerEvaluator {
+	private final Integer value;
+	
+	public IntegerLiteralEvaluator(final Integer value) {
+		this.value = value;
+	}
+	
+	
+	@Override
+	public Integer evaluate(final Map<String, Object> objectMap) {
+		return value;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/literal/StringLiteralEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/literal/StringLiteralEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/literal/StringLiteralEvaluator.java
new file mode 100644
index 0000000..3b29611
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/literal/StringLiteralEvaluator.java
@@ -0,0 +1,35 @@
+/*
+ * 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.hl7.query.evaluator.literal;
+
+import java.util.Map;
+
+import org.apache.nifi.hl7.query.evaluator.StringEvaluator;
+
+public class StringLiteralEvaluator extends StringEvaluator {
+	private final String value;
+	
+	public StringLiteralEvaluator(final String value) {
+		this.value = value;
+	}
+	
+	@Override
+	public String evaluate(final Map<String, Object> objectMap) {
+		return value;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/logic/AndEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/logic/AndEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/logic/AndEvaluator.java
new file mode 100644
index 0000000..21f596e
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/logic/AndEvaluator.java
@@ -0,0 +1,43 @@
+/*
+ * 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.hl7.query.evaluator.logic;
+
+import java.util.Map;
+
+import org.apache.nifi.hl7.query.evaluator.BooleanEvaluator;
+
+public class AndEvaluator extends BooleanEvaluator {
+	private final BooleanEvaluator lhs;
+	private final BooleanEvaluator rhs;
+	
+	public AndEvaluator(final BooleanEvaluator lhs, final BooleanEvaluator rhs) {
+		this.lhs = lhs;
+		this.rhs = rhs;
+	}
+	
+	@Override
+	public Boolean evaluate(final Map<String, Object> objectMap) {
+		final Boolean lhsValue = lhs.evaluate(objectMap);
+		if ( lhsValue == null || Boolean.FALSE.equals(lhsValue) ) {
+			return false;
+		}
+		
+		final Boolean rhsValue = rhs.evaluate(objectMap);
+		return (rhsValue != null && Boolean.TRUE.equals(rhsValue));
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/logic/OrEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/logic/OrEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/logic/OrEvaluator.java
new file mode 100644
index 0000000..d090946
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/logic/OrEvaluator.java
@@ -0,0 +1,43 @@
+/*
+ * 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.hl7.query.evaluator.logic;
+
+import java.util.Map;
+
+import org.apache.nifi.hl7.query.evaluator.BooleanEvaluator;
+
+public class OrEvaluator extends BooleanEvaluator {
+	private final BooleanEvaluator lhs;
+	private final BooleanEvaluator rhs;
+	
+	public OrEvaluator(final BooleanEvaluator lhs, final BooleanEvaluator rhs) {
+		this.lhs = lhs;
+		this.rhs = rhs;
+	}
+	
+	@Override
+	public Boolean evaluate(final Map<String, Object> objectMap) {
+		final Boolean lhsValue = lhs.evaluate(objectMap);
+		if ( lhsValue != null && Boolean.TRUE.equals(lhsValue) ) {
+			return true;
+		}
+		
+		final Boolean rhsValue = rhs.evaluate(objectMap);
+		return (rhsValue != null && Boolean.TRUE.equals(rhsValue));
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/DeclaredReferenceEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/DeclaredReferenceEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/DeclaredReferenceEvaluator.java
new file mode 100644
index 0000000..6afb8d7
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/DeclaredReferenceEvaluator.java
@@ -0,0 +1,42 @@
+/*
+ * 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.hl7.query.evaluator.message;
+
+import java.util.Map;
+
+import org.apache.nifi.hl7.query.evaluator.Evaluator;
+import org.apache.nifi.hl7.query.evaluator.StringEvaluator;
+
+public class DeclaredReferenceEvaluator implements Evaluator<Object> {
+	private final StringEvaluator referenceNameEvaluator;
+	
+	public DeclaredReferenceEvaluator(final StringEvaluator referenceNameEvaluator) {
+		this.referenceNameEvaluator = referenceNameEvaluator;
+	}
+	
+	@Override
+	public Object evaluate(final Map<String, Object> objectMap) {
+		final String referenceName = referenceNameEvaluator.evaluate(objectMap);
+		return objectMap.get(referenceName);
+	}
+
+	@Override
+	public Class<? extends Object> getType() {
+		return Object.class;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/DotEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/DotEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/DotEvaluator.java
new file mode 100644
index 0000000..c5fbf41
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/DotEvaluator.java
@@ -0,0 +1,88 @@
+/*
+ * 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.hl7.query.evaluator.message;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.nifi.hl7.model.HL7Component;
+import org.apache.nifi.hl7.model.HL7Message;
+import org.apache.nifi.hl7.model.HL7Segment;
+import org.apache.nifi.hl7.query.evaluator.Evaluator;
+import org.apache.nifi.hl7.query.evaluator.IntegerEvaluator;
+
+public class DotEvaluator implements Evaluator<Object> {
+	private final Evaluator<?> lhs;
+	private final IntegerEvaluator rhs;
+	
+	public DotEvaluator(final Evaluator<?> lhs, final IntegerEvaluator rhs) {
+		this.lhs = lhs;
+		this.rhs = rhs;
+	}
+	
+	@Override
+	public Object evaluate(final Map<String, Object> objectMap) {
+		final Object lhsValue = this.lhs.evaluate(objectMap);
+		final Integer rhsValue = this.rhs.evaluate(objectMap);
+		
+		if ( lhsValue == null || rhsValue == null ) {
+			return null;
+		}
+		
+		final List<Object> results = new ArrayList<>();
+		if ( lhsValue instanceof Collection ) {
+			final Collection<?> lhsCollection = (Collection<?>) lhsValue;
+			for ( final Object obj : lhsCollection ) {
+				final Object val = getValue(obj, rhsValue);
+				results.add(val);
+			}
+		} else {
+			final Object val = getValue(lhsValue, rhsValue);
+			return val;
+		}
+		
+		return results;
+	}
+	
+	private Object getValue(final Object lhsValue, final int rhsValue) {
+		final List<?> list;
+		if ( lhsValue instanceof HL7Message ) {
+			list = ((HL7Message) lhsValue).getSegments();
+		} else if ( lhsValue instanceof HL7Segment ) {
+			list = ((HL7Segment) lhsValue).getFields();
+		} else if ( lhsValue instanceof HL7Component ) {
+			list = ((HL7Component) lhsValue).getComponents();
+		} else {
+			return null;
+		}
+		
+		if ( rhsValue > list.size() ) {
+			return null;
+		}
+		
+		// convert from 0-based to 1-based
+		return list.get(rhsValue - 1);
+	}
+
+	@Override
+	public Class<? extends Object> getType() {
+		return Object.class;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/FieldEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/FieldEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/FieldEvaluator.java
new file mode 100644
index 0000000..869c2d0
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/FieldEvaluator.java
@@ -0,0 +1,67 @@
+/*
+ * 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.hl7.query.evaluator.message;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.nifi.hl7.model.HL7Field;
+import org.apache.nifi.hl7.model.HL7Segment;
+import org.apache.nifi.hl7.query.evaluator.Evaluator;
+import org.apache.nifi.hl7.query.evaluator.IntegerEvaluator;
+
+@SuppressWarnings("rawtypes")
+public class FieldEvaluator implements Evaluator<List> {
+	private final SegmentEvaluator segmentEvaluator;
+	private final IntegerEvaluator indexEvaluator;
+	
+	public FieldEvaluator(final SegmentEvaluator segmentEvaluator, final IntegerEvaluator indexEvaluator) {
+		this.segmentEvaluator = segmentEvaluator;
+		this.indexEvaluator = indexEvaluator;
+	}
+	
+	public List<HL7Field> evaluate(final Map<String, Object> objectMap) {
+		final List<HL7Segment> segments = segmentEvaluator.evaluate(objectMap);
+		if ( segments == null ) {
+			return Collections.emptyList();
+		}
+		
+		final Integer index = indexEvaluator.evaluate(objectMap);
+		if ( index == null ) {
+			return Collections.emptyList();
+		}
+		
+		final List<HL7Field> fields = new ArrayList<>();
+		for ( final HL7Segment segment : segments ) {
+			final List<HL7Field> segmentFields = segment.getFields();
+			if ( segmentFields.size() <= index ) {
+				continue;
+			}
+			
+			fields.add(segmentFields.get(index));
+		}
+		
+		return fields;
+	}
+
+	public Class<? extends List> getType() {
+		return List.class;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/MessageEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/MessageEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/MessageEvaluator.java
new file mode 100644
index 0000000..5e08961
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/MessageEvaluator.java
@@ -0,0 +1,34 @@
+/*
+ * 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.hl7.query.evaluator.message;
+
+import java.util.Map;
+
+import org.apache.nifi.hl7.model.HL7Message;
+import org.apache.nifi.hl7.query.evaluator.Evaluator;
+
+public class MessageEvaluator implements Evaluator<HL7Message> {
+
+	public HL7Message evaluate(final Map<String, Object> objectMap) {
+		return (HL7Message) objectMap.get(Evaluator.MESSAGE_KEY);
+	}
+
+	public Class<? extends HL7Message> getType() {
+		return HL7Message.class;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/SegmentEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/SegmentEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/SegmentEvaluator.java
new file mode 100644
index 0000000..1b9782d
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/message/SegmentEvaluator.java
@@ -0,0 +1,51 @@
+/*
+ * 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.hl7.query.evaluator.message;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.nifi.hl7.model.HL7Message;
+import org.apache.nifi.hl7.model.HL7Segment;
+import org.apache.nifi.hl7.query.evaluator.Evaluator;
+import org.apache.nifi.hl7.query.evaluator.StringEvaluator;
+
+@SuppressWarnings("rawtypes")
+public class SegmentEvaluator implements Evaluator<List> {
+	private final StringEvaluator segmentTypeEvaluator;
+	
+	public SegmentEvaluator(final StringEvaluator segmentTypeEvaluator) {
+		this.segmentTypeEvaluator = segmentTypeEvaluator;
+	}
+	
+	public List<HL7Segment> evaluate(final Map<String, Object> objectMap) {
+		final String segmentType = segmentTypeEvaluator.evaluate(objectMap);
+		if ( segmentType == null ) {
+			return Collections.emptyList();
+		}
+		
+		final HL7Message message = (HL7Message) objectMap.get(Evaluator.MESSAGE_KEY);
+		final List<HL7Segment> segments = message.getSegments(segmentType);
+		return (segments == null) ? Collections.<HL7Segment>emptyList() : segments;
+	}
+
+	public Class<? extends List> getType() {
+		return List.class;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/exception/HL7QueryParsingException.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/exception/HL7QueryParsingException.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/exception/HL7QueryParsingException.java
new file mode 100644
index 0000000..998f3bc
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/exception/HL7QueryParsingException.java
@@ -0,0 +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.hl7.query.exception;
+
+public class HL7QueryParsingException extends RuntimeException {
+	private static final long serialVersionUID = 1L;
+
+	public HL7QueryParsingException() {
+		super();
+	}
+	
+	public HL7QueryParsingException(final Throwable cause) {
+		super(cause);
+	}
+	
+	public HL7QueryParsingException(final String message) {
+		super(message);
+	}
+	
+	public HL7QueryParsingException(final String message, final Throwable cause) {
+		super(message, cause);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/result/MissedResult.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/result/MissedResult.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/result/MissedResult.java
new file mode 100644
index 0000000..a6b36c8
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/result/MissedResult.java
@@ -0,0 +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.hl7.query.result;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.nifi.hl7.query.QueryResult;
+import org.apache.nifi.hl7.query.ResultHit;
+import org.apache.nifi.hl7.query.Selection;
+
+public class MissedResult implements QueryResult {
+	private final List<Selection> selections;
+	
+	public MissedResult(final List<Selection> selections) {
+		this.selections = selections;
+	}
+	
+	@Override
+	public List<String> getLabels() {
+		final List<String> labels = new ArrayList<>();
+		for ( final Selection selection : selections ) {
+			labels.add(selection.getName());
+		}
+		return labels;
+	}
+
+	@Override
+	public boolean isMatch() {
+		return false;
+	}
+
+	@Override
+	public ResultHit nextHit() {
+		return null;
+	}
+	
+	@Override
+	public int getHitCount() {
+		return 0;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/result/StandardQueryResult.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/result/StandardQueryResult.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/result/StandardQueryResult.java
new file mode 100644
index 0000000..fbc16ca
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/result/StandardQueryResult.java
@@ -0,0 +1,69 @@
+/*
+ * 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.hl7.query.result;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.nifi.hl7.query.QueryResult;
+import org.apache.nifi.hl7.query.ResultHit;
+import org.apache.nifi.hl7.query.Selection;
+
+public class StandardQueryResult implements QueryResult {
+	private final List<Selection> selections;
+	private final Set<Map<String, Object>> hits;
+	private final Iterator<Map<String, Object>> hitIterator;
+	
+	public StandardQueryResult(final List<Selection> selections, final Set<Map<String, Object>> hits) {
+		this.selections = selections;
+		this.hits = hits;
+		
+		hitIterator = hits.iterator();
+	}
+	
+	@Override
+	public boolean isMatch() {
+		return !hits.isEmpty();
+	}
+
+	@Override
+	public List<String> getLabels() {
+		final List<String> labels = new ArrayList<>();
+		for ( final Selection selection : selections ) {
+			labels.add(selection.getName());
+		}
+		return labels;
+	}
+
+	@Override
+	public int getHitCount() {
+		return hits.size();
+	}
+	
+	@Override
+	public ResultHit nextHit() {
+		if ( hitIterator.hasNext() ) {
+			return new StandardResultHit(hitIterator.next());
+		} else {
+			return null;
+		}
+	}
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/result/StandardResultHit.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/result/StandardResultHit.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/result/StandardResultHit.java
new file mode 100644
index 0000000..944e998
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/result/StandardResultHit.java
@@ -0,0 +1,41 @@
+/*
+ * 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.hl7.query.result;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.nifi.hl7.query.ResultHit;
+
+public class StandardResultHit implements ResultHit {
+	private final Map<String, Object> values;
+	
+	public StandardResultHit(final Map<String, Object> values) {
+		this.values = values;
+	}
+	
+	@Override
+	public Object getValue(final String label) {
+		return values.get(label);
+	}
+
+	@Override
+	public Map<String, Object> getSelectedValues() {
+		return Collections.unmodifiableMap(values);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/test/java/org/apache/nifi/hl7/query/TestHL7Query.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/test/java/org/apache/nifi/hl7/query/TestHL7Query.java b/nifi/nifi-commons/nifi-hl7-query-language/src/test/java/org/apache/nifi/hl7/query/TestHL7Query.java
new file mode 100644
index 0000000..fbe4a8d
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/test/java/org/apache/nifi/hl7/query/TestHL7Query.java
@@ -0,0 +1,352 @@
+/*
+ * 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.hl7.query;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.nifi.hl7.hapi.HapiMessage;
+import org.apache.nifi.hl7.model.HL7Field;
+import org.apache.nifi.hl7.model.HL7Message;
+import org.apache.nifi.hl7.query.HL7Query;
+import org.apache.nifi.hl7.query.QueryResult;
+import org.junit.Test;
+
+import ca.uhn.hl7v2.DefaultHapiContext;
+import ca.uhn.hl7v2.HL7Exception;
+import ca.uhn.hl7v2.HapiContext;
+import ca.uhn.hl7v2.model.Message;
+import ca.uhn.hl7v2.parser.PipeParser;
+import ca.uhn.hl7v2.validation.impl.ValidationContextFactory;
+
+@SuppressWarnings("resource")
+public class TestHL7Query {
+
+	@Test
+	public void testAssignAliases() {
+		final LinkedHashMap<String, List<Object>> possibleValueMap = new LinkedHashMap<>();
+		
+		final List<Object> valuesA = new ArrayList<>();
+		valuesA.add("a");
+		valuesA.add("b");
+		valuesA.add("c");
+		
+		final List<Object> valuesB = new ArrayList<>();
+		valuesB.add("d");
+		
+		final List<Object> valuesC = new ArrayList<>();
+		valuesC.add("e");
+		valuesC.add("f");
+		
+		final List<Object> valuesD = new ArrayList<>();
+		valuesD.add("g");
+		valuesD.add("h");
+		
+		possibleValueMap.put("A", valuesA);
+		possibleValueMap.put("B", valuesB);
+		possibleValueMap.put("C", valuesC);
+		possibleValueMap.put("D", valuesD);
+		
+		for (int i=0; i < valuesA.size() * valuesB.size() * valuesC.size() * valuesD.size(); i++) {
+			System.out.println(i + " : " + HL7Query.assignAliases(possibleValueMap, i));
+		}
+		
+		verifyAssignments(HL7Query.assignAliases(possibleValueMap, 0), "a", "d", "e", "g");
+		verifyAssignments(HL7Query.assignAliases(possibleValueMap, 1), "b", "d", "e", "g");
+		verifyAssignments(HL7Query.assignAliases(possibleValueMap, 2), "c", "d", "e", "g");
+		verifyAssignments(HL7Query.assignAliases(possibleValueMap, 3), "a", "d", "f", "g");
+		verifyAssignments(HL7Query.assignAliases(possibleValueMap, 4), "b", "d", "f", "g");
+		verifyAssignments(HL7Query.assignAliases(possibleValueMap, 5), "c", "d", "f", "g");
+		verifyAssignments(HL7Query.assignAliases(possibleValueMap, 6), "a", "d", "e", "h");
+		verifyAssignments(HL7Query.assignAliases(possibleValueMap, 7), "b", "d", "e", "h");
+		verifyAssignments(HL7Query.assignAliases(possibleValueMap, 8), "c", "d", "e", "h");
+		verifyAssignments(HL7Query.assignAliases(possibleValueMap, 9), "a", "d", "f", "h");
+		verifyAssignments(HL7Query.assignAliases(possibleValueMap, 10), "b", "d", "f", "h");
+		verifyAssignments(HL7Query.assignAliases(possibleValueMap, 11), "c", "d", "f", "h");
+	}
+	
+	private void verifyAssignments(final Map<String, Object> map, final String a, final String b, final String c, final String d) {
+		assertEquals(a, map.get("A"));
+		assertEquals(b, map.get("B"));
+		assertEquals(c, map.get("C"));
+		assertEquals(d, map.get("D"));
+	}
+	
+	@Test
+	public void testSelectMessage() throws HL7Exception, IOException {
+		final HL7Query query = HL7Query.compile("SELECT MESSAGE");
+		final HL7Message msg = createMessage(new File("src/test/resources/vaers-message-long"));
+		final QueryResult result = query.evaluate(msg);
+		assertTrue(result.isMatch());
+		final List<String> labels = result.getLabels();
+		assertEquals(1, labels.size());
+		assertEquals("MESSAGE", labels.get(0));
+		
+		assertEquals(1, result.getHitCount());
+		assertEquals(msg, result.nextHit().getValue("MESSAGE"));
+	}
+	
+	@Test
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	public void testSelectField() throws HL7Exception, IOException {
+		final HL7Query query = HL7Query.compile("SELECT PID.5");
+		final HL7Message msg = createMessage(new File("src/test/resources/unsolicited-vaccine-update-short"));
+		final QueryResult result = query.evaluate(msg);
+		assertTrue(result.isMatch());
+		final List<String> labels = result.getLabels();
+		assertEquals(1, labels.size());
+		assertEquals(1, result.getHitCount());
+		
+		final Object names = result.nextHit().getValue("PID.5");
+		assertTrue(names instanceof List);
+		final List<Object> nameList = (List) names;
+		assertEquals(1, nameList.size());
+		final HL7Field nameField = (HL7Field) nameList.get(0);
+		assertEquals("KENNEDY^JOHN^FITZGERALD^JR", nameField.getValue());
+	}
+	
+	@Test
+	public void testSelectAbnormalTestResult() throws HL7Exception, IOException {
+		final String query = "DECLARE result AS REQUIRED OBX SELECT result WHERE result.7 != 'N' AND result.1 = 1";
+		
+		final HL7Query hl7Query = HL7Query.compile(query);
+		final QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/vaers-message-long")));
+		assertFalse( result.isMatch() );
+	}
+	
+	
+	@Test
+	public void testFieldEqualsString() throws HL7Exception, IOException {
+		HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.7 = 'L'");
+		QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertTrue( result.isMatch() );
+		
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.7 = 'H'");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertFalse( result.isMatch() );
+	}
+	
+	@Test
+	public void testLessThan() throws HL7Exception, IOException {
+		HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 < 600");
+		QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertTrue( result.isMatch() );
+		
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 < 59");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertFalse( result.isMatch() );
+	}
+	
+	@Test
+	public void testCompareTwoFields() throws HL7Exception, IOException {
+		HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 < result.6.2");
+		QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertTrue( result.isMatch() );
+		
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE NOT(result.4 > result.6.3)");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertFalse( result.isMatch() );
+	}
+	
+	@Test
+	public void testLessThanOrEqual() throws HL7Exception, IOException {
+		HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 <= 59");
+		QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertTrue( result.isMatch() );
+
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 <= 600");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertTrue( result.isMatch() );
+
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 <= 58");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertFalse( result.isMatch() );
+	}
+	
+	@Test
+	public void testGreaterThanOrEqual() throws HL7Exception, IOException {
+		HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 >= 59");
+		QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertTrue( result.isMatch() );
+
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 >= 6");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertTrue( result.isMatch() );
+
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 >= 580");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertFalse( result.isMatch() );
+	}
+	
+	@Test
+	public void testGreaterThan() throws HL7Exception, IOException {
+		HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 > 58");
+		QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertTrue( result.isMatch() );
+
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 > 6");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertTrue( result.isMatch() );
+
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 > 580");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertFalse( result.isMatch() );
+	}
+
+	
+	@Test
+	public void testDistinctValuesReturned() throws HL7Exception, IOException {
+		HL7Query hl7Query = HL7Query.compile("DECLARE result1 AS REQUIRED OBX, result2 AS REQUIRED OBX SELECT MESSAGE WHERE result1.7 = 'L' OR result2.7 != 'H'");
+		QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertTrue( result.isMatch() );
+		assertEquals(1, result.getHitCount());
+		
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT result WHERE result.1 = 1");
+		HL7Message msg = createMessage(new File("src/test/resources/vaers-message-long"));
+		result = hl7Query.evaluate(msg);
+		assertTrue( result.isMatch() );
+		assertEquals(9, result.getHitCount());
+		
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT result WHERE result.1 = 1 AND result.3.1.1 = '30961-7'");
+		result = hl7Query.evaluate(msg);
+		assertTrue( result.isMatch() );
+		assertEquals(1, result.getHitCount());
+
+	}
+	
+	@Test
+	public void testAndWithParens() throws HL7Exception, IOException {
+		HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.7 = 'L' AND result.3.1 = 'GLU'");
+		QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertTrue( result.isMatch() );
+		
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.7 = 'L' AND result.3.1 = 'GLU'");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hyperglycemia")));
+		assertFalse( result.isMatch() );
+		
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.7 = 'H' AND result.3.1 = 'GLU'");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertFalse( result.isMatch() );
+
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.7 = 'H' AND result.3.1 = 'GLU'");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hyperglycemia")));
+		assertTrue( result.isMatch() );
+
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE (result.7 = 'H') AND (result.3.1 = 'GLU')");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hyperglycemia")));
+		assertTrue( result.isMatch() );
+
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE ((result.7 = 'H') AND (result.3.1 = 'GLU'))");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hyperglycemia")));
+		assertTrue( result.isMatch() );
+
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE (( ((result.7 = 'H')) AND ( ((result.3.1 = 'GLU')) )))");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hyperglycemia")));
+		assertTrue( result.isMatch() );
+
+	}
+	
+	
+	@Test
+	public void testIsNull() throws HL7Exception, IOException {
+		HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.999 IS NULL");
+		QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertTrue( result.isMatch() );
+		
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.1 IS NULL");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertFalse( result.isMatch() );
+		
+		hl7Query = HL7Query.compile("SELECT MESSAGE WHERE ZZZ IS NULL");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertTrue( result.isMatch() );
+		
+		hl7Query = HL7Query.compile("SELECT MESSAGE WHERE OBX IS NULL");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertFalse( result.isMatch() );
+
+		hl7Query = HL7Query.compile("SELECT MESSAGE WHERE NK1.1 = '1' AND NK1.8 IS NULL");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/unsolicited-vaccine-update-long")));
+		assertTrue( result.isMatch() );
+	}
+	
+	
+	@Test
+	public void testNotNull() throws HL7Exception, IOException {
+		HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.999 NOT NULL");
+		QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertFalse( result.isMatch() );
+		
+		hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.1 NOT NULL");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertTrue( result.isMatch() );
+		
+		hl7Query = HL7Query.compile("SELECT MESSAGE WHERE ZZZ NOT NULL");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertFalse( result.isMatch() );
+		
+		hl7Query = HL7Query.compile("SELECT MESSAGE WHERE OBX NOT NULL");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
+		assertTrue( result.isMatch() );
+
+		hl7Query = HL7Query.compile("SELECT MESSAGE WHERE NK1.1 = '1' AND NK1.33 NOT NULL");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/unsolicited-vaccine-update-long")));
+		assertTrue( result.isMatch() );
+		
+		hl7Query = HL7Query.compile("SELECT MESSAGE WHERE NK1.1 = 1 AND NK1.33 NOT NULL");
+		result = hl7Query.evaluate(createMessage(new File("src/test/resources/unsolicited-vaccine-update-long")));
+		assertTrue( result.isMatch() );
+	}
+	
+	private HL7Message createMessage(final File file) throws HL7Exception, IOException {
+		final byte[] bytes = Files.readAllBytes(file.toPath());
+		final String msgText = new String(bytes, "UTF-8");
+		
+		final HapiContext hapiContext = new DefaultHapiContext();
+		hapiContext.setValidationContext(ValidationContextFactory.noValidation());
+		
+		final PipeParser parser = hapiContext.getPipeParser();
+		final Message message = parser.parse(msgText);
+		return new HapiMessage(message);
+	}
+	
+	@Test
+	@SuppressWarnings("unused")
+	public void createMessage() throws IOException, HL7Exception {
+		final byte[] bytes = Files.readAllBytes(Paths.get("src/test/resources/vaers-message-long"));
+		final String msgText = new String(bytes, "UTF-8");
+		
+		final HapiContext hapiContext = new DefaultHapiContext();
+		hapiContext.setValidationContext(ValidationContextFactory.noValidation());
+		
+		final PipeParser parser = hapiContext.getPipeParser();
+		final Message message = parser.parse(msgText);
+		
+		final HL7Message hl7Msg = new HapiMessage(message);
+	}
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/hyperglycemia
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/hyperglycemia b/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/hyperglycemia
new file mode 100644
index 0000000..dc44b89
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/hyperglycemia
@@ -0,0 +1,5 @@
+MSH|^~\&|CERNER||PriorityHealth||||ORU^R01|Q479004375T431430612|P|2.3|
+PID|||001677980||SMITH^CURTIS||19680219|M||||||||||929645156318|123456789|
+PD1||||1234567890^LAST^FIRST^M^^^^^NPI|
+OBR|1|341856649^HNAM_ORDERID|000002006326002362|648088^Basic Metabolic Panel|||20061122151600|||||||||1620^Hooker^Robert^L||||||20061122154733|||F|||||||||||20061122140000|
+OBX|1|NM|GLU^Glucose Lvl|159|mg/dL|65-99^65^99|H|||F|||20061122154733|
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/hypoglycemia
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/hypoglycemia b/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/hypoglycemia
new file mode 100644
index 0000000..02e8967
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/hypoglycemia
@@ -0,0 +1,5 @@
+MSH|^~\&|CERNER||PriorityHealth||||ORU^R01|Q479004375T431430612|P|2.3|
+PID|||001677980||SMITH^CURTIS||19680219|M||||||||||929645156318|123456789|
+PD1||||1234567890^LAST^FIRST^M^^^^^NPI|
+OBR|1|341856649^HNAM_ORDERID|000002006326002362|648088^Basic Metabolic Panel|||20061122151600|||||||||1620^Hooker^Robert^L||||||20061122154733|||F|||||||||||20061122140000|
+OBX|1|NM|GLU^Glucose Lvl|59|mg/dL|65-99^65^99|L|||F|||20061122154733|
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/metabolic-panel
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/metabolic-panel b/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/metabolic-panel
new file mode 100644
index 0000000..c62fc45
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/metabolic-panel
@@ -0,0 +1,23 @@
+MSH|^~\&|Lab1^1234^CLIA|^1234^CLIA|ELR^2.16.840.1.113883.19.3.2^ISO|SPH^2.16.840.1.113883.19.3.2^ISO|20110410140502-0500||ORU^R01^ORU_R01|1234567890|P^T|2.5.1|||NE|NE|USA||||USELR1.0^^2.16.840.1.114222.4.10.3^ISO 
+SFT|1|Level Seven Healthcare Software, Inc.^L^^^^&2.16.840.1.113883.19.4.6^ISO^XX^^^1234|1.2|An Lab System|56734||20080817 
+PID|1||36363636^^^MPI&2.16.840.1.113883.19.3.2.1&ISO^MR^A&2.16.840.1.113883.19.3.2.1&ISO~444333333^^^&2.16.840.1.113883.4.1^IS O^SS||Everyman^Adam^A^^^^L^^^^^^^BS|Mum^Martha^M^^^^M|19800602|M||2106-3^White^CDCREC^^^^04/24/2007|2222 Home Street^^Ann Arbor^MI^99999^USA^H||^PRN^PH^^1^555^5552004|^WPN^PH^^1^955^5551009|eng^English^ISO6392^^^^3/29/2007|M^Married^HL70002^^^^2.5.1||||||N^Not Hispanic or Latino^HL70189^^^^2.5.1||||||||N|||200808151000-0700| Reliable^2.16.840.1.113883.19.3.1^ISO 
+ORC|RE|23456^EHR^2.16.840.1.113883.19.3.2.3^ISO|9700123^Lab^2.16.840.1.113883.19.3.1.6^ISO|||||||||1234^Admit^Alan^A^III^Dr^^^&2.16.840.1.113883.19.4.6^ISO^L^^^EI^&2.16.840.1.113883.19.4.6^ISO^^^^^^^^MD||^WPN^PH^^1^555^5551005|||||||Level Seven Healthcare, Inc.^L^^^^&2.16.840.1.113883.19.4.6^ISO^XX^^^1234|1005 Healthcare Drive^^Ann Arbor^MI^99999^USA^B|^WPN^PH^^1^555^5553001|4444 Healthcare Drive^Suite 123^Ann Arbor^MI^99999^USA^B 
+OBR|1|23456^EHR^2.16.840.1.113883.19.3.2.3^ISO|9700123^Lab^2.16.840.1.113883.19.3.1.6^ISO|24323-8^Comprehensive metabolic 2000 panel in Serum or Plasma^LN^3436442^Metaboloic Panel 2000, Comprehensive^99USI|||201104101130-0500||||||angina|||1234^Admit^Alan^A^III^Dr^^^&2.16.840.1.113883.19.4.6^ISO^L^^^EI^&2.16.840.1.113883.19.4.6^ISO^^^^^^^^MD|^WPN^PH^^1^555^5551005|||||201104101405-0500|||F||||||413^Angina pectoris^I9CDX^^^^07/09/2008|1235&Slide&Stan&S&&Dr&MD&&DOC&2.16.840.1.113883.19.4.6&ISO 
+OBX|1|NM|17861-6^Calcium [Mass/volume] in Serum or Plasma^LN||27.3|mg/dL^milligrams per deciliter^UCUM|8.7-10.7|HH|||F|||201104101130-0500|||||201104101325-0500||||GHH Lab^L^^^^CLIA&2.16.840.1.113883.19.4.6&ISO^XX^^^1236|3434 Industrial Loop^^Ann Arbor^MI^99999^USA^B|9876543^Slide^Stan^S^^^^^NPPES&2.16.840.1.113883.19.4.6&ISO^L^^^NPI 
+OBX|2|NM|3094-0^Urea nitrogen [Mass/volume] in Serum of Plasma^LN||15|mg/dL^milligrams per deciliter^UCUM|6 to 23|N|||F|||201104101130-0500|||||201104101325-0500||||GHH Lab^L^^^^CLIA&2.16.840.1.113883.19.4.6&ISO^XX^^^1236|3434 Industrial Loop^^Ann Arbor^MI^99999^USA^B|9876543^Slide^Stan^S^^^^^NPPES&2.16.840.1.113883.19.4.6&ISO^L^^^NPI 
+OBX|3|NM|2160-0^Creatinine [Mass/volume] in Serum or Plasma^LN||1.8|mg/dL^milligrams per deciliter^UCUM|0.7 to 1.2|H|||F|||201104101130-0500|||||201104101325-0500||||GHH Lab^L^^^^CLIA&2.16.840.1.113883.19.4.6&ISO^XX^^^1236|3434 Industrial Loop^^Ann Arbor^MI^99999^USA^B|9876543^Slide^Stan^S^^^^^NPPES&2.16.840.1.113883.19.4.6&ISO^L^^^NPI 
+OBX|4|NM|3097-3^Urea nitrogen/Creatinine [Mass ratio] in Serum or Plasma^LN||15||6 to 25|N|||F|||201104101130-0500|||||201104101325-0500||||GHH Lab^L^^^^CLIA&2.16.840.1.113883.19.4.6&ISO^XX^^^1236|3434 Industrial Loop^^Ann Arbor^MI^99999^USA^B|9876543^Slide^Stan^S^^^^^NPPES&2.16.840.1.113883.19.4.6&ISO^L^^^NPI
+OBX|5|NM|2885-2^Protein [Mass/volume] in Serum or Plasma^LN||8.9|gm/dL^grams per deciliter^UCUM|6.3 to 8.2|H|||F|||201104101130-0500|||||201104101325-0500||||GHH Lab^L^^^^CLIA&2.16.840.1.113883.19.4.6&ISO^XX^^^1236|3434 Industrial Loop^^Ann Arbor^MI^99999^USA^B|9876543^Slide^Stan^S^^^^^NPPES&2.16.840.1.113883.19.4.6&ISO^L^^^NPI 
+OBX|6|NM|1751-7^Albumin [Mass/volume] in Serum or Plasma^LN||5.7|gm/dL^grams per deciliter^UCUM|3.5 to 5.0|H|||F|||201104101130-0500|||||201104101325-0500||||GHH Lab^L^^^^CLIA&2.16.840.1.113883.19.4.6&ISO^XX^^^1236|3434 Industrial Loop^^Ann Arbor^MI^99999^USA^B|9876543^Slide^Stan^S^^^^^NPPES&2.16.840.1.113883.19.4.6&ISO^L^^^NPI 
+OBX|7|NM|2336-6^Globulin [Mass/volume] in Serum or Plasma^LN||4.7|gm/dL^grams per deciliter^UCUM|2.2 to 4.2|H|||F|||201104101130-0500|||||201104101325-0500||||GHH Lab^L^^^^CLIA&2.16.840.1.113883.19.4.6&ISO^XX^^^1236|3434 Industrial Loop^^Ann Arbor^MI^99999^USA^B|9876543^Slide^Stan^S^^^^^NPPES&2.16.840.1.113883.19.4.6&ISO^L^^^NPI 
+OBX|8|NM|1759-0^Albumin/Globulin [Mass ratio] in Serum or Plasma^LN||1.7||0.8 to 2.0|N|||F|||201104101130-0500|||||201104101325-0500||||GHH Lab^L^^^^CLIA&2.16.840.1.113883.19.4.6&ISO^XX^^^1236|3434 Industrial Loop^^Ann Arbor^MI^99999^USA^B|9876543^Slide^Stan^S^^^^^NPPES&2.16.840.1.113883.19.4.6&ISO^L^^^NPI
+OBX|9|NM|1975-2^Bilirubin.total [Mass/volume] in Serum or Plasma^LN||0.7|mg/dL^milligrams per deciliter^UCUM|0.3 to 1.9|N|||F|||201104101130-0500|||||201104101325-0500||||GHH Lab^L^^^^CLIA&2.16.840.1.113883.19.4.6&ISO^XX^^^1236|3434 Industrial Loop^^Ann Arbor^MI^99999^USA^B|9876543^Slide^Stan^S^^^^^NPPES&2.16.840.1.113883.19.4.6&ISO^L^^^NPI 
+OBX|10|NM|2345-7^Glucose [Mass/volume] in Serum or Plasma^LN||55|mg/dL^milligrams per deciliter^UCUM|60 to 109|L|||F|||201104101130-0500|||||201104101325-0500||||GHH Lab^L^^^^CLIA&2.16.840.1.113883.19.4.6&ISO^XX^^^1236|3434 Industrial Loop^^Ann Arbor^MI^99999^USA^B|9876543^Slide^Stan^S^^^^^NPPES&2.16.840.1.113883.19.4.6&ISO^L^^^NPI 
+OBX|11|NM|6768-6^Alkaline phosphatase [Enzymatic activity/volume] in Serum or Plasma^LN||64|U/L^units per liter^UCUM|32 to 110|N|||F|||201104101130-0500|||||201104101325-0500||||GHH Lab^L^^^^CLIA&2.16.840.1.113883.19.4.6&ISO^XX^^^1236|3434 Industrial Loop^^Ann Arbor^MI^99999^USA^B|9876543^Slide^Stan^S^^^^^NPPES&2.16.840.1.113883.19.4.6&ISO^L^^^NPI 
+OBX|12|NM|1920-8^Aspartate aminotransferase [Enzymatic activity/volume] in Serum or Plasma^LN||6|U/L^units per liter^UCUM|6 to 18|N|||F|||201104101130-0500|||||201104101325-0500||||GHH Lab^L^^^^CLIA&2.16.840.1.113883.19.4.6&ISO^XX^^^1236|3434 Industrial Loop^^Ann Arbor^MI^99999^USA^B|9876543^Slide^Stan^S^^^^^NPPES&2.16.840.1.113883.19.4.6&ISO^L^^^NPI 
+OBX|13|NM|1742-6^Alanine aminotransferase [Enzymatic activity/volume] in Serum or Plasma^LN||10|U/L^units per liter^UCUM|5 to 35|N|||F|||201104101130-0500|||||201104101325-0500||||GHH Lab^L^^^^CLIA&2.16.840.1.113883.19.4.6&ISO^XX^^^1236|3434 Industrial Loop^^Ann Arbor^MI^99999^USA^B|9876543^Slide^Stan^S^^^^^NPPES&2.16.840.1.113883.19.4.6&ISO^L^^^NPI 
+OBX|14|NM|2951-2^Sodium [Moles/volume] in Serum or Plasma^LN||140|mmol/L^millimoles per liter^UCUM|137 to 147|N|||F|||201104101130-0500|||||201104101325-0500||||GHH Lab^L^^^^CLIA&2.16.840.1.113883.19.4.6&ISO^XX^^^1236|3434 Industrial Loop^^Ann Arbor^MI^99999^USA^B|9876543^Slide^Stan^S^^^^^NPPES&2.16.840.1.113883.19.4.6&ISO^L^^^NPI 
+OBX|15|NM|2823-3^Potassium [Moles/volume] in Serum or Plasma^LN||4.5|mmol/L^millimoles per liter^UCUM|3.4 to 5.3|N|||F|||201104101130-0500|||||201104101325-0500||||GHH Lab^L^^^^CLIA&2.16.840.1.113883.19.4.6&ISO^XX^^^1236|3434 Industrial Loop^^Ann Arbor^MI^99999^USA^B|9876543^Slide^Stan^S^^^^^NPPES&2.16.840.1.113883.19.4.6&ISO^L^^^NPI 
+OBX|16|NM|2075-0^Chloride [Moles/volume] in Serum or Plasma^LN||99|mmol/L^millimoles per liter^UCUM|99 to 108|N|||F|||201104101130-0500|||||201104101325-0500||||GHH Lab^L^^^^CLIA&2.16.840.1.113883.19.4.6&ISO^XX^^^1236|3434 Industrial Loop^^Ann Arbor^MI^99999^USA^B|9876543^Slide^Stan^S^^^^^NPPES&2.16.840.1.113883.19.4.6&ISO^L^^^NPI 
+OBX|17|NM|2028-9^Carbon dioxide, total [Moles/volume] in Serum or Plasma^LN||27|mmol/L^millimoles per liter^UCUM|22 to 29|N|||F|||201104101130-0500|||||201104101325-0500||||GHH Lab^L^^^^CLIA&2.16.840.1.113883.19.4.6&ISO^XX^^^1236|3434 Industrial Loop^^Ann Arbor^MI^99999^USA^B|9876543^Slide^Stan^S^^^^^NPPES&2.16.840.1.113883.19.4.6&ISO^L^^^NPI 
+SPM|1|23456&EHR&2.16.840.1.113883.19.3.2.3&ISO^9700122&Lab&2.16.840.1.113883.19.3.1.6&ISO||119364003^Serum specimen^SCT^^^^20080131|||||||||||||201104101130-0500|201104101130-0500

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/unsolicited-vaccine-update-long
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/unsolicited-vaccine-update-long b/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/unsolicited-vaccine-update-long
new file mode 100644
index 0000000..8edd3fd
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/unsolicited-vaccine-update-long
@@ -0,0 +1,16 @@
+MSH|^~\&||MA0000||GA0000|19970901||VXU^V04|19970522MA53|T|2.3.1|||AL
+PID|||1234^^^^SR^~1234-12^^^^LR^~3872^^^^MR~221345671^^^^SS^~430078856^^^^MA^ ||KENNEDY^JOHN^FITZGERALD^JR^^^L|BOUVIER^^^^^^M|19900607|M|KENNEDY^BABY BOY^^^^^^ B|W^WHITE^NY8 RACE CODES^W^WHITE^HL70005|123 MAIN ST^APT 3B^LEXINGTON^MA^00210^ ^M^MSA CODE^MA034~345 ELM ST^^BOSTON^MA^00314^^BLD~^^^^^^BR^^MA002| |(617) 555-1212 ^PRN^PH^^^617^5551212^^||EN^ENGLISH^HL70296^^^|||||||WN^NOT HISPANIC^LOCAL CODE SET^NH^NOT OF HISPANIC ORIGIN^HL70189|CHILDREN=S HOSPITAL
+PD1|||CHILDREN=S HOSPITAL^^1234^^^^XX~LEXINGTON CLINIC^^1234A^^^^FI|12345^CARE^ PRIMARY^^^DR^MD^^^L^^^DN|||||||03^REMINDER/RECALL - NO CALLS^HL70215|Y
+NK1|1|KENNEDY^JACQUELINE^LEE|32^MOTHER^HL70063||||||||||||||||||||||||||||||898666725^^^^SS
+NK1|2|KENNEDY^JOHN^FITZGERALD|33^FATHER^HL70063||||||||||||||||||||||||||||||822546618^^^^SS
+PV1||R|||||||||||||||A|||V02^19900607~H02^19900607
+RXA|0|1|19900607|19900607|08^HEPB-PEDIATRIC/ADOLESCENT^CVX^90744^HEPB-PEDATRIC/ADOLESCENT^CPT|.5|ML^^ISO+||03^HISTORICAL INFORMATION - FROM PARENT=S WRITTEN RECORD^NIP0001|^JONES^LISA|^^^CHILDREN=S HOSPITAL||5|MCG^^ISO+|MRK12345| 199206|MSD^MERCK^MVX
+RXA|0|4|19910907|19910907|50^DTAP-HIB^CVX^90721^DTAP-HIB^CPT|.5|ML^^ISO+||00^NEW IMMUNIZATION RECORD^NIP0001|1234567890^SMITH^SALLY^S^^^^^^^^^VEI~1234567891 ^O=BRIAN^ROBERT^A^^DR^MD^^^^^^OEI|^^^CHILD HEALTHCARE CLINIC^^^^^101 MAIN STREET^^ BOSTON^MA||||W46932777|199208|PMC^PASTEUR MERIEUX CONNAUGHT^MVX|||CP|A| 19910907120030
+RXR|IM^INTRAMUSCULAR^HL70162|LA^LEFT ARM^HL70163
+RXA|0|1|19910907|19910907|03^MMR^CVX|.5|ML^^ISO+|||1234567890^SMITH^SALLY^S^^^^^^^^^VEI~1234567891^O=BRIAN^ROBERT^A^^DR^MD^^^^^^OEI|^^^CHILD HEALTHCARE CLINIC^^^^^101 MAIN STREET^^BOSTON^MA||||W2348796456|19920731|MSD^MERCK^MVX
+RXR|SC^SUBCUTANEOUS^HL70162|LA^LEFT ARM^HL70163
+RXA|0|5|19950520|19950520|20^DTAP^CVX|.5|ML^^ISO+|||1234567891^O=BRIAN^ROBERT^A^^DR|^^^CHILD HEALTHCARE CLINIC^^^^^101 MAIN STREET^^BOSTON^MA||||W22532806|19950705|PMC^ PASTEUR MERIEUX CONNAUGHT^MVX
+RXR|IM^INTRAMUSCULAR^HL70162|LA^LEFT ARM^HL70163
+NTE|PATIENT DEVELOPED HIGH FEVER APPROX 3 HRS AFTER VACCINE INJECTION
+RXA|0|2|19950520|19950520|03^MMR^CVX|.5|ML^^ISO+|||1234567891^O=BRIAN^ROBERT^A^^DR|^^^CHILD HEALTHCARE CLINIC^^^^^101 MAIN STREET^^BOSTON^MA||||W2341234567|19950630| MSD^MERCK^MVX
+RXR|SC^SUBCUTANEOUS^HL70162|LA^LEFT ARM^HL70163

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/unsolicited-vaccine-update-short
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/unsolicited-vaccine-update-short b/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/unsolicited-vaccine-update-short
new file mode 100644
index 0000000..c3e7cf0
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/unsolicited-vaccine-update-short
@@ -0,0 +1,4 @@
+MSH|^~\&|||||||VXU^V04|19970522MA53|P|2.3.1
+PID|||221345671^^^^SS||KENNEDY^JOHN^FITZGERALD^JR|BOUVIER^^^^^^M|19900607|M|||^^^^MA^^^BLD
+NK1|1|KENNEDY^JACQUELINE^LEE|32^MOTHER^HL70063
+RXA|0|1|19900607|19900607|08^HEPB-PEDIATRIC/ADOLESCENT^CVX|.5|ML^^ISO+||||||||MRK12345||MSD^MERCK^MVX

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/vaccine-query
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/vaccine-query b/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/vaccine-query
new file mode 100644
index 0000000..1bd47b2
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/vaccine-query
@@ -0,0 +1,3 @@
+MSH|^~\&||GA0000||MA0000|199705221605||VXQ^V01|19970522GA40|T|2.3.1|||AL
+QRD|199705221605|R|I|19970522GA05|||25^RD|^KENNEDY^JOHN^FITZGERALD^JR|VXI^VACCINE INFORMATION^HL70048|^SIIS
+QRF|MA0000||||256946789~19900607~MA~MA99999999~88888888~KENNEDY^JACQUELINE^LEE~BOUVIER~898666725~KENNEDY^JOHN^FITZGERALD~822546618

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/vaers-message-long
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/vaers-message-long b/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/vaers-message-long
new file mode 100644
index 0000000..888b04c
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/test/resources/vaers-message-long
@@ -0,0 +1,60 @@
+MSH|^~\&||GA0000||VAERS PROCESSOR|20010331605||ORU^R01|20010422GA03|T|2.3.1|||AL|
+PID|||1234^^^^SR~1234-12^^^^LR~00725^^^^MR||Doe^John^Fitzgerald^JR^^^L||20001007|M||2106-3^White^HL70005|123 Peachtree St^APT 3B^Atlanta^GA^30210^^M^^GA067||(678) 555-1212^^PRN|
+NK1|1|Jones^Jane^Lee^^RN|VAB^Vaccine administered by (Name)^HL70063|
+NK1|2|Jones^Jane^Lee^^RN|FVP^Form completed by (Name)-Vaccine provider^HL70063|101 Main Street^^Atlanta^GA^38765^^O^^GA121||(404) 554-9097^^WPN|
+ORC|CN|||||||||||1234567^Welby^Marcus^J^Jr^Dr.^MD^L|||||||||Peachtree Clinic|101 Main Street^^Atlanta^GA^38765^^O^^GA121|(404) 554-9097^^WPN|101 Main Street^^Atlanta^GA^38765^^O^^GA121|
+OBR|1|||^CDC VAERS-1 (FDA) Report|||20010316|
+OBX|1|NM|21612-7^Reported Patient Age^LN||05|mo^month^ANSI|
+OBX|1|TS|30947-6^Date form completed^LN||20010316|
+OBX|2|FT|30948-4^Vaccination adverse events and treatment, if any^LN|1|fever of 106F, with vomiting, seizures, persistent crying lasting over 3 hours, loss of appetite|
+OBX|3|CE|30949-2^Vaccination adverse event outcome^LN|1|E^required emergency room/doctor visit^NIP005|
+OBX|4|CE|30949-2^Vaccination adverse event outcome^LN|1|H^required hospitalization^NIP005|
+OBX|5|NM|30950-0^Number of days hospitalized due to vaccination adverse event^LN|1|02|d^day^ANSI|
+OBX|6|CE|30951-8^Patient recovered^LN||Y^Yes^ HL70239|
+OBX|7|TS|30952-6^Date of vaccination^LN||20010216|
+OBX|8|TS|30953-4^Adverse event onset date and time^LN||200102180900|
+OBX|9|FT|30954-2^Relevant diagnostic tests/lab data^LN||Electrolytes, CBC, Blood culture|
+OBR|2|||30955-9^All vaccines given on date listed in #10^LN|
+OBX|1|CE30955-9&30956-7^Vaccine type^LN|1|08^HepB-Adolescent/pediatric^CVX|
+OBX|2|CE|30955-9&30957-5^Manufacturer^LN|1|MSD^Merck^MVX|
+OBX|3|ST|30955-9&30959-1^Lot number^LN|1|MRK12345|
+OBX|4|CE|30955-9&30958-3^ Route^LN|1|IM^Intramuscular ^HL70162|
+OBX|5|CE|30955-9&31034-2^Site^LN|1|LA^Left arm^ HL70163|
+OBX|6|NM|30955-9&30960-9^Number of previous doses^LN|1|01I
+OBX|7|CE|CE|30955-9&30956-7^Vaccine type^LN|2|50^DTaP-Hib^CVX|
+OBX|8|CE|30955-9&30957-5^ Manufacturer^LN|2|WAL^Wyeth_Ayerst^MVX|
+OBX|9|ST|30955-9&30959-1^Lot number^LN|2|W46932777|
+OBX|10|CE|30955-9&30958-3^ Route^LN|2|IM^Intramuscular^HL70162|
+OBX|11|CE|30955-9&31034-2^Site^LN|2|LA^Left arm^HL70163|
+OBX|12|NM|30955-9&30960-9^Number of previous doses^LN|2|01|
+OBR|3|||30961-7^Any other vaccinations within 4 weeks prior to the date listed in #10|
+OBX|1|CE|30961-7&30956-7^Vaccine type^LN|1|10^IPV^CVX|
+OBX|2|CE|30961-7&30957-5^Manufacturer^LN|1|PMC^Aventis Pasteur ^MVX|
+OBX|3|ST|30961-7&30959-1^Lot number^LN|1|PMC123456|
+OBX|4|CE|30961-7&30958-3^Route^LN|1|SC^Subcutaneaous^HL70162|
+OBX|5|CE|30961-7&31034-2^Site^LN|1|LA^Left arm^HL70163|
+OBX|6|NM|30961-7&30960-9^Number of previous doses^LN|1|01|
+OBX|7|TS|30961-7&31035-9^date given^LN|1|20001216|
+OBX|8|CE|30962-^Vaccinated at^LN||PVT^Private doctor�s office/hospital^NIP009|
+OBX|9|CE|30963-3^Vaccine purchased with^LN||PBF^Public funds^NIP008|
+OBX|10|FT|30964-1^Other medications^LN||None|
+OBX|11|FT|30965-8^Illness at time of vaccination (specify)^LN||None|
+OBX|12|FT|30966-6^Pre-existing physician diagnosed allergies, birth defects, medical conditions^LN||Past conditions convulsions|
+OBX|13|CE|30967-4^Was adverse event reported previously^LN||N^no^NIP009|
+OBR|4||30968-2^Adverse event following prior vaccination in patient^LN|
+OBX|1|TX|30968-2&30971-6^Adverse event^LN||None|
+OBR|5||30969-0^Adverse event following prior vaccination in brother^LN|
+OBX|1|TX||30969-0&30971-6^Adverse event^LN||vomiting, fever, otitis media|
+OBX|2|NM||30969-0&30972-4^Onset age^LN||04|mo^month^ANSI|
+OBX|3|CE||30969-0&30956-7^Vaccine Type ^LN||10^IPV^CVX|
+OBX|4|NM||30969-0&30973-2^Dose number in series^LN||02|
+OBR|6|||30970-8^Adverse event following prior vaccination in sister^LN|
+OBX|1|TX|30970-8&30971-6^Adverse event^LN||None|
+OBR|7||^For children 5 and under|
+OBX|1|NM|8339-4^Body weight at birth^LN||82|oz^ounces^ANSI|
+OBX|2|NM|30974-0^Number of brothers and sisters^LN||2|
+OBR|8|||^Only for reports submitted by manufacturer/immunization project|
+OBX|1|ST|30975-7^Mfr./Imm. Proj. report no.^LN||12345678|
+OBX|2|TS|30976-5^Date received by manufacturer/immunization project^LN||12345678|
+OBX|3|CE|30977-3^15 day report^LN||N^No^HL70136|
+OBX|4|CE|30978-1^Report type^LN||IN^Initial^NIP010|

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-nar/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-nar/pom.xml b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-nar/pom.xml
new file mode 100644
index 0000000..391206e
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-nar/pom.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-hl7-bundle</artifactId>
+        <version>0.1.0-incubating-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>nifi-hl7-nar</artifactId>
+    <packaging>nar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-hl7-processors</artifactId>
+            <version>0.1.0-incubating-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/.gitignore
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/.gitignore b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/.gitignore
@@ -0,0 +1 @@
+/target/

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/pom.xml b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/pom.xml
new file mode 100644
index 0000000..2a0c909
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/pom.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-hl7-bundle</artifactId>
+        <version>0.1.0-incubating-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>nifi-hl7-processors</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-processor-utils</artifactId>
+        </dependency>
+        
+        <dependency>
+        	<groupId>org.apache.nifi</groupId>
+        	<artifactId>nifi-hl7-query-language</artifactId>
+        	<version>0.1.0-incubating-SNAPSHOT</version>
+        </dependency>
+        
+		<dependency>
+			<groupId>ca.uhn.hapi</groupId>
+			<artifactId>hapi-base</artifactId>
+			<version>2.2</version>
+		</dependency>       
+        <dependency>
+		  <groupId>ca.uhn.hapi</groupId>
+		  <artifactId>hapi-structures-v21</artifactId>
+		  <version>2.2</version>
+		</dependency>
+		<dependency>
+		  <groupId>ca.uhn.hapi</groupId>
+		  <artifactId>hapi-structures-v22</artifactId>
+		  <version>2.2</version>
+		</dependency>
+        <dependency>
+		  <groupId>ca.uhn.hapi</groupId>
+		  <artifactId>hapi-structures-v23</artifactId>
+		  <version>2.2</version>
+		</dependency>
+		<dependency>
+		  <groupId>ca.uhn.hapi</groupId>
+		  <artifactId>hapi-structures-v231</artifactId>
+		  <version>2.2</version>
+		</dependency>
+        <dependency>
+		  <groupId>ca.uhn.hapi</groupId>
+		  <artifactId>hapi-structures-v24</artifactId>
+		  <version>2.2</version>
+		</dependency>
+		<dependency>
+		  <groupId>ca.uhn.hapi</groupId>
+		  <artifactId>hapi-structures-v25</artifactId>
+		  <version>2.2</version>
+		</dependency>
+		<dependency>
+		  <groupId>ca.uhn.hapi</groupId>
+		  <artifactId>hapi-structures-v251</artifactId>
+		  <version>2.2</version>
+		</dependency>
+        <dependency>
+		  <groupId>ca.uhn.hapi</groupId>
+		  <artifactId>hapi-structures-v26</artifactId>
+		  <version>2.2</version>
+		</dependency>
+        
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-mock</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>


[26/62] [abbrv] incubator-nifi git commit: NIFI-478: Fixed bug that caused byte sequence to be dropped for last split under certain circumstances; added new unit tests

Posted by ma...@apache.org.
NIFI-478: Fixed bug that caused byte sequence to be dropped for last split under certain circumstances; added new unit tests

Signed-off-by: joewitt <jo...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/4752e284
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/4752e284
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/4752e284

Branch: refs/heads/NIFI-25
Commit: 4752e284f6b4c4d3ae082af6b6f66d393c2a1ad3
Parents: e964771
Author: Mark Payne <ma...@hotmail.com>
Authored: Tue Mar 31 08:34:23 2015 -0400
Committer: joewitt <jo...@apache.org>
Committed: Tue Mar 31 23:03:25 2015 -0400

----------------------------------------------------------------------
 .../nifi/processors/standard/SplitContent.java  |  93 +++++++++++---
 .../processors/standard/TestSplitContent.java   | 123 +++++++++++++++++++
 2 files changed, 202 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4752e284/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/SplitContent.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/SplitContent.java b/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/SplitContent.java
index 9838af7..419e12d 100644
--- a/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/SplitContent.java
+++ b/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/SplitContent.java
@@ -18,7 +18,9 @@ package org.apache.nifi.processors.standard;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -28,6 +30,7 @@ import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.apache.commons.codec.DecoderException;
 import org.apache.commons.codec.binary.Hex;
 import org.apache.nifi.annotation.behavior.EventDriven;
 import org.apache.nifi.annotation.behavior.SideEffectFree;
@@ -35,8 +38,10 @@ import org.apache.nifi.annotation.behavior.SupportsBatching;
 import org.apache.nifi.annotation.documentation.CapabilityDescription;
 import org.apache.nifi.annotation.documentation.SeeAlso;
 import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
 import org.apache.nifi.annotation.behavior.WritesAttribute;
 import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.components.AllowableValue;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.ValidationContext;
 import org.apache.nifi.components.ValidationResult;
@@ -50,6 +55,7 @@ import org.apache.nifi.processor.ProcessSession;
 import org.apache.nifi.processor.ProcessorInitializationContext;
 import org.apache.nifi.processor.Relationship;
 import org.apache.nifi.processor.io.InputStreamCallback;
+import org.apache.nifi.processor.util.StandardValidators;
 import org.apache.nifi.stream.io.BufferedInputStream;
 import org.apache.nifi.util.NaiveSearchRingBuffer;
 import org.apache.nifi.util.Tuple;
@@ -72,19 +78,39 @@ public class SplitContent extends AbstractProcessor {
     public static final String FRAGMENT_COUNT = "fragment.count";
     public static final String SEGMENT_ORIGINAL_FILENAME = "segment.original.filename";
 
+    static final AllowableValue HEX_FORMAT = new AllowableValue("Hexadecimal", "Hexadecimal", "The Byte Sequence will be interpreted as a hexadecimal representation of bytes");
+    static final AllowableValue UTF8_FORMAT = new AllowableValue("Text", "Text", "The Byte Sequence will be interpreted as UTF-8 Encoded text");
+    
+    static final AllowableValue TRAILING_POSITION = new AllowableValue("Trailing", "Trailing", "Keep the Byte Sequence at the end of the first split if <Keep Byte Sequence> is true");
+    static final AllowableValue LEADING_POSITION = new AllowableValue("Leading", "Leading", "Keep the Byte Sequence at the beginning of the second split if <Keep Byte Sequence> is true");
+    
+    public static final PropertyDescriptor FORMAT = new PropertyDescriptor.Builder()
+            .name("Byte Sequence Format")
+            .description("Specifies how the <Byte Sequence> property should be interpreted")
+            .required(true)
+            .allowableValues(HEX_FORMAT, UTF8_FORMAT)
+            .defaultValue(HEX_FORMAT.getValue())
+            .build();
     public static final PropertyDescriptor BYTE_SEQUENCE = new PropertyDescriptor.Builder()
             .name("Byte Sequence")
-            .description("A hex representation of bytes to look for and upon which to split the source file into separate files")
-            .addValidator(new HexStringPropertyValidator())
+            .description("A representation of bytes to look for and upon which to split the source file into separate files")
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
             .required(true)
             .build();
     public static final PropertyDescriptor KEEP_SEQUENCE = new PropertyDescriptor.Builder()
             .name("Keep Byte Sequence")
-            .description("Determines whether or not the Byte Sequence should be included at the end of each Split")
+            .description("Determines whether or not the Byte Sequence should be included with each Split")
             .required(true)
             .allowableValues("true", "false")
             .defaultValue("false")
             .build();
+    public static final PropertyDescriptor BYTE_SEQUENCE_LOCATION = new PropertyDescriptor.Builder()
+            .name("Byte Sequence Location")
+            .description("If <Keep Byte Sequence> is set to true, specifies whether the byte sequence should be added to the end of the first split or the beginning of the second; if <Keep Byte Sequence> is false, this property is ignored.")
+            .required(true)
+            .allowableValues(TRAILING_POSITION, LEADING_POSITION)
+            .defaultValue(TRAILING_POSITION.getValue())
+            .build();
 
     public static final Relationship REL_SPLITS = new Relationship.Builder()
             .name("splits")
@@ -108,8 +134,10 @@ public class SplitContent extends AbstractProcessor {
         this.relationships = Collections.unmodifiableSet(relationships);
 
         final List<PropertyDescriptor> properties = new ArrayList<>();
+        properties.add(FORMAT);
         properties.add(BYTE_SEQUENCE);
         properties.add(KEEP_SEQUENCE);
+        properties.add(BYTE_SEQUENCE_LOCATION);
         this.properties = Collections.unmodifiableList(properties);
     }
 
@@ -124,13 +152,27 @@ public class SplitContent extends AbstractProcessor {
     }
 
     @Override
-    public void onPropertyModified(final PropertyDescriptor descriptor, final String oldValue, final String newValue) {
-        if (descriptor.equals(BYTE_SEQUENCE)) {
-            try {
-                this.byteSequence.set(Hex.decodeHex(newValue.toCharArray()));
-            } catch (final Exception e) {
-                this.byteSequence.set(null);
-            }
+    protected Collection<ValidationResult> customValidate(final ValidationContext validationContext) {
+        final List<ValidationResult> results = new ArrayList<>(1);
+        final String format = validationContext.getProperty(FORMAT).getValue();
+        if ( HEX_FORMAT.getValue().equals(format) ) {
+            final String byteSequence = validationContext.getProperty(BYTE_SEQUENCE).getValue();
+            final ValidationResult result = new HexStringPropertyValidator().validate(BYTE_SEQUENCE.getName(), byteSequence, validationContext);
+            results.add(result);
+        }
+        return results;
+    }
+    
+
+    @OnScheduled
+    public void initializeByteSequence(final ProcessContext context) throws DecoderException {
+        final String bytePattern = context.getProperty(BYTE_SEQUENCE).getValue();
+        
+        final String format = context.getProperty(FORMAT).getValue();
+        if ( HEX_FORMAT.getValue().equals(format) ) {
+            this.byteSequence.set(Hex.decodeHex(bytePattern.toCharArray()));
+        } else {
+            this.byteSequence.set(bytePattern.getBytes(StandardCharsets.UTF_8));
         }
     }
 
@@ -143,6 +185,21 @@ public class SplitContent extends AbstractProcessor {
 
         final ProcessorLog logger = getLogger();
         final boolean keepSequence = context.getProperty(KEEP_SEQUENCE).asBoolean();
+        final boolean keepTrailingSequence;
+        final boolean keepLeadingSequence;
+        if ( keepSequence ) {
+            if ( context.getProperty(BYTE_SEQUENCE_LOCATION).getValue().equals(TRAILING_POSITION.getValue()) ) {
+                keepTrailingSequence = true;
+                keepLeadingSequence = false;
+            } else {
+                keepTrailingSequence = false;
+                keepLeadingSequence = true;
+            }
+        } else {
+            keepTrailingSequence = false;
+            keepLeadingSequence = false;
+        }
+        
         final byte[] byteSequence = this.byteSequence.get();
         if (byteSequence == null) {   // should never happen. But just in case...
             logger.error("{} Unable to obtain Byte Sequence", new Object[]{this});
@@ -169,15 +226,20 @@ public class SplitContent extends AbstractProcessor {
                         bytesRead++;
                         boolean matched = buffer.addAndCompare((byte) (nextByte & 0xFF));
                         if (matched) {
-                            final long splitLength;
+                            long splitLength;
 
-                            if (keepSequence) {
+                            if (keepTrailingSequence) {
                                 splitLength = bytesRead - startOffset;
                             } else {
                                 splitLength = bytesRead - startOffset - byteSequence.length;
                             }
 
-                            splits.add(new Tuple<>(startOffset, splitLength));
+                            if ( keepLeadingSequence && startOffset > 0 ) {
+                                splitLength += byteSequence.length;
+                            }
+                            
+                            final long splitStart = (keepLeadingSequence && startOffset > 0) ? startOffset - byteSequence.length : startOffset;
+                            splits.add(new Tuple<>(splitStart, splitLength));
                             startOffset = bytesRead;
                             buffer.clear();
                         }
@@ -207,8 +269,11 @@ public class SplitContent extends AbstractProcessor {
             lastOffsetPlusSize = offset + size;
         }
 
+        // lastOffsetPlusSize indicates the ending position of the last split.
+        // if the data didn't end with the byte sequence, we need one final split to run from the end
+        // of the last split to the end of the content.
         long finalSplitOffset = lastOffsetPlusSize;
-        if (!keepSequence) {
+        if (!keepTrailingSequence && !keepLeadingSequence) {
             finalSplitOffset += byteSequence.length;
         }
         if (finalSplitOffset > -1L && finalSplitOffset < flowFile.getSize()) {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4752e284/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestSplitContent.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestSplitContent.java b/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestSplitContent.java
index 2e08062..07c255b 100644
--- a/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestSplitContent.java
+++ b/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestSplitContent.java
@@ -30,6 +30,129 @@ import org.junit.Test;
 public class TestSplitContent {
 
     @Test
+    public void testTextFormatLeadingPosition() {
+        final TestRunner runner = TestRunners.newTestRunner(new SplitContent());
+        runner.setProperty(SplitContent.FORMAT, SplitContent.UTF8_FORMAT.getValue());
+        runner.setProperty(SplitContent.BYTE_SEQUENCE, "ub");
+        runner.setProperty(SplitContent.KEEP_SEQUENCE, "true");
+        runner.setProperty(SplitContent.BYTE_SEQUENCE_LOCATION, SplitContent.LEADING_POSITION.getValue());
+
+        runner.enqueue("rub-a-dub-dub".getBytes());
+        runner.run();
+
+        runner.assertTransferCount(SplitContent.REL_ORIGINAL, 1);
+        runner.assertTransferCount(SplitContent.REL_SPLITS, 4);
+
+        runner.assertQueueEmpty();
+        final List<MockFlowFile> splits = runner.getFlowFilesForRelationship(SplitContent.REL_SPLITS);
+        splits.get(0).assertContentEquals("r");
+        splits.get(1).assertContentEquals("ub-a-d");
+        splits.get(2).assertContentEquals("ub-d");
+        splits.get(3).assertContentEquals("ub");
+    }
+    
+    
+    @Test
+    public void testTextFormatSplits() {
+        final TestRunner runner = TestRunners.newTestRunner(new SplitContent());
+        runner.setProperty(SplitContent.FORMAT, SplitContent.UTF8_FORMAT.getValue());
+        runner.setProperty(SplitContent.BYTE_SEQUENCE, "test");
+        runner.setProperty(SplitContent.KEEP_SEQUENCE, "true");
+        runner.setProperty(SplitContent.BYTE_SEQUENCE_LOCATION, SplitContent.LEADING_POSITION.getValue());
+
+        final byte[] input = "This is a test. This is another test. And this is yet another test. Finally this is the last Test.".getBytes();
+        runner.enqueue(input);
+        runner.run();
+
+        runner.assertTransferCount(SplitContent.REL_ORIGINAL, 1);
+        runner.assertTransferCount(SplitContent.REL_SPLITS, 4);
+
+        runner.assertQueueEmpty();
+        List<MockFlowFile> splits = runner.getFlowFilesForRelationship(SplitContent.REL_SPLITS);
+        splits.get(0).assertContentEquals("This is a ");
+        splits.get(1).assertContentEquals("test. This is another ");
+        splits.get(2).assertContentEquals("test. And this is yet another ");
+        splits.get(3).assertContentEquals("test. Finally this is the last Test.");
+        runner.clearTransferState();
+        
+        runner.setProperty(SplitContent.KEEP_SEQUENCE, "false");
+        runner.enqueue(input);
+        runner.run();
+        runner.assertTransferCount(SplitContent.REL_ORIGINAL, 1);
+        runner.assertTransferCount(SplitContent.REL_SPLITS, 4);
+        splits = runner.getFlowFilesForRelationship(SplitContent.REL_SPLITS);
+        splits.get(0).assertContentEquals("This is a ");
+        splits.get(1).assertContentEquals(". This is another ");
+        splits.get(2).assertContentEquals(". And this is yet another ");
+        splits.get(3).assertContentEquals(". Finally this is the last Test.");
+        runner.clearTransferState();
+        
+        runner.setProperty(SplitContent.KEEP_SEQUENCE, "true");
+        runner.setProperty(SplitContent.BYTE_SEQUENCE_LOCATION, SplitContent.TRAILING_POSITION.getValue());
+        runner.enqueue(input);
+        runner.run();
+        runner.assertTransferCount(SplitContent.REL_ORIGINAL, 1);
+        runner.assertTransferCount(SplitContent.REL_SPLITS, 4);
+        splits = runner.getFlowFilesForRelationship(SplitContent.REL_SPLITS);
+        splits.get(0).assertContentEquals("This is a test");
+        splits.get(1).assertContentEquals(". This is another test");
+        splits.get(2).assertContentEquals(". And this is yet another test");
+        splits.get(3).assertContentEquals(". Finally this is the last Test.");
+        runner.clearTransferState();
+        
+        runner.setProperty(SplitContent.KEEP_SEQUENCE, "true");
+        runner.setProperty(SplitContent.BYTE_SEQUENCE_LOCATION, SplitContent.TRAILING_POSITION.getValue());
+        runner.enqueue("This is a test. This is another test. And this is yet another test. Finally this is the last test".getBytes());
+        runner.run();
+        runner.assertTransferCount(SplitContent.REL_ORIGINAL, 1);
+        runner.assertTransferCount(SplitContent.REL_SPLITS, 4);
+        splits = runner.getFlowFilesForRelationship(SplitContent.REL_SPLITS);
+        splits.get(0).assertContentEquals("This is a test");
+        splits.get(1).assertContentEquals(". This is another test");
+        splits.get(2).assertContentEquals(". And this is yet another test");
+        splits.get(3).assertContentEquals(". Finally this is the last test");
+        runner.clearTransferState();
+        
+        runner.setProperty(SplitContent.KEEP_SEQUENCE, "true");
+        runner.setProperty(SplitContent.BYTE_SEQUENCE_LOCATION, SplitContent.LEADING_POSITION.getValue());
+        runner.enqueue("This is a test. This is another test. And this is yet another test. Finally this is the last test".getBytes());
+        runner.run();
+        runner.assertTransferCount(SplitContent.REL_ORIGINAL, 1);
+        runner.assertTransferCount(SplitContent.REL_SPLITS, 5);
+        splits = runner.getFlowFilesForRelationship(SplitContent.REL_SPLITS);
+        splits.get(0).assertContentEquals("This is a ");
+        splits.get(1).assertContentEquals("test. This is another ");
+        splits.get(2).assertContentEquals("test. And this is yet another ");
+        splits.get(3).assertContentEquals("test. Finally this is the last ");
+        splits.get(4).assertContentEquals("test");
+        
+        runner.clearTransferState();
+    }
+    
+    
+    @Test
+    public void testTextFormatTrailingPosition() {
+        final TestRunner runner = TestRunners.newTestRunner(new SplitContent());
+        runner.setProperty(SplitContent.FORMAT, SplitContent.UTF8_FORMAT.getValue());
+        runner.setProperty(SplitContent.BYTE_SEQUENCE, "ub");
+        runner.setProperty(SplitContent.KEEP_SEQUENCE, "true");
+        runner.setProperty(SplitContent.BYTE_SEQUENCE_LOCATION, SplitContent.TRAILING_POSITION.getValue());
+
+        runner.enqueue("rub-a-dub-dub".getBytes());
+        runner.run();
+
+        runner.assertTransferCount(SplitContent.REL_ORIGINAL, 1);
+        runner.assertTransferCount(SplitContent.REL_SPLITS, 3);
+
+        runner.assertQueueEmpty();
+        final List<MockFlowFile> splits = runner.getFlowFilesForRelationship(SplitContent.REL_SPLITS);
+        splits.get(0).assertContentEquals("rub");
+        splits.get(1).assertContentEquals("-a-dub");
+        splits.get(2).assertContentEquals("-dub");
+    }
+    
+    
+    @Test
     public void testSmallSplits() throws IOException {
         final TestRunner runner = TestRunners.newTestRunner(new SplitContent());
         runner.setProperty(SplitContent.KEEP_SEQUENCE, "false");


[57/62] [abbrv] incubator-nifi git commit: NIFI-501: - Ensuring the property descriptor is loaded before adding/updating the property in the table.

Posted by ma...@apache.org.
NIFI-501:
- Ensuring the property descriptor is loaded before adding/updating the property in the table.

Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/c1959b3e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/c1959b3e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/c1959b3e

Branch: refs/heads/NIFI-25
Commit: c1959b3e2d8f48247642655d3e8cbeb08baa3cef
Parents: eb023e5
Author: Matt Gilman <ma...@gmail.com>
Authored: Fri Apr 10 06:56:51 2015 -0400
Committer: Matt Gilman <ma...@gmail.com>
Committed: Fri Apr 10 06:56:51 2015 -0400

----------------------------------------------------------------------
 .../propertytable/jquery.propertytable.js       | 82 +++++++++-----------
 1 file changed, 36 insertions(+), 46 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/c1959b3e/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
index c0b8884..b5f56b0 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
@@ -882,24 +882,16 @@
                         // update the revision
                         nf.Client.setRevision(response.revision);
 
-                        $.Deferred(function (deferred) {
-                            // load the property descriptor if possible
-                            if (typeof configurationOptions.descriptorDeferred === 'function') {
-                                configurationOptions.descriptorDeferred(item.property).done(function(response) {
-                                    var descriptor = response.propertyDescriptor;
-
-                                    // store the descriptor for use later
-                                    var descriptors = gridContainer.data('descriptors');
-                                    if (!nf.Common.isUndefined(descriptors)) {
-                                        descriptors[descriptor.name] = descriptor;
-                                    }
-
-                                    deferred.resolve();
-                                });
-                            } else {
-                                deferred.resolve();
+                        // load the descriptor and update the property
+                        configurationOptions.descriptorDeferred(item.property).done(function(descriptorResponse) {
+                            var descriptor = descriptorResponse.propertyDescriptor;
+
+                            // store the descriptor for use later
+                            var descriptors = gridContainer.data('descriptors');
+                            if (!nf.Common.isUndefined(descriptors)) {
+                                descriptors[descriptor.name] = descriptor;
                             }
-                        }).done(function() {
+
                             // add a row for the new property
                             var data = grid.getData();
                             data.updateItem(item.id, $.extend(item, {
@@ -1385,37 +1377,35 @@
 
                             // ensure the property name and value is specified
                             if (propertyName !== '') {
-                                // load the property descriptor if possible
-                                if (typeof options.descriptorDeferred === 'function') {
-                                    options.descriptorDeferred(propertyName).done(function(response) {
-                                        var descriptor = response.propertyDescriptor;
-                                        
-                                        // store the descriptor for use later
-                                        var descriptors = table.data('descriptors');
-                                        if (!nf.Common.isUndefined(descriptors)) {
-                                            descriptors[descriptor.name] = descriptor;
-                                        }
+                                // load the descriptor and add the property
+                                options.descriptorDeferred(propertyName).done(function(response) {
+                                    var descriptor = response.propertyDescriptor;
+
+                                    // store the descriptor for use later
+                                    var descriptors = table.data('descriptors');
+                                    if (!nf.Common.isUndefined(descriptors)) {
+                                        descriptors[descriptor.name] = descriptor;
+                                    }
+
+                                    // add a row for the new property
+                                    var propertyGrid = table.data('gridInstance');
+                                    var propertyData = propertyGrid.getData();
+                                    var id = propertyData.getLength(); 
+                                    propertyData.addItem({
+                                        id: id,
+                                        hidden: false,
+                                        property: propertyName,
+                                        displayName: propertyName,
+                                        previousValue: null,
+                                        value: null,
+                                        type: 'userDefined'
                                     });
-                                }
-                                
-                                // add a row for the new property
-                                var propertyGrid = table.data('gridInstance');
-                                var propertyData = propertyGrid.getData();
-                                var id = propertyData.getLength(); 
-                                propertyData.addItem({
-                                    id: id,
-                                    hidden: false,
-                                    property: propertyName,
-                                    displayName: propertyName,
-                                    previousValue: null,
-                                    value: null,
-                                    type: 'userDefined'
+
+                                    // select the new properties row
+                                    var row = propertyData.getRowById(id);
+                                    propertyGrid.setSelectedRows([row]);
+                                    propertyGrid.scrollRowIntoView(row);
                                 });
-                                
-                                // select the new properties row
-                                var row = propertyData.getRowById(id);
-                                propertyGrid.setSelectedRows([row]);
-                                propertyGrid.scrollRowIntoView(row);
                             } else {
                                 nf.Dialog.showOkDialog({
                                     dialogContent: 'Property name must be specified.',


[08/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
new file mode 100644
index 0000000..1c25460
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
@@ -0,0 +1,1317 @@
+/*
+ * 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.
+ */
+
+/* global nf, Slick */
+
+/**
+ * Create a property table. The options are specified in the following
+ * format:
+ *
+ * {
+ *   readOnly: true,
+ *   newPropertyDialogContainer: 'body'
+ * }
+ */
+
+/**
+ * jQuery plugin for a property table.
+ * 
+ * @param {type} $
+ */
+(function ($) {
+    var languageId = 'nfel';
+    var editorClass = languageId + '-editor';
+
+    // text editor
+    var textEditor = function (args) {
+        var scope = this;
+        var initialValue = '';
+        var previousValue;
+        var propertyDescriptor;
+        var wrapper;
+        var isEmpty;
+        var input;
+
+        this.init = function () {
+            var container = $('body');
+
+            // get the property descriptor
+            var gridContainer = $(args.grid.getContainerNode());
+            var descriptors = gridContainer.data('descriptors');
+            propertyDescriptor = descriptors[args.item.property];
+
+            // record the previous value
+            previousValue = args.item[args.column.field];
+
+            // create the wrapper
+            wrapper = $('<div></div>').css({
+                'z-index': 100000,
+                'position': 'absolute',
+                'background': 'white',
+                'padding': '5px',
+                'overflow': 'hidden',
+                'border': '3px solid #365C6A',
+                'box-shadow': '4px 4px 6px rgba(0, 0, 0, 0.9)',
+                'cursor': 'move'
+            }).draggable({
+                cancel: '.button, textarea, .nf-checkbox',
+                containment: 'parent'
+            }).appendTo(container);
+
+            // create the input field
+            input = $('<textarea hidefocus rows="5"/>').css({
+                'background': 'white',
+                'width': args.position.width + 'px',
+                'min-width': '150px',
+                'height': '80px',
+                'border-width': '0',
+                'outline': '0',
+                'overflow-y': 'auto',
+                'resize': 'both',
+                'margin-bottom': '28px'
+            }).tab().on('keydown', scope.handleKeyDown).appendTo(wrapper);
+
+            // create the button panel
+            var stringCheckPanel = $('<div class="string-check-container">');
+
+            // build the custom checkbox
+            isEmpty = $('<div class="nf-checkbox string-check"/>').appendTo(stringCheckPanel);
+            $('<span class="string-check-label">&nbsp;Empty</span>').appendTo(stringCheckPanel);
+
+            var ok = $('<div class="button button-normal">Ok</div>').on('click', scope.save);
+            var cancel = $('<div class="button button-normal">Cancel</div>').on('click', scope.cancel);
+            $('<div></div>').css({
+                'position': 'absolute',
+                'bottom': '0',
+                'left': '0',
+                'right': '0',
+                'padding': '0 3px 5px'
+            }).append(stringCheckPanel).append(ok).append(cancel).append('<div class="clear"></div>').appendTo(wrapper);
+
+            // position and focus
+            scope.position(args.position);
+            input.focus().select();
+        };
+
+        this.handleKeyDown = function (e) {
+            if (e.which === $.ui.keyCode.ENTER && !e.shiftKey) {
+                scope.save();
+            } else if (e.which === $.ui.keyCode.ESCAPE) {
+                scope.cancel();
+            }
+        };
+
+        this.save = function () {
+            args.commitChanges();
+        };
+
+        this.cancel = function () {
+            input.val(initialValue);
+            args.cancelChanges();
+        };
+
+        this.hide = function () {
+            wrapper.hide();
+        };
+
+        this.show = function () {
+            wrapper.show();
+        };
+
+        this.position = function (position) {
+            wrapper.css({
+                'top': position.top - 5,
+                'left': position.left - 5
+            });
+        };
+
+        this.destroy = function () {
+            wrapper.remove();
+        };
+
+        this.focus = function () {
+            input.focus();
+        };
+
+        this.loadValue = function (item) {
+            // determine if this is a sensitive property
+            var isEmptyChecked = false;
+            var sensitive = nf.Common.isSensitiveProperty(propertyDescriptor);
+
+            // determine the value to use when populating the text field
+            if (nf.Common.isDefinedAndNotNull(item[args.column.field])) {
+                if (sensitive) {
+                    initialValue = nf.Common.config.sensitiveText;
+                } else {
+                    initialValue = item[args.column.field];
+                    isEmptyChecked = initialValue === '';
+                }
+            }
+
+            // determine if its an empty string
+            var checkboxStyle = isEmptyChecked ? 'checkbox-checked' : 'checkbox-unchecked';
+            isEmpty.addClass(checkboxStyle);
+
+            // style sensitive properties differently
+            if (sensitive) {
+                input.addClass('sensitive').keydown(function () {
+                    var sensitiveInput = $(this);
+                    if (sensitiveInput.hasClass('sensitive')) {
+                        sensitiveInput.removeClass('sensitive');
+                        if (sensitiveInput.val() === nf.Common.config.sensitiveText) {
+                            sensitiveInput.val('');
+                        }
+                    }
+                });
+            }
+
+            input.val(initialValue);
+            input.select();
+        };
+
+        this.serializeValue = function () {
+            // if the field has been cleared, set the value accordingly
+            if (input.val() === '') {
+                // if the user has checked the empty string checkbox, use emtpy string
+                if (isEmpty.hasClass('checkbox-checked')) {
+                    return '';
+                } else {
+                    // otherwise if the property is required
+                    if (nf.Common.isRequiredProperty(propertyDescriptor)) {
+                        if (nf.Common.isBlank(propertyDescriptor.defaultValue)) {
+                            return previousValue;
+                        } else {
+                            return propertyDescriptor.defaultValue;
+                        }
+                    } else {
+                        // if the property is not required, clear the value
+                        return null;
+                    }
+                }
+            } else {
+                // if the field still has the sensitive class it means a property
+                // was edited but never modified so we should restore the previous
+                // value instead of setting it to the 'sensitive value set' string
+                if (input.hasClass('sensitive')) {
+                    return previousValue;
+                } else {
+                    // if there is text specified, use that value
+                    return input.val();
+                }
+            }
+        };
+
+        this.applyValue = function (item, state) {
+            item[args.column.field] = state;
+        };
+
+        this.isValueChanged = function () {
+            return scope.serializeValue() !== previousValue;
+        };
+
+        this.validate = function () {
+            return {
+                valid: true,
+                msg: null
+            };
+        };
+
+        // initialize the custom long text editor
+        this.init();
+    };
+
+    // nfel editor
+    var nfelEditor = function (args) {
+        var scope = this;
+        var initialValue = '';
+        var previousValue;
+        var propertyDescriptor;
+        var isEmpty;
+        var wrapper;
+        var editor;
+
+        this.init = function () {
+            var container = $('body');
+
+            // get the property descriptor
+            var gridContainer = $(args.grid.getContainerNode());
+            var descriptors = gridContainer.data('descriptors');
+            propertyDescriptor = descriptors[args.item.property];
+
+            // determine if this is a sensitive property
+            var sensitive = nf.Common.isSensitiveProperty(propertyDescriptor);
+
+            // record the previous value
+            previousValue = args.item[args.column.field];
+
+            var languageId = 'nfel';
+            var editorClass = languageId + '-editor';
+
+            // create the wrapper
+            wrapper = $('<div></div>').addClass('slickgrid-nfel-editor').css({
+                'z-index': 14000,
+                'position': 'absolute',
+                'background': 'white',
+                'padding': '5px',
+                'overflow': 'hidden',
+                'border': '3px solid #365C6A',
+                'box-shadow': '4px 4px 6px rgba(0, 0, 0, 0.9)',
+                'cursor': 'move'
+            }).draggable({
+                cancel: 'input, textarea, pre, .nf-checkbox, .button, .' + editorClass,
+                containment: 'parent'
+            }).appendTo(container);
+
+            // create the editor
+            editor = $('<div></div>').addClass(editorClass).appendTo(wrapper).nfeditor({
+                languageId: languageId,
+                width: args.position.width,
+                minWidth: 175,
+                minHeight: 100,
+                resizable: true,
+                sensitive: sensitive,
+                escape: function () {
+                    scope.cancel();
+                },
+                enter: function () {
+                    scope.save();
+                }
+            });
+
+            // create the button panel
+            var stringCheckPanel = $('<div class="string-check-container">');
+
+            // build the custom checkbox
+            isEmpty = $('<div class="nf-checkbox string-check"/>').appendTo(stringCheckPanel);
+            $('<span class="string-check-label">&nbsp;Empty</span>').appendTo(stringCheckPanel);
+
+            var ok = $('<div class="button button-normal">Ok</div>').on('click', scope.save);
+            var cancel = $('<div class="button button-normal">Cancel</div>').on('click', scope.cancel);
+            $('<div></div>').css({
+                'position': 'absolute',
+                'bottom': '0',
+                'left': '0',
+                'right': '0',
+                'padding': '0 3px 5px 1px'
+            }).append(stringCheckPanel).append(ok).append(cancel).append('<div class="clear"></div>').appendTo(wrapper);
+
+            // position and focus
+            scope.position(args.position);
+            editor.nfeditor('focus').nfeditor('selectAll');
+        };
+
+        this.save = function () {
+            args.commitChanges();
+        };
+
+        this.cancel = function () {
+            editor.nfeditor('setValue', initialValue);
+            args.cancelChanges();
+        };
+
+        this.hide = function () {
+            wrapper.hide();
+        };
+
+        this.show = function () {
+            wrapper.show();
+            editor.nfeditor('setSize', args.position.width, null).nfeditor('refresh');
+        };
+
+        this.position = function (position) {
+            wrapper.css({
+                'top': position.top - 5,
+                'left': position.left - 5
+            });
+        };
+
+        this.destroy = function () {
+            editor.nfeditor('destroy');
+            wrapper.remove();
+        };
+
+        this.focus = function () {
+            editor.nfeditor('focus');
+        };
+
+        this.loadValue = function (item) {
+            // determine if this is a sensitive property
+            var isEmptyChecked = false;
+            var sensitive = nf.Common.isSensitiveProperty(propertyDescriptor);
+
+            // determine the value to use when populating the text field
+            if (nf.Common.isDefinedAndNotNull(item[args.column.field])) {
+                if (sensitive) {
+                    initialValue = nf.Common.config.sensitiveText;
+                } else {
+                    initialValue = item[args.column.field];
+                    isEmptyChecked = initialValue === '';
+                }
+            }
+
+            // determine if its an empty string
+            var checkboxStyle = isEmptyChecked ? 'checkbox-checked' : 'checkbox-unchecked';
+            isEmpty.addClass(checkboxStyle);
+
+            editor.nfeditor('setValue', initialValue).nfeditor('selectAll');
+        };
+
+        this.serializeValue = function () {
+            var value = editor.nfeditor('getValue');
+
+            // if the field has been cleared, set the value accordingly
+            if (value === '') {
+                // if the user has checked the empty string checkbox, use emtpy string
+                if (isEmpty.hasClass('checkbox-checked')) {
+                    return '';
+                } else {
+                    // otherwise if the property is required
+                    if (nf.Common.isRequiredProperty(propertyDescriptor)) {
+                        if (nf.Common.isBlank(propertyDescriptor.defaultValue)) {
+                            return previousValue;
+                        } else {
+                            return propertyDescriptor.defaultValue;
+                        }
+                    } else {
+                        // if the property is not required, clear the value
+                        return null;
+                    }
+                }
+            } else {
+                // if the field still has the sensitive class it means a property
+                // was edited but never modified so we should restore the previous
+                // value instead of setting it to the 'sensitive value set' string
+
+                // if the field hasn't been modified return the previous value... this
+                // is important because sensitive properties contain the text 'sensitive
+                // value set' which is cleared when the value is edited. we do not 
+                // want to actually use this value
+                if (editor.nfeditor('isModified') === false) {
+                    return previousValue;
+                } else {
+                    // if there is text specified, use that value
+                    return value;
+                }
+            }
+        };
+
+        this.applyValue = function (item, state) {
+            item[args.column.field] = state;
+        };
+
+        this.isValueChanged = function () {
+            return scope.serializeValue() !== previousValue;
+        };
+
+        this.validate = function () {
+            return {
+                valid: true,
+                msg: null
+            };
+        };
+
+        // initialize the custom long nfel editor
+        this.init();
+    };
+
+    // combo editor
+    var comboEditor = function (args) {
+        var scope = this;
+        var initialValue = null;
+        var wrapper;
+        var combo;
+        var propertyDescriptor;
+
+        this.init = function () {
+            var container = $('body');
+
+            // get the property descriptor
+            var gridContainer = $(args.grid.getContainerNode());
+            var descriptors = gridContainer.data('descriptors');
+            propertyDescriptor = descriptors[args.item.property];
+
+            // create the wrapper
+            wrapper = $('<div></div>').css({
+                'z-index': 1999,
+                'position': 'absolute',
+                'background': 'white',
+                'padding': '5px',
+                'overflow': 'hidden',
+                'border': '3px solid #365C6A',
+                'box-shadow': '4px 4px 6px rgba(0, 0, 0, 0.9)',
+                'cursor': 'move'
+            }).draggable({
+                cancel: '.button, .combo',
+                containment: 'parent'
+            }).appendTo(container);
+
+            // check for allowable values which will drive which editor to use
+            var allowableValues = nf.Common.getAllowableValues(propertyDescriptor);
+
+            // show the output port options
+            var options = [];
+            if (propertyDescriptor.required === false) {
+                options.push({
+                    text: 'No value',
+                    value: null,
+                    optionClass: 'unset'
+                });
+            }
+            if ($.isArray(allowableValues)) {
+                $.each(allowableValues, function (i, allowableValue) {
+                    options.push({
+                        text: allowableValue.displayName,
+                        value: allowableValue.value,
+                        description: nf.Common.escapeHtml(allowableValue.description)
+                    });
+                });
+            }
+
+            // ensure the options there is at least one option
+            if (options.length === 0) {
+                options.push({
+                    text: 'No value',
+                    value: null,
+                    optionClass: 'unset',
+                    disabled: true
+                });
+            }
+
+            // determine the max height
+            var position = args.position;
+            var windowHeight = $(window).height();
+            var maxHeight = windowHeight - position.bottom - 16;
+
+            // build the combo field
+            combo = $('<div class="value-combo combo"></div>').combo({
+                options: options,
+                maxHeight: maxHeight
+            }).width(position.width - 16).appendTo(wrapper);
+
+            // add buttons for handling user input
+            $('<div class="button button-normal">Cancel</div>').css({
+                'margin': '0 0 0 5px',
+                'float': 'left'
+            }).on('click', scope.cancel).appendTo(wrapper);
+            $('<div class="button button-normal">Ok</div>').css({
+                'margin': '0 0 0 5px',
+                'float': 'left'
+            }).on('click', scope.save).appendTo(wrapper);
+
+            // position and focus
+            scope.position(position);
+        };
+
+        this.save = function () {
+            args.commitChanges();
+        };
+
+        this.cancel = function () {
+            args.cancelChanges();
+        };
+
+        this.hide = function () {
+            wrapper.hide();
+        };
+
+        this.show = function () {
+            wrapper.show();
+        };
+
+        this.position = function (position) {
+            wrapper.css({
+                'top': position.top - 5,
+                'left': position.left - 5
+            });
+        };
+
+        this.destroy = function () {
+            wrapper.remove();
+        };
+
+        this.focus = function () {
+        };
+
+        this.loadValue = function (item) {
+            // select as appropriate
+            if (!nf.Common.isUndefined(item.value)) {
+                initialValue = item.value;
+
+                combo.combo('setSelectedOption', {
+                    value: item.value
+                });
+            } else if (nf.Common.isDefinedAndNotNull(propertyDescriptor.defaultValue)) {
+                initialValue = propertyDescriptor.defaultValue;
+
+                combo.combo('setSelectedOption', {
+                    value: propertyDescriptor.defaultValue
+                });
+            }
+        };
+
+        this.serializeValue = function () {
+            var selectedOption = combo.combo('getSelectedOption');
+            return selectedOption.value;
+        };
+
+        this.applyValue = function (item, state) {
+            item[args.column.field] = state;
+        };
+
+        this.isValueChanged = function () {
+            var selectedOption = combo.combo('getSelectedOption');
+            return (!(selectedOption.value === "" && initialValue === null)) && (selectedOption.value !== initialValue);
+        };
+
+        this.validate = function () {
+            return {
+                valid: true,
+                msg: null
+            };
+        };
+
+        // initialize the custom long text editor
+        this.init();
+    };
+
+    /**
+     * Shows the property value for the specified row and cell.
+     * 
+     * @param {type} propertyGrid
+     * @param {type} descriptors 
+     * @param {type} row
+     * @param {type} cell
+     */
+    var showPropertyValue = function (propertyGrid, descriptors, row, cell) {
+        // remove any currently open detail dialogs
+        removeAllPropertyDetailDialogs();
+
+        // get the property in question
+        var propertyData = propertyGrid.getData();
+        var property = propertyData.getItem(row);
+
+        // ensure there is a value
+        if (nf.Common.isDefinedAndNotNull(property.value)) {
+
+            // get the descriptor to insert the description tooltip
+            var propertyDescriptor = descriptors[property.property];
+
+            // ensure we're not dealing with a sensitive property
+            if (!nf.Common.isSensitiveProperty(propertyDescriptor)) {
+
+                // get details about the location of the cell
+                var cellNode = $(propertyGrid.getCellNode(row, cell));
+                var offset = cellNode.offset();
+
+                // create the wrapper
+                var wrapper = $('<div class="property-detail"></div>').css({
+                    'z-index': 100000,
+                    'position': 'absolute',
+                    'background': 'white',
+                    'padding': '5px',
+                    'overflow': 'hidden',
+                    'border': '3px solid #365C6A',
+                    'box-shadow': '4px 4px 6px rgba(0, 0, 0, 0.9)',
+                    'cursor': 'move',
+                    'top': offset.top - 5,
+                    'left': offset.left - 5
+                }).appendTo('body');
+
+                var editor = null;
+
+                // so the nfel editor is appropriate
+                if (nf.Common.supportsEl(propertyDescriptor)) {
+                    var languageId = 'nfel';
+                    var editorClass = languageId + '-editor';
+
+                    // prevent dragging over the nf editor
+                    wrapper.draggable({
+                        cancel: 'input, textarea, pre, .button, .' + editorClass,
+                        containment: 'parent'
+                    });
+
+                    // create the editor
+                    editor = $('<div></div>').addClass(editorClass).appendTo(wrapper).nfeditor({
+                        languageId: languageId,
+                        width: cellNode.width(),
+                        content: property.value,
+                        minWidth: 175,
+                        minHeight: 100,
+                        readOnly: true,
+                        resizable: true
+                    });
+                } else {
+                    // prevent dragging over standard components
+                    wrapper.draggable({
+                        containment: 'parent'
+                    });
+
+                    // create the input field
+                    $('<textarea hidefocus rows="5" readonly="readonly"/>').css({
+                        'background': 'white',
+                        'width': cellNode.width() + 'px',
+                        'height': '80px',
+                        'border-width': '0',
+                        'outline': '0',
+                        'overflow-y': 'auto',
+                        'resize': 'both',
+                        'margin-bottom': '28px'
+                    }).text(property.value).appendTo(wrapper);
+                }
+
+                // add an ok button that will remove the entire pop up
+                var ok = $('<div class="button button-normal">Ok</div>').on('click', function () {
+                    // clean up the editor
+                    if (editor !== null) {
+                        editor.nfeditor('destroy');
+                    }
+
+                    // clean up the rest
+                    wrapper.hide().remove();
+                });
+                $('<div></div>').css({
+                    'position': 'absolute',
+                    'bottom': '0',
+                    'left': '0',
+                    'right': '0',
+                    'padding': '0 3px 5px'
+                }).append(ok).append('<div class="clear"></div>').appendTo(wrapper);
+            }
+        }
+    };
+
+    /**
+     * Removes all currently open property detail dialogs.
+     */
+    var removeAllPropertyDetailDialogs = function () {
+        $('body').children('div.property-detail').hide().remove();
+    };
+
+    var initPropertiesTable = function (table, options) {
+        // function for formatting the property name
+        var nameFormatter = function (row, cell, value, columnDef, dataContext) {
+            var nameWidthOffset = 10;
+            var cellContent = $('<div></div>');
+
+            // format the contents
+            var formattedValue = $('<span/>').addClass('table-cell').text(value).appendTo(cellContent);
+            if (dataContext.type === 'required') {
+                formattedValue.addClass('required');
+            }
+
+            // get the property descriptor
+            var descriptors = table.data('descriptors');
+            var propertyDescriptor = descriptors[dataContext.property];
+
+            // show the property description if applicable
+            if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+                if (!nf.Common.isBlank(propertyDescriptor.description) || !nf.Common.isBlank(propertyDescriptor.defaultValue) || !nf.Common.isBlank(propertyDescriptor.supportsEl)) {
+                    $('<img class="icon-info" src="images/iconInfo.png" alt="Info" title="" style="float: right; margin-right: 6px; margin-top: 4px;" />').appendTo(cellContent);
+                    $('<span class="hidden property-descriptor-name"></span>').text(dataContext.property).appendTo(cellContent);
+                    nameWidthOffset = 26; // 10 + icon width (10) + icon margin (6)
+                }
+            }
+
+            // adjust the width accordingly
+            formattedValue.width(columnDef.width - nameWidthOffset).ellipsis();
+
+            // return the cell content
+            return cellContent.html();
+        };
+
+        // function for formatting the property value
+        var valueFormatter = function (row, cell, value, columnDef, dataContext) {
+            var valueMarkup;
+            if (nf.Common.isDefinedAndNotNull(value)) {
+                // get the property descriptor
+                var descriptors = table.data('descriptors');
+                var propertyDescriptor = descriptors[dataContext.property];
+
+                // determine if the property is sensitive
+                if (nf.Common.isSensitiveProperty(propertyDescriptor)) {
+                    valueMarkup = '<span class="table-cell sensitive">Sensitive value set</span>';
+                } else {
+                    // if there are allowable values, attempt to swap out for the display name
+                    var allowableValues = nf.Common.getAllowableValues(propertyDescriptor);
+                    if ($.isArray(allowableValues)) {
+                        $.each(allowableValues, function (_, allowableValue) {
+                            if (value === allowableValue.value) {
+                                value = allowableValue.displayName;
+                                return false;
+                            }
+                        });
+                    }
+
+                    if (value === '') {
+                        valueMarkup = '<span class="table-cell blank">Empty string set</span>';
+                    } else {
+                        valueMarkup = '<div class="table-cell value"><pre class="ellipsis">' + nf.Common.escapeHtml(value) + '</pre></div>';
+                    }
+                }
+            } else {
+                valueMarkup = '<span class="unset">No value set</span>';
+            }
+
+            // format the contents
+            var content = $(valueMarkup);
+            if (dataContext.type === 'required') {
+                content.addClass('required');
+            }
+            content.find('.ellipsis').width(columnDef.width - 10).ellipsis();
+
+            // return the appropriate markup
+            return $('<div/>').append(content).html();
+        };
+
+        var propertyColumns = [
+            {id: 'property', field: 'displayName', name: 'Property', sortable: false, resizable: true, rerenderOnResize: true, formatter: nameFormatter},
+            {id: 'value', field: 'value', name: 'Value', sortable: false, resizable: true, cssClass: 'pointer', rerenderOnResize: true, formatter: valueFormatter}
+        ];
+
+        if (options.readOnly !== true) {
+            // custom formatter for the actions column
+            var actionFormatter = function (row, cell, value, columnDef, dataContext) {
+                var markup = '';
+
+                // allow user defined properties to be removed
+                if (dataContext.type === 'userDefined') {
+                    markup = '<img src="images/iconDelete.png" title="Delete" class="delete-property pointer" style="margin-top: 2px" />';
+                }
+
+                return markup;
+            };
+            propertyColumns.push({id: "actions", name: "&nbsp;", minWidth: 20, width: 20, formatter: actionFormatter});
+        }
+
+        var propertyConfigurationOptions = {
+            forceFitColumns: true,
+            enableTextSelectionOnCells: true,
+            enableCellNavigation: true,
+            enableColumnReorder: false,
+            editable: options.readOnly !== true,
+            enableAddRow: false,
+            autoEdit: false
+        };
+
+        // initialize the dataview
+        var propertyData = new Slick.Data.DataView({
+            inlineFilters: false
+        });
+        propertyData.setItems([]);
+        propertyData.setFilterArgs({
+            searchString: '',
+            property: 'hidden'
+        });
+        propertyData.setFilter(filter);
+        propertyData.getItemMetadata = function (index) {
+            var item = propertyData.getItem(index);
+
+            // get the property descriptor
+            var descriptors = table.data('descriptors');
+            var propertyDescriptor = descriptors[item.property];
+
+            // support el if specified or unsure yet (likely a dynamic property)
+            if (nf.Common.isUndefinedOrNull(propertyDescriptor) || nf.Common.supportsEl(propertyDescriptor)) {
+                return {
+                    columns: {
+                        value: {
+                            editor: nfelEditor
+                        }
+                    }
+                };
+            } else {
+                // check for allowable values which will drive which editor to use
+                var allowableValues = nf.Common.getAllowableValues(propertyDescriptor);
+                if ($.isArray(allowableValues)) {
+                    return {
+                        columns: {
+                            value: {
+                                editor: comboEditor
+                            }
+                        }
+                    };
+                } else {
+                    return {
+                        columns: {
+                            value: {
+                                editor: textEditor
+                            }
+                        }
+                    };
+                }
+            }
+        };
+
+        // initialize the grid
+        var propertyGrid = new Slick.Grid(table, propertyData, propertyColumns, propertyConfigurationOptions);
+        propertyGrid.setSelectionModel(new Slick.RowSelectionModel());
+        propertyGrid.onClick.subscribe(function (e, args) {
+            if (propertyGrid.getColumns()[args.cell].id === 'value') {
+                if (options.readOnly === true) {
+                    var descriptors = table.data('descriptors');
+                    showPropertyValue(propertyGrid, descriptors, args.row, args.cell);
+                } else {
+                    // edits the clicked cell
+                    propertyGrid.gotoCell(args.row, args.cell, true);
+                }
+
+                // prevents standard edit logic
+                e.stopImmediatePropagation();
+            } else if (propertyGrid.getColumns()[args.cell].id === 'actions') {
+                var target = $(e.target);
+                if (target.hasClass('delete-property')) {
+                    // mark the property in question for removal
+                    var property = propertyData.getItem(args.row);
+                    property.hidden = true;
+
+                    // refresh the table
+                    propertyData.updateItem(property.id, property);
+
+                    // prevents standard edit logic
+                    e.stopImmediatePropagation();
+                }
+            }
+        });
+        propertyGrid.onKeyDown.subscribe(function(e, args) {
+            if (e.which === $.ui.keyCode.ESCAPE) {
+                var editorLock = propertyGrid.getEditorLock();
+                if (editorLock.isActive()) {
+                    editorLock.cancelCurrentEdit();
+                    
+                    // prevents standard cancel logic - standard logic does
+                    // not stop propagation when escape is pressed
+                    e.stopImmediatePropagation();
+                    e.preventDefault();
+                }
+            }
+        });
+
+        // wire up the dataview to the grid
+        propertyData.onRowCountChanged.subscribe(function (e, args) {
+            propertyGrid.updateRowCount();
+            propertyGrid.render();
+        });
+        propertyData.onRowsChanged.subscribe(function (e, args) {
+            propertyGrid.invalidateRows(args.rows);
+            propertyGrid.render();
+        });
+
+        // hold onto an instance of the grid and listen for mouse events to add tooltips where appropriate
+        table.data('gridInstance', propertyGrid).on('mouseenter', 'div.slick-cell', function (e) {
+            var infoIcon = $(this).find('img.icon-info');
+            if (infoIcon.length && !infoIcon.data('qtip')) {
+                var property = $(this).find('span.property-descriptor-name').text();
+
+                // get the property descriptor
+                var descriptors = table.data('descriptors');
+                var propertyDescriptor = descriptors[property];
+
+                // get the history
+                var history = table.data('history');
+                var propertyHistory = history[property];
+
+                // format the tooltip
+                var tooltip = nf.Common.formatPropertyTooltip(propertyDescriptor, propertyHistory);
+
+                if (nf.Common.isDefinedAndNotNull(tooltip)) {
+                    infoIcon.qtip($.extend({
+                        content: tooltip
+                    }, nf.Common.config.tooltipConfig));
+                }
+            }
+        });
+    };
+
+    var saveRow = function (table) {
+        // get the property grid to commit the current edit
+        var propertyGrid = table.data('gridInstance');
+        if (nf.Common.isDefinedAndNotNull(propertyGrid)) {
+            var editController = propertyGrid.getEditController();
+            editController.commitCurrentEdit();
+        }
+    };
+
+    /**
+     * Performs the filtering.
+     * 
+     * @param {object} item     The item subject to filtering
+     * @param {object} args     Filter arguments
+     * @returns {Boolean}       Whether or not to include the item
+     */
+    var filter = function (item, args) {
+        return item.hidden === false;
+    };
+
+    /**
+     * Loads the specified properties.
+     * 
+     * @param {type} table 
+     * @param {type} properties
+     * @param {type} descriptors
+     * @param {type} history
+     */
+    var loadProperties = function (table, properties, descriptors, history) {
+        // save the descriptors and history
+        table.data({
+            'descriptors': descriptors,
+            'history': history
+        });
+
+        // get the grid
+        var propertyGrid = table.data('gridInstance');
+        var propertyData = propertyGrid.getData();
+
+        // generate the properties
+        if (nf.Common.isDefinedAndNotNull(properties)) {
+            propertyData.beginUpdate();
+
+            var i = 0;
+            $.each(properties, function (name, value) {
+                // get the property descriptor
+                var descriptor = descriptors[name];
+
+                // determine the property type
+                var type = 'userDefined';
+                var displayName = name;
+                if (nf.Common.isDefinedAndNotNull(descriptor)) {
+                    if (nf.Common.isRequiredProperty(descriptor)) {
+                        type = 'required';
+                    } else if (nf.Common.isDynamicProperty(descriptor)) {
+                        type = 'userDefined';
+                    } else {
+                        type = 'optional';
+                    }
+
+                    // use the display name if possible
+                    displayName = descriptor.displayName;
+
+                    // determine the value
+                    if (nf.Common.isNull(value) && nf.Common.isDefinedAndNotNull(descriptor.defaultValue)) {
+                        value = descriptor.defaultValue;
+                    }
+                }
+
+                // add the row
+                propertyData.addItem({
+                    id: i++,
+                    hidden: false,
+                    property: name,
+                    displayName: displayName,
+                    previousValue: value,
+                    value: value,
+                    type: type
+                });
+            });
+
+            propertyData.endUpdate();
+        }
+    };
+
+    /**
+     * Clears the property table container.
+     * 
+     * @param {jQuery} propertyTableContainer
+     */
+    var clear = function (propertyTableContainer) {
+        var options = propertyTableContainer.data('options');
+        if (options.readOnly === true) {
+            removeAllPropertyDetailDialogs();
+        } else {
+            // clear any existing new property dialogs
+            if (nf.Common.isDefinedAndNotNull(options.newPropertyDialogContainer)) {
+                $(options.newPropertyDialogContainer).children('div.new-property-dialog').hide();
+            }
+        }
+
+        // clean up data
+        var table = propertyTableContainer.find('div.property-table');
+        table.removeData('descriptors history');
+
+        // clean up any tooltips that may have been generated
+        nf.Common.cleanUpTooltips(table, 'img.icon-info');
+
+        // clear the data in the grid
+        var propertyGrid = table.data('gridInstance');
+        var propertyData = propertyGrid.getData();
+        propertyData.setItems([]);
+    };
+
+    var methods = {
+        /**
+         * Initializes the tag cloud.
+         * 
+         * @argument {object} options The options for the tag cloud
+         */
+        init: function (options) {
+            return this.each(function () {
+                // ensure the options have been properly specified
+                if (nf.Common.isDefinedAndNotNull(options)) {
+                    // get the tag cloud
+                    var propertyTableContainer = $(this);
+
+                    // clear any current contents, remote events, and store options
+                    propertyTableContainer.empty().unbind().data('options', options);
+
+                    // build the component
+                    var header = $('<div class="properties-header"></div>').appendTo(propertyTableContainer);
+                    $('<div class="required-property-note">Required field</div>').appendTo(header);
+
+                    // build the table
+                    var table = $('<div class="property-table"></div>').appendTo(propertyTableContainer);
+
+                    // optionally add a add new property button
+                    if (options.readOnly !== true && nf.Common.isDefinedAndNotNull(options.newPropertyDialogContainer)) {
+                        // build the new property dialog
+                        var newPropertyDialogMarkup = 
+                                '<div class="new-property-dialog dialog">' +
+                                    '<div>' +
+                                        '<div class="setting-name">Property name</div>' +
+                                        '<div class="setting-field new-property-name-container">' +
+                                            '<input class="new-property-name" type="text"/>' +
+                                        '</div>' +
+                                    '</div>' +
+                                    '<div class="new-property-button-container">' +
+                                        '<div class="new-property-ok button button-normal">Ok</div>' +
+                                        '<div class="new-property-cancel button button-normal">Cancel</div>' +
+                                        '<div class="clear"></div>' +
+                                    '</div>' +
+                                '</div>';
+
+                        var newPropertyDialog = $(newPropertyDialogMarkup).appendTo(options.newPropertyDialogContainer);
+                        var newPropertyNameField = newPropertyDialog.find('input.new-property-name');
+
+                        var add = function () {
+                            var propertyName = $.trim(newPropertyNameField.val());
+
+                            // ensure the property name and value is specified
+                            if (propertyName !== '') {
+                                // load the property descriptor if possible
+                                if (typeof options.descriptorDeferred === 'function') {
+                                    options.descriptorDeferred(propertyName).done(function(response) {
+                                        var descriptor = response.propertyDescriptor;
+                                        
+                                        // store the descriptor for use later
+                                        var descriptors = table.data('descriptors');
+                                        if (!nf.Common.isUndefined(descriptors)) {
+                                            descriptors[descriptor.name] = descriptor;
+                                        }
+                                    });
+                                }
+                                
+                                // add a row for the new property
+                                var propertyGrid = table.data('gridInstance');
+                                var propertyData = propertyGrid.getData();
+                                propertyData.addItem({
+                                    id: propertyData.getLength(),
+                                    hidden: false,
+                                    property: propertyName,
+                                    displayName: propertyName,
+                                    previousValue: null,
+                                    value: null,
+                                    type: 'userDefined'
+                                });
+                            } else {
+                                nf.Dialog.showOkDialog({
+                                    dialogContent: 'Property name must be specified.',
+                                    overlayBackground: false
+                                });
+                            }
+
+                            // close the dialog
+                            newPropertyDialog.hide();
+                        };
+
+                        var cancel = function () {
+                            newPropertyDialog.hide();
+                        };
+
+                        // make the new property dialog draggable
+                        newPropertyDialog.draggable({
+                            cancel: 'input, textarea, pre, .button, .' + editorClass,
+                            containment: 'body'
+                        }).on('click', 'div.new-property-ok', add).on('click', 'div.new-property-cancel', cancel);
+
+                        // build the control to open the new property dialog
+                        var addProperty = $('<div class="add-property"></div>').appendTo(header);
+                        $('<div class="add-property-icon add-icon-bg"></div>').on('click', function () {
+                            // close all fields currently being edited
+                            saveRow(table);
+
+                            // clear the dialog
+                            newPropertyNameField.val('');
+
+                            // open the new property dialog
+                            newPropertyDialog.center().show();
+
+                            // set the initial focus
+                            newPropertyNameField.focus();
+                        }).on('mouseenter', function () {
+                            $(this).removeClass('add-icon-bg').addClass('add-icon-bg-hover');
+                        }).on('mouseleave', function () {
+                            $(this).removeClass('add-icon-bg-hover').addClass('add-icon-bg');
+                        }).appendTo(addProperty);
+                        $('<div class="add-property-text">New property</div>').appendTo(addProperty);
+                    }
+                    $('<div class="clear"></div>').appendTo(header);
+
+                    // initializes the properties table
+                    initPropertiesTable(table, options);
+                }
+            });
+        },
+        
+        /**
+         * Loads the specified properties.
+         * 
+         * @argument {object} properties        The properties
+         * @argument {map} descriptors          The property descriptors (property name -> property descriptor)
+         * @argument {map} history
+         */
+        loadProperties: function (properties, descriptors, history) {
+            return this.each(function () {
+                var table = $(this).find('div.property-table');
+                loadProperties(table, properties, descriptors, history);
+            });
+        },
+        
+        /**
+         * Saves the last edited row in the specified grid.
+         */
+        saveRow: function () {
+            return this.each(function () {
+                var table = $(this).find('div.property-table');
+                saveRow(table);
+            });
+        },
+        
+        /**
+         * Update the size of the grid based on its container's current size.
+         */
+        resetTableSize: function () {
+            return this.each(function () {
+                var table = $(this).find('div.property-table');
+                var propertyGrid = table.data('gridInstance');
+                if (nf.Common.isDefinedAndNotNull(propertyGrid)) {
+                    propertyGrid.resizeCanvas();
+                }
+            });
+        },
+        
+        /**
+         * Cancels the edit in the specified row.
+         */
+        cancelEdit: function () {
+            return this.each(function () {
+                var table = $(this).find('div.property-table');
+                var propertyGrid = table.data('gridInstance');
+                if (nf.Common.isDefinedAndNotNull(propertyGrid)) {
+                    var editController = propertyGrid.getEditController();
+                    editController.cancelCurrentEdit();
+                }
+            });
+        },
+        
+        /**
+         * Destroys the property table.
+         */
+        destroy: function () {
+            return this.each(function () {
+                var propertyTableContainer = $(this);
+                var options = propertyTableContainer.data('options');
+                
+                // clear the property table container
+                clear(propertyTableContainer);
+                
+                // clear any existing new property dialogs
+                if (nf.Common.isDefinedAndNotNull(options.newPropertyDialogContainer)) {
+                    $(options.newPropertyDialogContainer).children('div.new-property-dialog').remove();
+                }
+            });
+        },
+        
+        /**
+         * Clears the property table.
+         */
+        clear: function () {
+            return this.each(function () {
+                clear($(this));
+            });
+        },
+        
+        /**
+         * Determines if a save is required for the first matching element.
+         */
+        isSaveRequired: function () {
+            var isSaveRequired = false;
+
+            this.each(function () {
+                // get the property grid
+                var table = $(this).find('div.property-table');
+                var propertyGrid = table.data('gridInstance');
+                var propertyData = propertyGrid.getData();
+
+                // determine if any of the properties have changed
+                $.each(propertyData.getItems(), function () {
+                    if (this.value !== this.previousValue) {
+                        isSaveRequired = true;
+                        return false;
+                    }
+                });
+
+                return false;
+            });
+
+            return isSaveRequired;
+        },
+        
+        /**
+         * Marshalls the properties for the first matching element.
+         */
+        marshalProperties: function () {
+            // properties
+            var properties = {};
+
+            this.each(function () {
+                // get the property grid data
+                var table = $(this).find('div.property-table');
+                var propertyGrid = table.data('gridInstance');
+                var propertyData = propertyGrid.getData();
+                $.each(propertyData.getItems(), function () {
+                    if (this.hidden === true) {
+                        // hidden properties were removed by the user, clear the value
+                        properties[this.property] = null;
+                    } else if (this.value !== this.previousValue) {
+                        // the value has changed
+                        properties[this.property] = this.value;
+                    }
+                });
+
+                return false;
+            });
+
+            return properties;
+        }
+    };
+
+    $.fn.propertytable = function (method) {
+        if (methods[method]) {
+            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+        } else {
+            return methods.init.apply(this, arguments);
+        }
+    };
+})(jQuery);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/tabbs/jquery.tabbs.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/tabbs/jquery.tabbs.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/tabbs/jquery.tabbs.js
index 2b2c1e7..e3bbb51 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/tabbs/jquery.tabbs.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/tabbs/jquery.tabbs.js
@@ -44,6 +44,8 @@
      *   }],
      *   select: selectHandler
      * }
+     * 
+     * @argument {object} options 
      */
     $.fn.tabbs = function (options) {
         return this.each(function () {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/tagcloud/jquery.tagcloud.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/tagcloud/jquery.tagcloud.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/tagcloud/jquery.tagcloud.css
new file mode 100644
index 0000000..dda7474
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/tagcloud/jquery.tagcloud.css
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+/*
+    Styles for the Nifi tag cloud.
+*/
+
+ul.tag-cloud {
+    font-size: 90%;
+    padding: 2px;
+    margin-left: -5px;
+    overflow: hidden;
+}
+
+ul.tag-cloud li {
+    float: left;
+    list-style-type: none;
+    margin: 0 3px;
+    height: 20px;
+    line-height: 20px;
+    overflow: hidden;
+    white-space: nowrap;
+}
+
+div.tag-cloud-separator {
+    height: 1px;
+    border-bottom: 1px solid #aaa;
+    margin: 3px 0;
+}
+
+ul.tag-filter {
+    overflow: hidden;
+}
+
+ul.tag-filter li {
+    height: 18px;
+    line-height: 18px;
+    padding: 2px;
+}
+
+div.selected-tag-text {
+    float: left;
+}
+
+img.remove-selected-tag {
+    float: right;
+    margin-top: 1px;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/tagcloud/jquery.tagcloud.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/tagcloud/jquery.tagcloud.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/tagcloud/jquery.tagcloud.js
new file mode 100644
index 0000000..5c45eda
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/tagcloud/jquery.tagcloud.js
@@ -0,0 +1,226 @@
+/*
+ * 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.
+ */
+
+/* global nf */
+
+/**
+ * jQuery plugin for a NiFi style tag cloud. The options are specified in the following
+ * format:
+ *
+ * {
+ *   tags: ['attributes', 'copy', 'regex', 'xml', 'copy', 'xml', 'attributes'],
+ *   select: selectHandler,
+ *   remove: removeHandler
+ * }
+ * 
+ * @param {type} $
+ * @returns {undefined}
+ */
+(function ($) {
+
+    var isUndefined = function (obj) {
+        return typeof obj === 'undefined';
+    };
+
+    var isNull = function (obj) {
+        return obj === null;
+    };
+
+    var isDefinedAndNotNull = function (obj) {
+        return !isUndefined(obj) && !isNull(obj);
+    };
+
+    var isBlank = function (str) {
+        return isUndefined(str) || isNull(str) || str === '';
+    };
+
+    var config = {
+        maxTags: 25,
+        maxTagFontSize: 2,
+        minTagFontSize: 1,
+        minWidth: 20
+    };
+    
+    /**
+     * Adds the specified tag filter.
+     * 
+     * @argument {jQuery} cloudContainer    The tag cloud container
+     * @argument {string} tag               The tag to add
+     */
+    var addTagFilter = function (cloudContainer, tag) {
+        var config = cloudContainer.data('options');
+        var tagFilter = cloudContainer.find('ul.tag-filter');
+        
+        // ensure this tag hasn't already been added
+        var tagFilterExists = false;
+        tagFilter.find('li div.selected-tag-text').each(function () {
+            if (tag === $(this).text()) {
+                tagFilterExists = true;
+                return false;
+            }
+        });
+
+        // add this tag filter if applicable
+        if (!tagFilterExists) {
+            // create the list item content
+            var tagText = $('<div class="selected-tag-text"></div>').text(tag);
+            var removeTagIcon = $('<img src="images/iconDelete.png" class="remove-selected-tag pointer"></img>').click(function () {
+                // remove this tag
+                $(this).closest('li').remove();
+
+                // fire a remove event if applicable
+                if (isDefinedAndNotNull(config.remove)) {
+                    config.remove.call(cloudContainer, tag);
+                }
+            });
+            var selectedTagItem = $('<div></div>').append(tagText).append(removeTagIcon);
+
+            // create the list item and update the tag filter list
+            $('<li></li>').append(selectedTagItem).appendTo(tagFilter);
+
+            // fire a select event if applicable
+            if (isDefinedAndNotNull(config.select)) {
+                config.select.call(cloudContainer, tag);
+            }
+        }
+    };
+
+    var methods = {
+        
+        /**
+         * Initializes the tag cloud.
+         * 
+         * @argument {object} options The options for the tag cloud
+         */
+        init: function (options) {
+            return this.each(function () {
+                // ensure the options have been properly specified
+                if (isDefinedAndNotNull(options) && isDefinedAndNotNull(options.tags)) {
+                    // get the tag cloud
+                    var cloudContainer = $(this);
+                    
+                    // clear any current contents, remote events, and store options
+                    cloudContainer.empty().unbind().data('options', options);
+
+                    // build the component
+                    var cloud = $('<ul class="tag-cloud"></ul>').appendTo(cloudContainer);
+                    $('<div class="tag-cloud-separator">').appendTo(cloudContainer);
+                    var filter = $('<ul class="tag-filter"></ul>').appendTo(cloudContainer);
+
+                    var tagCloud = {};
+                    var tags = [];
+                    
+                    // count the frequency of each tag for this type
+                    $.each(options.tags, function (i, tag) {
+                        var normalizedTagName = tag.toLowerCase();
+
+                        if (nf.Common.isDefinedAndNotNull(tagCloud[normalizedTagName])) {
+                            tagCloud[normalizedTagName].count = tagCloud[normalizedTagName].count + 1;
+                        } else {
+                            var tagCloudEntry = {
+                                term: normalizedTagName,
+                                count: 1
+                            };
+                            tags.push(tagCloudEntry);
+                            tagCloud[normalizedTagName] = tagCloudEntry;
+                        }
+                    });
+                    
+                    // handle the case when no tags are present
+                    if (tags.length > 0) {
+                        // sort the tags by frequency to limit the less frequent tags
+                        tags.sort(function (a, b) {
+                            return b.count - a.count;
+                        });
+
+                        // limit to the most frequest tags
+                        if (tags.length > config.maxTags) {
+                            tags = tags.slice(0, config.maxTags);
+                        }
+
+                        // determine the max frequency
+                        var maxFrequency = tags[0].count;
+
+                        // sort the tags alphabetically
+                        tags.sort(function (a, b) {
+                            var compA = a.term.toUpperCase();
+                            var compB = b.term.toUpperCase();
+                            return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
+                        });
+
+                        // set the tag content
+                        $.each(tags, function (i, tag) {
+                            // determine the appropriate font size
+                            var fontSize = Math.log(tag.count) / Math.log(maxFrequency) * (config.maxTagFontSize - config.minTagFontSize) + config.minTagFontSize;
+                            var minWidth = config.minWidth * fontSize;
+
+                            // create the tag cloud entry
+                            $('<li></li>').append($('<span class="link"></span>').text(tag.term).css({
+                                'font-size': fontSize + 'em'
+                            })).css({
+                                'min-width': minWidth + 'px'
+                            }).click(function () {
+                                // ensure we don't exceed 5 selected
+                                if (filter.children('li').length < 5) {
+                                    var tagText = $(this).children('span').text();
+                                    addTagFilter(cloudContainer, tagText);
+                                }
+                            }).appendTo(cloud).ellipsis();
+                        });
+                    } else {
+                        // indicate when no tags are found
+                        $('<li><span class="unset">No tags specified</span></li>').appendTo(cloud);
+                    }
+                }
+            });
+        },
+        
+        /**
+         * Resets the selected tags from the tag cloud.
+         */
+        clearSelectedTags: function () {
+            return this.each(function() {
+                var cloudContainer = $(this);
+                cloudContainer.find('img.remove-selected-tag').trigger('click');
+            });
+        },
+        
+        /**
+         * Returns the selected tags of the first matching element.
+         */
+        getSelectedTags: function () {
+            var tags = [];
+
+            this.each(function () {
+                var cloudContainer = $(this);
+                cloudContainer.find('div.selected-tag-text').each(function() {
+                    tags.push($(this).text());
+                });
+            });
+
+            return tags;
+        }
+    };
+
+    $.fn.tagcloud = function (method) {
+        if (methods[method]) {
+            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+        } else {
+            return methods.init.apply(this, arguments);
+        }
+    };
+})(jQuery);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/bulletin-board/nf-bulletin-board.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/bulletin-board/nf-bulletin-board.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/bulletin-board/nf-bulletin-board.js
index f181016..85e52b0 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/bulletin-board/nf-bulletin-board.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/bulletin-board/nf-bulletin-board.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, top */
+
 $(document).ready(function () {
     // initialize the bulletin board
     nf.BulletinBoard.init();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
index 880d767..47532ad 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.Actions = (function () {
 
     var config = {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-birdseye.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-birdseye.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-birdseye.js
index b317a6f..01abd31 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-birdseye.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-birdseye.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.Birdseye = (function () {
 
     var birdseyeGroup;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js
index 335b145..ff5d2f2 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-header.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.CanvasHeader = (function () {
 
     var config = {
@@ -58,13 +61,11 @@ nf.CanvasHeader = (function () {
             });
 
             // mouse over for the flow settings link
-            if (nf.Common.isDFM()) {
-                nf.Common.addHoverEffect('#flow-settings-link', 'flow-settings-link', 'flow-settings-link-hover').click(function () {
+            nf.Common.addHoverEffect('#flow-settings-link', 'flow-settings-link', 'flow-settings-link-hover').click(function () {
+                nf.Settings.loadSettings().done(function () {
                     nf.Settings.showSettings();
                 });
-            } else {
-                $('#flow-settings-link').addClass('flow-settings-link-disabled');
-            }
+            });
 
             // mouse over for the users link
             if (nf.Common.isAdmin()) {
@@ -96,29 +97,9 @@ nf.CanvasHeader = (function () {
                 nf.Shell.showPage('bulletin-board');
             });
 
-            // setup the tooltip for the refresh icon
-            $('#refresh-required-icon').qtip($.extend({
-                content: 'This flow has been modified by another user. Please refresh.'
-            }, nf.CanvasUtils.config.systemTooltipConfig));
-
             // setup the refresh link actions
             $('#refresh-required-link').click(function () {
-                nf.Canvas.reload().done(function () {
-                    // update component visibility
-                    nf.Canvas.View.updateVisibility();
-
-                    // refresh the birdseye
-                    nf.Birdseye.refresh();
-
-                    // hide the refresh link
-                    $('#stats-last-refreshed').removeClass('alert');
-                    $('#refresh-required-container').hide();
-                }).fail(function () {
-                    nf.Dialog.showOkDialog({
-                        dialogContent: 'Unable to refresh the current group.',
-                        overlayBackground: true
-                    });
-                });
+                nf.CanvasHeader.reloadAndClearWarnings();
             });
 
             // get the about details
@@ -235,6 +216,9 @@ nf.CanvasHeader = (function () {
                         $('#fill-color').minicolors('value', '');
                     }
                 }
+            }).draggable({
+                containment: 'parent',
+                handle: '.dialog-header'
             });
 
             // initialize the fill color picker
@@ -299,7 +283,7 @@ nf.CanvasHeader = (function () {
                     var delta = 0;
                     if (nf.Common.isDefinedAndNotNull(evt.originalEvent.detail)) {
                         delta = -evt.originalEvent.detail;
-                    } else if (nf.Common.isDefinedAndNotNull(evnt.originalEvent.wheelDelta)) {
+                    } else if (nf.Common.isDefinedAndNotNull(evt.originalEvent.wheelDelta)) {
                         delta = evt.originalEvent.wheelDelta;
                     }
 
@@ -324,6 +308,32 @@ nf.CanvasHeader = (function () {
                     }
                 }
             });
+        },
+        
+        /**
+         * Reloads and clears any warnings.
+         */
+        reloadAndClearWarnings: function () {
+            nf.Canvas.reload().done(function () {
+                // update component visibility
+                nf.Canvas.View.updateVisibility();
+
+                // refresh the birdseye
+                nf.Birdseye.refresh();
+
+                // hide the refresh link on the canvas
+                $('#stats-last-refreshed').removeClass('alert');
+                $('#refresh-required-container').hide();
+                
+                // hide the refresh link on the settings
+                $('#settings-last-refreshed').removeClass('alert');
+                $('#settings-refresh-required-icon').hide();
+            }).fail(function () {
+                nf.Dialog.showOkDialog({
+                    dialogContent: 'Unable to refresh the current group.',
+                    overlayBackground: true
+                });
+            });
         }
     };
 }());
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-toolbar.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-toolbar.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-toolbar.js
index b5702af..8347f37 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-toolbar.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-toolbar.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.CanvasToolbar = (function () {
 
     var actions;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-toolbox.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-toolbox.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-toolbox.js
index 7de17f4..2812142 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-toolbox.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-toolbox.js
@@ -14,16 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, Slick */
+
 nf.CanvasToolbox = (function () {
 
     var config = {
-        /**
-         * Tag properties configuration.
-         */
-        maxTags: 25,
-        maxTagFontSize: 2,
-        minTagFontSize: 1,
-        minWidth: 20,
         filterText: 'Filter',
         type: {
             processor: 'Processor',
@@ -71,7 +67,7 @@ nf.CanvasToolbox = (function () {
                 return $('<div class="toolbox-icon"></div>').addClass(dragCls).appendTo('body');
             },
             'containment': 'body',
-            'start': function(e, ui) {
+            'start': function (e, ui) {
                 // hide the context menu if necessary
                 nf.ContextMenu.hide();
             },
@@ -92,49 +88,12 @@ nf.CanvasToolbox = (function () {
                         x: x,
                         y: y
                     });
-                } 
+                }
             }
         }).appendTo(toolbox);
     };
 
     /**
-     * Adds the specified tag filter.
-     * 
-     * @argument {string} tag       The tag to add
-     */
-    var addTagFilter = function (tag) {
-        // ensure this tag hasn't already been added
-        var tagFilter = $('#tag-filter');
-        var tagFilterExists = false;
-        tagFilter.find('li div.selected-tag-text').each(function () {
-            if (tag === $(this).text()) {
-                tagFilterExists = true;
-                return false;
-            }
-        });
-
-        // add this tag filter if applicable
-        if (!tagFilterExists) {
-            // create the list item content
-            var tagText = $('<div class="selected-tag-text"></div>').text(tag);
-            var removeTagIcon = $('<img src="images/iconDelete.png" class="remove-selected-tag pointer"></img>').click(function () {
-                // remove this tag
-                $(this).closest('li').remove();
-
-                // re-apply the filter
-                applyFilter();
-            });
-            var selectedTagItem = $('<div></div>').append(tagText).append(removeTagIcon);
-
-            // create the list item and update the tag filter list
-            $('<li></li>').append(selectedTagItem).appendTo(tagFilter);
-
-            // re-apply the filter
-            applyFilter();
-        }
-    };
-
-    /**
      * Filters the processor type table.
      */
     var applyFilter = function () {
@@ -192,7 +151,7 @@ nf.CanvasToolbox = (function () {
         // determine if the row matches the selected tags
         var matchesTags = true;
         if (matchesFilter) {
-            var tagFilters = $('#tag-filter li');
+            var tagFilters = $('#processor-tag-cloud').tagcloud('getSelectedTags');
             var hasSelectedTags = tagFilters.length > 0;
             if (hasSelectedTags) {
                 matchesTags = matchesSelectedTags(tagFilters, item['tags']);
@@ -221,13 +180,13 @@ nf.CanvasToolbox = (function () {
     /**
      * Determines if the specified tags match all the tags selected by the user.
      * 
-     * @argument {jQuery} tagFilters    The tag filters
-     * @argument {string} tags          The tags to test
+     * @argument {string[]} tagFilters      The tag filters
+     * @argument {string} tags              The tags to test
      */
     var matchesSelectedTags = function (tagFilters, tags) {
         var selectedTags = [];
-        tagFilters.each(function () {
-            selectedTags.push($(this).text());
+        $.each(tagFilters, function (_, filter) {
+            selectedTags.push(filter);
         });
 
         // normalize the tags
@@ -280,14 +239,13 @@ nf.CanvasToolbox = (function () {
      * Resets the filtered processor types.
      */
     var resetProcessorDialog = function () {
-        // clear and selected tags
-        var tagFilter = $('#tag-filter');
-        tagFilter.empty();
-
+        // clear the selected tag cloud
+        $('#processor-tag-cloud').tagcloud('clearSelectedTags');
+        
         // clear any filter strings
         $('#processor-type-filter').addClass(config.styles.filterList).val(config.filterText);
 
-        // reset the filter before closing
+        // reapply the filter
         applyFilter();
 
         // clear the selected row
@@ -295,7 +253,7 @@ nf.CanvasToolbox = (function () {
         $('#processor-type-name').text('');
         $('#selected-processor-name').text('');
         $('#selected-processor-type').text('');
-
+        
         // unselect any current selection
         var processTypesGrid = $('#processor-types-table').data('gridInstance');
         processTypesGrid.setSelectedRows([]);
@@ -987,6 +945,20 @@ nf.CanvasToolbox = (function () {
                     }
                 });
 
+                // wire up the dataview to the grid
+                processorTypesData.onRowCountChanged.subscribe(function (e, args) {
+                    processorTypesGrid.updateRowCount();
+                    processorTypesGrid.render();
+
+                    // update the total number of displayed processors
+                    $('#displayed-processor-types').text(args.current);
+                });
+                processorTypesData.onRowsChanged.subscribe(function (e, args) {
+                    processorTypesGrid.invalidateRows(args.rows);
+                    processorTypesGrid.render();
+                });
+                processorTypesData.syncGridSelection(processorTypesGrid, false);
+
                 // hold onto an instance of the grid
                 $('#processor-types-table').data('gridInstance', processorTypesGrid);
 
@@ -997,7 +969,6 @@ nf.CanvasToolbox = (function () {
                     url: config.urls.processorTypes,
                     dataType: 'json'
                 }).done(function (response) {
-                    var tagCloud = {};
                     var tags = [];
 
                     // begin the update
@@ -1016,21 +987,9 @@ nf.CanvasToolbox = (function () {
                             tags: documentedType.tags.join(', ')
                         });
 
-
                         // count the frequency of each tag for this type
                         $.each(documentedType.tags, function (i, tag) {
-                            var normalizedTagName = tag.toLowerCase();
-
-                            if (nf.Common.isDefinedAndNotNull(tagCloud[normalizedTagName])) {
-                                tagCloud[normalizedTagName].count = tagCloud[normalizedTagName].count + 1;
-                            } else {
-                                var tagCloudEntry = {
-                                    term: normalizedTagName,
-                                    count: 1
-                                };
-                                tags.push(tagCloudEntry);
-                                tagCloud[normalizedTagName] = tagCloudEntry;
-                            }
+                            tags.push(tag.toLowerCase());
                         });
                     });
 
@@ -1040,65 +999,12 @@ nf.CanvasToolbox = (function () {
                     // set the total number of processors
                     $('#total-processor-types, #displayed-processor-types').text(response.processorTypes.length);
 
-                    // handle the case when no tags are present
-                    if (tags.length > 0) {
-                        // sort the tags by frequency to limit the less frequent tags
-                        tags.sort(function (a, b) {
-                            return b.count - a.count;
-                        });
-
-                        // limit to the most frequest tags
-                        if (tags.length > config.maxTags) {
-                            tags = tags.slice(0, config.maxTags);
-                        }
-
-                        // determine the max frequency
-                        var maxFrequency = tags[0].count;
-
-                        // sort the tags alphabetically
-                        tags.sort(function (a, b) {
-                            var compA = a.term.toUpperCase();
-                            var compB = b.term.toUpperCase();
-                            return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
-                        });
-
-                        // set the tag content
-                        $.each(tags, function (i, tag) {
-                            // determine the appropriate font size
-                            var fontSize = Math.log(tag.count) / Math.log(maxFrequency) * (config.maxTagFontSize - config.minTagFontSize) + config.minTagFontSize;
-                            var minWidth = config.minWidth * fontSize;
-
-                            // create the tag cloud entry
-                            $('<li></li>').append($('<span class="link"></span>').text(tag.term).css({
-                                'font-size': fontSize + 'em'
-                            })).css({
-                                'min-width': minWidth + 'px'
-                            }).click(function () {
-                                // ensure we don't exceed 5 selected
-                                if ($('#tag-filter').children('li').length < 5) {
-                                    var tagText = $(this).children('span').text();
-                                    addTagFilter(tagText);
-                                }
-                            }).appendTo('#tag-cloud').ellipsis();
-                        });
-                    } else {
-                        // indicate when no tags are found
-                        $('<li><span class="unset">No tags specified</span></li>').appendTo('#tag-cloud');
-                    }
-
-                    // wire up the dataview to the grid
-                    processorTypesData.onRowCountChanged.subscribe(function (e, args) {
-                        processorTypesGrid.updateRowCount();
-                        processorTypesGrid.render();
-
-                        // update the total number of displayed processors
-                        $('#displayed-processor-types').text(args.current);
-                    });
-                    processorTypesData.onRowsChanged.subscribe(function (e, args) {
-                        processorTypesGrid.invalidateRows(args.rows);
-                        processorTypesGrid.render();
+                    // create the tag cloud
+                    $('#processor-tag-cloud').tagcloud({
+                        tags: tags,
+                        select: applyFilter,
+                        remove: applyFilter
                     });
-                    processorTypesData.syncGridSelection(processorTypesGrid, false);
                 }).fail(nf.Common.handleAjaxError);
 
                 // define the function for filtering the list

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
index 720a23e..f1cb458 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.CanvasUtils = (function () {
 
     var config = {


[28/62] [abbrv] incubator-nifi git commit: NIFI-477: - Updating the property details shown in a read only dialog for properties with allowable values. Now rendering a read only combo to show the value's display name and the other available options.

Posted by ma...@apache.org.
NIFI-477:
- Updating the property details shown in a read only dialog for properties with allowable values. Now rendering a read only combo to show the value's display name and the other available options.

Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/e2878170
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/e2878170
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/e2878170

Branch: refs/heads/NIFI-25
Commit: e2878170a1ddf680800409134684a62bc5ea0373
Parents: 30fc75e
Author: Matt Gilman <ma...@gmail.com>
Authored: Wed Apr 1 11:15:07 2015 -0400
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Apr 1 11:15:07 2015 -0400

----------------------------------------------------------------------
 .../propertytable/jquery.propertytable.js       | 155 ++++++++++++-------
 .../main/webapp/js/nf/canvas/nf-canvas-utils.js |   9 --
 .../src/main/webapp/js/nf/canvas/nf-canvas.js   |   2 +-
 .../js/nf/canvas/nf-controller-service.js       |   3 +
 .../js/nf/canvas/nf-processor-configuration.js  |   3 +
 .../webapp/js/nf/canvas/nf-reporting-task.js    |   3 +
 .../src/main/webapp/js/nf/nf-common.js          |   9 ++
 .../main/webapp/js/nf/nf-processor-details.js   |   3 +
 8 files changed, 125 insertions(+), 62 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e2878170/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
index c2672f3..31495bb 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.js
@@ -598,7 +598,7 @@
      */
     var showPropertyValue = function (propertyGrid, descriptors, row, cell) {
         // remove any currently open detail dialogs
-        nf.CanvasUtils.removeAllPropertyDetailDialogs();
+        nf.Common.removeAllPropertyDetailDialogs();
 
         // get the property in question
         var propertyData = propertyGrid.getData();
@@ -619,7 +619,7 @@
 
                 // create the wrapper
                 var wrapper = $('<div class="property-detail"></div>').css({
-                    'z-index': 100000,
+                    'z-index': 1999,
                     'position': 'absolute',
                     'background': 'white',
                     'padding': '5px',
@@ -631,65 +631,116 @@
                     'left': offset.left - 5
                 }).appendTo('body');
 
-                var editor = null;
-
-                // so the nfel editor is appropriate
-                if (nf.Common.supportsEl(propertyDescriptor)) {
-                    var languageId = 'nfel';
-                    var editorClass = languageId + '-editor';
-
-                    // prevent dragging over the nf editor
+                var allowableValues = nf.Common.getAllowableValues(propertyDescriptor);
+                if ($.isArray(allowableValues)) {
+                    // prevent dragging over the combo
                     wrapper.draggable({
-                        cancel: 'input, textarea, pre, .button, .' + editorClass,
+                        cancel: '.button, .combo',
                         containment: 'parent'
                     });
 
-                    // create the editor
-                    editor = $('<div></div>').addClass(editorClass).appendTo(wrapper).nfeditor({
-                        languageId: languageId,
-                        width: cellNode.width(),
-                        content: property.value,
-                        minWidth: 175,
-                        minHeight: 100,
-                        readOnly: true,
-                        resizable: true
+                    // create the read only options
+                    var options = [];
+                    $.each(allowableValues, function (i, allowableValue) {
+                        options.push({
+                            text: allowableValue.displayName,
+                            value: allowableValue.value,
+                            description: nf.Common.escapeHtml(allowableValue.description),
+                            disabled: true
+                        });
                     });
+
+                    // ensure the options there is at least one option
+                    if (options.length === 0) {
+                        options.push({
+                            text: 'No value',
+                            value: null,
+                            optionClass: 'unset',
+                            disabled: true
+                        });
+                    }
+
+                    // determine the max height
+                    var windowHeight = $(window).height();
+                    var maxHeight = windowHeight - (offset.top + cellNode.height()) - 16;
+                    var width = cellNode.width() - 16;
+
+                    // build the combo field
+                    $('<div class="value-combo combo"></div>').width(width).combo({
+                        options: options,
+                        maxHeight: maxHeight,
+                        selectedOption: {
+                            value: property.value
+                        }
+                    }).appendTo(wrapper);
+                    
+                    $('<div class="button button-normal">Ok</div>').css({
+                        'margin': '0 0 0 5px',
+                        'float': 'left'
+                    }).on('click', function () {
+                        wrapper.hide().remove();
+                    }).appendTo(wrapper);
                 } else {
-                    // prevent dragging over standard components
-                    wrapper.draggable({
-                        containment: 'parent'
-                    });
+                    var editor = null;
 
-                    // create the input field
-                    $('<textarea hidefocus rows="5" readonly="readonly"/>').css({
-                        'background': 'white',
-                        'width': cellNode.width() + 'px',
-                        'height': '80px',
-                        'border-width': '0',
-                        'outline': '0',
-                        'overflow-y': 'auto',
-                        'resize': 'both',
-                        'margin-bottom': '28px'
-                    }).text(property.value).appendTo(wrapper);
-                }
+                    // so the nfel editor is appropriate
+                    if (nf.Common.supportsEl(propertyDescriptor)) {
+                        var languageId = 'nfel';
+                        var editorClass = languageId + '-editor';
+
+                        // prevent dragging over the nf editor
+                        wrapper.draggable({
+                            cancel: 'input, textarea, pre, .button, .' + editorClass,
+                            containment: 'parent'
+                        });
+
+                        // create the editor
+                        editor = $('<div></div>').addClass(editorClass).appendTo(wrapper).nfeditor({
+                            languageId: languageId,
+                            width: cellNode.width(),
+                            content: property.value,
+                            minWidth: 175,
+                            minHeight: 100,
+                            readOnly: true,
+                            resizable: true
+                        });
+                    } else {
+                        // prevent dragging over standard components
+                        wrapper.draggable({
+                            containment: 'parent'
+                        });
 
-                // add an ok button that will remove the entire pop up
-                var ok = $('<div class="button button-normal">Ok</div>').on('click', function () {
-                    // clean up the editor
-                    if (editor !== null) {
-                        editor.nfeditor('destroy');
+                        // create the input field
+                        $('<textarea hidefocus rows="5" readonly="readonly"/>').css({
+                            'background': 'white',
+                            'width': cellNode.width() + 'px',
+                            'height': '80px',
+                            'border-width': '0',
+                            'outline': '0',
+                            'overflow-y': 'auto',
+                            'resize': 'both',
+                            'margin-bottom': '28px'
+                        }).text(property.value).appendTo(wrapper);
                     }
 
-                    // clean up the rest
-                    wrapper.hide().remove();
-                });
-                $('<div></div>').css({
-                    'position': 'absolute',
-                    'bottom': '0',
-                    'left': '0',
-                    'right': '0',
-                    'padding': '0 3px 5px'
-                }).append(ok).append('<div class="clear"></div>').appendTo(wrapper);
+                    // add an ok button that will remove the entire pop up
+                    var ok = $('<div class="button button-normal">Ok</div>').on('click', function () {
+                        // clean up the editor
+                        if (editor !== null) {
+                            editor.nfeditor('destroy');
+                        }
+
+                        // clean up the rest
+                        wrapper.hide().remove();
+                    });
+                    $('<div></div>').css({
+                        'position': 'absolute',
+                        'bottom': '0',
+                        'left': '0',
+                        'right': '0',
+                        'padding': '0 3px 5px'
+                    }).append(ok).append('<div class="clear"></div>').appendTo(wrapper);
+                }
             }
         }
     };
@@ -1022,7 +1073,7 @@
     var clear = function (propertyTableContainer) {
         var options = propertyTableContainer.data('options');
         if (options.readOnly === true) {
-            nf.CanvasUtils.removeAllPropertyDetailDialogs();
+            nf.Common.removeAllPropertyDetailDialogs();
         } else {
             // clear any existing new property dialogs
             if (nf.Common.isDefinedAndNotNull(options.newPropertyDialogContainer)) {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e2878170/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
index 9460fa6..f1cb458 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-utils.js
@@ -871,15 +871,6 @@ nf.CanvasUtils = (function () {
         },
         
         /**
-         * Removes all read only property detail dialogs.
-         */
-        removeAllPropertyDetailDialogs: function () {
-            var propertyDetails = $('body').children('div.property-detail');
-            propertyDetails.find('div.nfel-editor').nfeditor('destroy');
-            propertyDetails.hide().remove();
-        },
-        
-        /**
          * Persists the current user view.
          */
         persistUserView: function () {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e2878170/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
index 82f455b..6fda984 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
@@ -549,7 +549,7 @@ nf.Canvas = (function () {
 
                 // first consider read only property detail dialog
                 if ($('div.property-detail').is(':visible')) {
-                    nf.CanvasUtils.removeAllPropertyDetailDialogs();
+                    nf.Common.removeAllPropertyDetailDialogs();
                     
                     // prevent further bubbling as we're already handled it
                     evt.stopPropagation();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e2878170/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
index 7fb3eba..26b7253 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
@@ -1126,6 +1126,9 @@ nf.ControllerService = (function () {
                         tabContentId: 'controller-service-comments-tab-content'
                     }],
                 select: function () {
+                    // remove all property detail dialogs
+                    nf.Common.removeAllPropertyDetailDialogs();
+                    
                     // update the property table size in case this is the first time its rendered
                     if ($(this).text() === 'Properties') {
                         $('#controller-service-properties').propertytable('resetTableSize');

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e2878170/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js
index 329afc1..4e828df 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js
@@ -404,6 +404,9 @@ nf.ProcessorConfiguration = (function () {
                         tabContentId: 'processor-comments-tab-content'
                     }],
                 select: function () {
+                    // remove all property detail dialogs
+                    nf.Common.removeAllPropertyDetailDialogs();
+                    
                     // update the processor property table size in case this is the first time its rendered
                     if ($(this).text() === 'Properties') {
                         $('#processor-properties').propertytable('resetTableSize');

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e2878170/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-reporting-task.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-reporting-task.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-reporting-task.js
index 7c95388..ed13f10 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-reporting-task.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-reporting-task.js
@@ -243,6 +243,9 @@ nf.ReportingTask = (function () {
                         tabContentId: 'reporting-task-comments-tab-content'
                     }],
                 select: function () {
+                    // remove all property detail dialogs
+                    nf.Common.removeAllPropertyDetailDialogs();
+                    
                     // update the property table size in case this is the first time its rendered
                     if ($(this).text() === 'Properties') {
                         $('#reporting-task-properties').propertytable('resetTableSize');

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e2878170/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
index b818e64..2a5273d 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
@@ -316,6 +316,15 @@ nf.Common = {
     },
     
     /**
+     * Removes all read only property detail dialogs.
+     */
+    removeAllPropertyDetailDialogs: function () {
+        var propertyDetails = $('body').children('div.property-detail');
+        propertyDetails.find('div.nfel-editor').nfeditor('destroy');
+        propertyDetails.hide().remove();
+    },
+    
+    /**
      * Formats the tooltip for the specified property.
      * 
      * @param {object} propertyDescriptor      The property descriptor

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e2878170/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js
index 76d9e53..13eed45 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js
@@ -69,6 +69,9 @@ nf.ProcessorDetails = (function () {
                         tabContentId: 'details-processor-comments-tab-content'
                     }],
                 select: function () {
+                    // remove all property detail dialogs
+                    nf.Common.removeAllPropertyDetailDialogs();
+                    
                     // resize the property grid in case this is the first time its rendered
                     if ($(this).text() === 'Properties') {
                         $('#read-only-processor-properties').propertytable('resetTableSize');


[50/62] [abbrv] incubator-nifi git commit: NIFI-506: Initial import of HL7 work

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/main/java/org/apache/nifi/processors/hl7/ExtractHL7Attributes.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/main/java/org/apache/nifi/processors/hl7/ExtractHL7Attributes.java b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/main/java/org/apache/nifi/processors/hl7/ExtractHL7Attributes.java
new file mode 100644
index 0000000..e4a0d53
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/main/java/org/apache/nifi/processors/hl7/ExtractHL7Attributes.java
@@ -0,0 +1,247 @@
+/*
+ * 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.processors.hl7;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.SupportsBatching;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.InputStreamCallback;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.stream.io.StreamUtils;
+
+import ca.uhn.hl7v2.DefaultHapiContext;
+import ca.uhn.hl7v2.HL7Exception;
+import ca.uhn.hl7v2.HapiContext;
+import ca.uhn.hl7v2.model.Composite;
+import ca.uhn.hl7v2.model.Group;
+import ca.uhn.hl7v2.model.Message;
+import ca.uhn.hl7v2.model.Primitive;
+import ca.uhn.hl7v2.model.Segment;
+import ca.uhn.hl7v2.model.Structure;
+import ca.uhn.hl7v2.model.Type;
+import ca.uhn.hl7v2.model.Varies;
+import ca.uhn.hl7v2.parser.PipeParser;
+import ca.uhn.hl7v2.validation.impl.ValidationContextFactory;
+
+
+@SideEffectFree
+@SupportsBatching
+@Tags({"HL7", "health level 7", "healthcare", "extract", "attributes"})
+@CapabilityDescription("Extracts information from an HL7 (Health Level 7) formatted FlowFile and adds the information as FlowFile Attributes. "
+		+ "The attributes are named as <Segment Name> <dot> <Field Index>. If the segment is repeating, the naming will be "
+		+ "<Segment Name> <underscore> <Segment Index> <dot> <Field Index>. For example, we may have an attribute named \"MHS.12\" with "
+		+ "a value of \"2.1\" and an attribute named \"OBX_11.3\" with a value of \"93000^CPT4\".")
+public class ExtractHL7Attributes extends AbstractProcessor {
+	public static final PropertyDescriptor CHARACTER_SET = new PropertyDescriptor.Builder()
+		.name("Character Encoding")
+		.description("The Character Encoding that is used to encode the HL7 data")
+		.required(true)
+		.expressionLanguageSupported(true)
+		.addValidator(StandardValidators.CHARACTER_SET_VALIDATOR)
+		.defaultValue("UTF-8")
+		.build();
+
+	public static final Relationship REL_SUCCESS = new Relationship.Builder()
+		.name("success")
+		.description("A FlowFile is routed to this relationship if it is properly parsed as HL7 and its attributes extracted")
+		.build();
+	
+	public static final Relationship REL_FAILURE = new Relationship.Builder()
+		.name("failure")
+		.description("A FlowFile is routed to this relationship if it cannot be mapped to FlowFile Attributes. This would happen if the FlowFile does not contain valid HL7 data")
+		.build();
+	
+	
+	@Override
+	protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+		final List<PropertyDescriptor> properties = new ArrayList<>();
+		properties.add(CHARACTER_SET);
+		return properties;
+	}
+	
+	@Override
+	public Set<Relationship> getRelationships() {
+		final Set<Relationship> relationships = new HashSet<>();
+		relationships.add(REL_SUCCESS);
+		relationships.add(REL_FAILURE);
+		return relationships;
+	}
+	
+	@Override
+	public void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException {
+		FlowFile flowFile = session.get();
+		if ( flowFile == null ) {
+			return;
+		}
+		
+		final Charset charset = Charset.forName(context.getProperty(CHARACTER_SET).evaluateAttributeExpressions(flowFile).getValue());
+		
+		final byte[] buffer = new byte[(int) flowFile.getSize()];
+		session.read(flowFile, new InputStreamCallback() {
+			@Override
+			public void process(final InputStream in) throws IOException {
+				StreamUtils.fillBuffer(in, buffer);
+			}
+		});
+
+		@SuppressWarnings("resource")
+		final HapiContext hapiContext = new DefaultHapiContext();
+		hapiContext.setValidationContext(ValidationContextFactory.noValidation());
+		
+		final PipeParser parser = hapiContext.getPipeParser();
+		final String hl7Text = new String(buffer, charset);
+		final Message message;
+		try {
+			message = parser.parse(hl7Text);
+			final Group group = message.getParent();
+
+			final Map<String, String> attributes = new HashMap<>();
+			extractAttributes(group, attributes);
+			flowFile = session.putAllAttributes(flowFile, attributes);
+			getLogger().info("Successfully extracted {} attributes for {}; routing to success", new Object[] {attributes.size(), flowFile});
+			getLogger().debug("Added the following attributes for {}: {}", new Object[] {flowFile, attributes});
+			session.transfer(flowFile, REL_SUCCESS);
+		} catch (final HL7Exception e) {
+			getLogger().error("Failed to extract attributes from {} due to {}", new Object[] {flowFile, e});
+			session.transfer(flowFile, REL_FAILURE);
+			return;
+		}
+	}
+
+	private void extractAttributes(final Group group, final Map<String, String> attributes) throws HL7Exception {
+		extractAttributes(group, attributes, new HashMap<String, Integer>());
+	}
+	
+	private void extractAttributes(final Group group, final Map<String, String> attributes, final Map<String, Integer> segmentCounts) throws HL7Exception {
+		if ( group.isEmpty() ) {
+			return;
+		}
+		
+		final String[] structureNames = group.getNames();
+		for ( final String structName : structureNames ) {
+			final Structure[] subStructures = group.getAll(structName);
+
+			if ( group.isGroup(structName) ) {
+				for ( final Structure subStructure : subStructures ) {
+					final Group subGroup = (Group) subStructure;
+					extractAttributes(subGroup, attributes, segmentCounts);
+				}
+			} else {
+				for ( final Structure structure : subStructures ) {
+					final Segment segment = (Segment) structure	;
+					
+					final String segmentName = segment.getName();
+					Integer segmentNum = segmentCounts.get(segmentName);
+					if (segmentNum == null) {
+						segmentNum = 1;
+						segmentCounts.put(segmentName, 1);
+					} else {
+						segmentNum++;
+						segmentCounts.put(segmentName, segmentNum);
+					}
+					
+					final boolean segmentRepeating = segment.getParent().isRepeating(segment.getName());
+					final boolean parentRepeating = (segment.getParent().getParent() != segment.getParent() && segment.getParent().getParent().isRepeating(segment.getParent().getName()));
+					final boolean useSegmentIndex = segmentRepeating || parentRepeating;
+					
+					final Map<String, String> attributeMap = getAttributes(segment, useSegmentIndex ? segmentNum : null);
+					attributes.putAll(attributeMap);
+				}
+			}
+		}
+	}
+	
+	
+	private Map<String, String> getAttributes(final Segment segment, final Integer segmentNum) throws HL7Exception {
+		final Map<String, String> attributes = new HashMap<>();
+		
+		for (int i=1; i <= segment.numFields(); i++) {
+			final String fieldName = segment.getName() + (segmentNum == null ? "" : "_" + segmentNum) + "." + i;
+			final Type[] types = segment.getField(i);
+			final StringBuilder sb = new StringBuilder();
+			for ( final Type type : types ) {
+				final String typeValue = getValue(type);
+				if ( !typeValue.isEmpty() ) {
+					sb.append(typeValue).append("^");
+				}
+			}
+			
+			if ( sb.length() == 0 ) {
+				continue;
+			}
+			String typeVal = sb.toString();
+			if ( typeVal.endsWith("^") ) {
+				typeVal = typeVal.substring(0, typeVal.length() - 1);
+			}
+			
+			attributes.put(fieldName, typeVal);
+		}
+
+		return attributes;
+	}
+	
+	
+	private String getValue(final Type type) {
+		if ( type == null ) {
+			return "";
+		}
+		
+		if ( type instanceof Primitive ) {
+			final String value = ((Primitive) type).getValue();
+			return value == null ? "" : value;
+		} else if ( type instanceof Composite ) {
+			final StringBuilder sb = new StringBuilder();
+			final Composite composite = (Composite) type;
+			for ( final Type component : composite.getComponents() ) {
+				final String componentValue = getValue(component);
+				if ( !componentValue.isEmpty() ) {
+					sb.append(componentValue).append("^");
+				}
+			}
+			
+			final String value = sb.toString();
+			if ( value.endsWith("^") ) {
+				return value.substring(0, value.length() - 1);
+			}
+			
+			return value;
+		} else if ( type instanceof Varies ) {
+			final Varies varies = (Varies) type;
+			return getValue(varies.getData());
+		}
+		
+		return "";
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/main/java/org/apache/nifi/processors/hl7/RouteHL7.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/main/java/org/apache/nifi/processors/hl7/RouteHL7.java b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/main/java/org/apache/nifi/processors/hl7/RouteHL7.java
new file mode 100644
index 0000000..c8c4176
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/main/java/org/apache/nifi/processors/hl7/RouteHL7.java
@@ -0,0 +1,217 @@
+/*
+ * 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.processors.hl7;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.nifi.annotation.behavior.DynamicProperties;
+import org.apache.nifi.annotation.behavior.DynamicProperty;
+import org.apache.nifi.annotation.behavior.EventDriven;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.SupportsBatching;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.ValidationContext;
+import org.apache.nifi.components.ValidationResult;
+import org.apache.nifi.components.Validator;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.hl7.hapi.HapiMessage;
+import org.apache.nifi.hl7.model.HL7Message;
+import org.apache.nifi.hl7.query.HL7Query;
+import org.apache.nifi.hl7.query.QueryResult;
+import org.apache.nifi.hl7.query.exception.HL7QueryParsingException;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.InputStreamCallback;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.stream.io.StreamUtils;
+
+import ca.uhn.hl7v2.DefaultHapiContext;
+import ca.uhn.hl7v2.HapiContext;
+import ca.uhn.hl7v2.model.Message;
+import ca.uhn.hl7v2.parser.PipeParser;
+import ca.uhn.hl7v2.validation.impl.ValidationContextFactory;
+
+@EventDriven
+@SideEffectFree
+@SupportsBatching
+@Tags({"HL7", "healthcare", "route", "Health Level 7"})
+@DynamicProperties({@DynamicProperty(name="Name of a Relationship", value="An HL7 Query Language query", description="If a FlowFile matches the query, it will be routed to a relationship with the name of the property")})
+@WritesAttributes({@WritesAttribute(attribute="RouteHL7.Route", description="The name of the relationship to which the FlowFile was routed")})
+@CapabilityDescription("Routes incoming HL7 data according to user-defined queries. To add a query, add a new property to the processor."
+		+ " The name of the property will become a new relationship for the processor, and the value is an HL7 Query Language query. If"
+		+ " a FlowFile matches the query, a copy of the FlowFile will be routed to the associated relationship.")
+public class RouteHL7 extends AbstractProcessor {
+	public static final PropertyDescriptor CHARACTER_SET = new PropertyDescriptor.Builder()
+		.name("Character Encoding")
+		.description("The Character Encoding that is used to encode the HL7 data")
+		.required(true)
+		.expressionLanguageSupported(true)
+		.addValidator(StandardValidators.CHARACTER_SET_VALIDATOR)
+		.defaultValue("UTF-8")
+		.build();
+
+	static final Relationship REL_FAILURE = new Relationship.Builder()
+		.name("failure")
+		.description("Any FlowFile that cannot be parsed as HL7 will be routed to this relationship")
+		.build();
+	static final Relationship REL_ORIGINAL = new Relationship.Builder()
+		.name("original")
+		.description("The original FlowFile that comes into this processor will be routed to this relationship, unless it is routed to 'failure'")
+		.build();
+	
+	private volatile Map<Relationship, HL7Query> queries = new HashMap<>();
+	
+	@Override
+	protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(final String propertyDescriptorName) {
+		return new PropertyDescriptor.Builder()
+			.name(propertyDescriptorName)
+			.description("Specifies a query that will cause any HL7 message matching the query to be routed to the '" + propertyDescriptorName + "' relationship")
+			.required(false)
+			.dynamic(true)
+			.addValidator(new HL7QueryValidator())
+			.build();
+	}
+
+	@Override
+	protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+		final List<PropertyDescriptor> properties = new ArrayList<>();
+		properties.add(CHARACTER_SET);
+		return properties;
+	}
+	
+	@Override
+	public void onPropertyModified(final PropertyDescriptor descriptor, final String oldValue, final String newValue) {
+		if ( !descriptor.isDynamic() ) {
+			return;
+		}
+		
+		final Map<Relationship, HL7Query> updatedQueryMap = new HashMap<>(queries);
+		final Relationship relationship = new Relationship.Builder().name(descriptor.getName()).build();
+		
+		if ( newValue == null ) {
+			updatedQueryMap.remove(relationship);
+		} else {
+			final HL7Query query = HL7Query.compile(newValue);
+			updatedQueryMap.put(relationship, query);
+		}
+		
+		this.queries = updatedQueryMap;
+	}
+	
+	@Override
+	public Set<Relationship> getRelationships() {
+		final Set<Relationship> relationships = new HashSet<>(queries.keySet());
+		relationships.add(REL_FAILURE);
+		relationships.add(REL_ORIGINAL);
+		return relationships;
+	}
+	
+	@Override
+	public void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException {
+		FlowFile flowFile = session.get();
+		if ( flowFile == null ) {
+			return;
+		}
+		
+		final Charset charset = Charset.forName(context.getProperty(CHARACTER_SET).evaluateAttributeExpressions(flowFile).getValue());
+		
+		final byte[] buffer = new byte[(int) flowFile.getSize()];
+		session.read(flowFile, new InputStreamCallback() {
+			@Override
+			public void process(final InputStream in) throws IOException {
+				StreamUtils.fillBuffer(in, buffer);
+			}
+		});
+
+		@SuppressWarnings("resource")
+		final HapiContext hapiContext = new DefaultHapiContext();
+		hapiContext.setValidationContext(ValidationContextFactory.noValidation());
+		
+		final PipeParser parser = hapiContext.getPipeParser();
+		final String hl7Text = new String(buffer, charset);
+		final HL7Message message;
+		try {
+			final Message hapiMessage = parser.parse(hl7Text);
+			message = new HapiMessage(hapiMessage);
+		} catch (final Exception e) {
+			getLogger().error("Failed to parse {} as HL7 due to {}; routing to failure", new Object[] {flowFile, e});
+			session.transfer(flowFile, REL_FAILURE);
+			return;
+		}
+
+		final Set<String> matchingRels = new HashSet<>();
+		final Map<Relationship, HL7Query> queryMap = queries;
+		for ( final Map.Entry<Relationship, HL7Query> entry : queryMap.entrySet() ) {
+			final Relationship relationship = entry.getKey();
+			final HL7Query query = entry.getValue();
+			
+			final QueryResult result = query.evaluate(message);
+			if ( result.isMatch() ) {
+				FlowFile clone = session.clone(flowFile);
+				clone = session.putAttribute(clone, "RouteHL7.Route", relationship.getName());
+				session.transfer(clone, relationship);
+				session.getProvenanceReporter().route(clone, relationship);
+				matchingRels.add(relationship.getName());
+			}
+		}
+
+		session.transfer(flowFile, REL_ORIGINAL);
+		getLogger().info("Routed a copy of {} to {} relationships: {}", new Object[] {flowFile, matchingRels.size(), matchingRels});
+	}
+
+	private static class HL7QueryValidator implements Validator {
+
+		@Override
+		public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
+			String error = null;
+			
+			try {
+				final HL7Query hl7Query = HL7Query.compile(input);
+				final List<Class<?>> returnTypes = hl7Query.getReturnTypes();
+				if ( returnTypes.size() != 1 ) {
+					error = "RouteHL7 requires that the HL7 Query return exactly 1 element of type MESSAGE";
+				} else if ( !HL7Message.class.isAssignableFrom(returnTypes.get(0)) ) {
+					error = "RouteHL7 requires that the HL7 Query return exactly 1 element of type MESSAGE";
+				}
+			} catch (final HL7QueryParsingException e) {
+				error = e.toString();
+			}
+			
+			if ( error == null ) {
+				return new ValidationResult.Builder().subject(subject).input(input).valid(true).build();
+			} else {
+				return new ValidationResult.Builder().subject(subject).input(input).valid(false).explanation(error).build();
+			}
+		}
+		
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
new file mode 100644
index 0000000..3f57ff0
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
@@ -0,0 +1,16 @@
+# 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.
+org.apache.nifi.processors.hl7.ExtractHL7Attributes
+org.apache.nifi.processors.hl7.RouteHL7
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/test/java/org/apache/nifi/processors/hl7/TestExtractHL7Attributes.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/test/java/org/apache/nifi/processors/hl7/TestExtractHL7Attributes.java b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/test/java/org/apache/nifi/processors/hl7/TestExtractHL7Attributes.java
new file mode 100644
index 0000000..f566288
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/test/java/org/apache/nifi/processors/hl7/TestExtractHL7Attributes.java
@@ -0,0 +1,48 @@
+/*
+ * 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.processors.hl7;
+
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.apache.nifi.processors.hl7.ExtractHL7Attributes;
+import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.util.TestRunner;
+import org.apache.nifi.util.TestRunners;
+import org.junit.Test;
+
+public class TestExtractHL7Attributes {
+
+	@Test
+	public void testExtract() throws IOException {
+		System.setProperty("org.slf4j.simpleLogger.log.org.apache.nifi", "DEBUG");
+		final TestRunner runner = TestRunners.newTestRunner(ExtractHL7Attributes.class);
+		runner.enqueue(Paths.get("src/test/resources/1.hl7"));
+		
+		runner.run();
+		runner.assertAllFlowFilesTransferred(ExtractHL7Attributes.REL_SUCCESS, 1);
+		final MockFlowFile out = runner.getFlowFilesForRelationship(ExtractHL7Attributes.REL_SUCCESS).get(0);
+		final SortedMap<String, String> sortedAttrs = new TreeMap<>(out.getAttributes());
+		for (final Map.Entry<String, String> entry : sortedAttrs.entrySet()) {
+			System.out.println(entry.getKey() + " : " + entry.getValue());
+		}
+	}
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/test/resources/1.hl7
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/test/resources/1.hl7 b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/test/resources/1.hl7
new file mode 100644
index 0000000..bf2b8a5
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/test/resources/1.hl7
@@ -0,0 +1,16 @@
+MSH|^~`&|ECG REPORTING|ROCHESTER|ERIS|ROCHESTER|20110621050440||ORU^R01|20110621050440|P|2.1
+PID|||999999999||TEST^PATIENT||18450101|F
+OBR|||211088491|0^ADULT^ROCHECG|||20110620170631|||||||||M999999^^^^^^^RACFID||||||20110621060232||EC|F|||||||M999999^LASTNAME MD^FIRSTNAME^^^^^RACFID
+OBX||ST|93000.2^VENTRICULAR RATE EKG/MIN^CPT4|1|52|/SEC
+OBX||ST|93000.4^PR INTERVAL(MSEC)^CPT4|2|208|MSEC
+OBX||ST|93000.5^QRS - INTERVAL(MSEC)^CPT4|3|88|MSEC
+OBX||ST|93000.6^QT - INTERVAL(MSEC)^CPT4|4|466|MSEC
+OBX||ST|93000&PTL^PHYSICAL TEST LOCATION^CPT4|5|STMA
+OBX||ST|93000&PTR^PHYSICAL TEST ROOM^CPT4|6|04254
+OBX||CE|93000.17^^CPT4|7|21&101^Sinus bradycardia`T`with 1st degree A-V block^MEIECG
+OBX||CE|93000.17^^CPT4|8|1687^Otherwise normal ECG^MEIECG
+OBX||CE|93000&CMP^^CPT4|9|1301^When compared with ECG of^MEIECG
+OBX||TS|93000&CMD^EKG COMPARISON DATE^CPT4|10|201106171659
+OBX||CE|93000&CMP^^CPT4|11|1305^No significant change was found^MEIECG
+OBX||TX|93000.48^EKG COMMENT^CPT4|12|9917^LASTNAME MD^FIRSTNAME
+OBX||FT|93000^ECG 12-LEAD^CPT4|13|{\rtf1\ansi \deff1\deflang1033\ {\fonttbl{\f1\fmodern\fcharset0 Courier;}{\f2\fmodern\fcharset0 Courier;}} \pard\plain \f1\fs18\par 20Jun2011 17:06\par VENTRICULAR RATE 52\par Sinus bradycardia with 1st degree A-V block\par Otherwise normal ECG\par When compared with ECG of 17-JUN-2011 16:59,\par No significant change was found\par 47507`S`'LASTNAME MD`S`'FIRSTNAME \par }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/test/resources/hypoglycemia.hl7
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/test/resources/hypoglycemia.hl7 b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/test/resources/hypoglycemia.hl7
new file mode 100644
index 0000000..02e8967
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/test/resources/hypoglycemia.hl7
@@ -0,0 +1,5 @@
+MSH|^~\&|CERNER||PriorityHealth||||ORU^R01|Q479004375T431430612|P|2.3|
+PID|||001677980||SMITH^CURTIS||19680219|M||||||||||929645156318|123456789|
+PD1||||1234567890^LAST^FIRST^M^^^^^NPI|
+OBR|1|341856649^HNAM_ORDERID|000002006326002362|648088^Basic Metabolic Panel|||20061122151600|||||||||1620^Hooker^Robert^L||||||20061122154733|||F|||||||||||20061122140000|
+OBX|1|NM|GLU^Glucose Lvl|59|mg/dL|65-99^65^99|L|||F|||20061122154733|
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-nar-bundles/nifi-hl7-bundle/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-hl7-bundle/pom.xml b/nifi/nifi-nar-bundles/nifi-hl7-bundle/pom.xml
new file mode 100644
index 0000000..3f9fbce
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-hl7-bundle/pom.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-nar-bundles</artifactId>
+        <version>0.1.0-incubating-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>nifi-hl7-bundle</artifactId>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>nifi-hl7-processors</module>
+        <module>nifi-hl7-nar</module>
+    </modules>
+
+</project>


[49/62] [abbrv] incubator-nifi git commit: NIFI-494: - Escaping parameters as appropriate. - Additional error handling.

Posted by ma...@apache.org.
NIFI-494:
- Escaping parameters as appropriate.
- Additional error handling.

Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/eb023e57
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/eb023e57
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/eb023e57

Branch: refs/heads/NIFI-25
Commit: eb023e57b2b4f449e8ae9c0fb369f9197eb5510d
Parents: 2154b82
Author: Matt Gilman <ma...@gmail.com>
Authored: Thu Apr 9 15:49:46 2015 -0400
Committer: Matt Gilman <ma...@gmail.com>
Committed: Thu Apr 9 15:49:46 2015 -0400

----------------------------------------------------------------------
 .../java/org/apache/nifi/util/EscapeUtils.java  | 42 +++++++++++++++++
 .../nifi/web/StandardNiFiContentAccess.java     | 10 +++-
 .../web/api/config/NotFoundExceptionMapper.java | 48 ++++++++++++++++++++
 .../src/main/resources/nifi-web-api-context.xml |  1 +
 .../nifi/web/ContentViewerController.java       | 18 +++++---
 .../src/main/webapp/WEB-INF/jsp/header.jsp      | 20 ++++----
 .../src/main/webapp/js/hexview/hexview.js       |  4 +-
 .../nifi-web/nifi-web-docs/pom.xml              |  6 +++
 .../main/webapp/WEB-INF/jsp/documentation.jsp   |  2 +-
 .../src/main/webapp/js/application.js           |  7 ++-
 .../nifi-framework/nifi-web/nifi-web-ui/pom.xml |  6 +++
 .../main/webapp/WEB-INF/pages/message-page.jsp  |  6 +--
 .../nifi-standard-content-viewer/pom.xml        |  5 ++
 .../src/main/webapp/WEB-INF/jsp/codemirror.jsp  |  7 ++-
 .../nifi-update-attribute-ui/pom.xml            |  5 ++
 .../src/main/webapp/WEB-INF/jsp/worksheet.jsp   |  8 ++--
 16 files changed, 165 insertions(+), 30 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/eb023e57/nifi/nifi-commons/nifi-utils/src/main/java/org/apache/nifi/util/EscapeUtils.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-utils/src/main/java/org/apache/nifi/util/EscapeUtils.java b/nifi/nifi-commons/nifi-utils/src/main/java/org/apache/nifi/util/EscapeUtils.java
new file mode 100644
index 0000000..9d48d3d
--- /dev/null
+++ b/nifi/nifi-commons/nifi-utils/src/main/java/org/apache/nifi/util/EscapeUtils.java
@@ -0,0 +1,42 @@
+/*
+ * 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.util;
+
+public class EscapeUtils {
+
+    /**
+     * Escapes the specified html by replacing &amp;, &lt;, &gt;, &quot;, &#39;, &#x2f; 
+     * with their corresponding html entity. If html is null, null is returned.
+     * 
+     * @param html
+     * @return 
+     */
+    public static String escapeHtml(String html) {
+        if (html == null) {
+            return null;
+        }
+        
+        html = html.replace("&", "&amp;");
+        html = html.replace("<", "&lt;");
+        html = html.replace(">", "&gt;");
+        html = html.replace("\"", "&quot;");
+        html = html.replace("'", "&#39;");
+        html = html.replace("/", "&#x2f;");
+        
+        return html;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/eb023e57/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java
index 70aad94..ed58143 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java
@@ -121,8 +121,14 @@ public class StandardNiFiContentAccess implements ContentAccess {
             final String rawDirection = StringUtils.substringAfterLast(eventDetails, "/content/");
             
             // get the content type
-            final Long eventId = Long.parseLong(rawEventId);
-            final ContentDirection direction = ContentDirection.valueOf(rawDirection.toUpperCase());
+            final Long eventId;
+            final ContentDirection direction;
+            try {
+                eventId = Long.parseLong(rawEventId);
+                direction = ContentDirection.valueOf(rawDirection.toUpperCase());
+            } catch (final IllegalArgumentException iae) {
+                throw new IllegalArgumentException("The specified data reference URI is not valid.");
+            }
             return serviceFacade.getContent(eventId, request.getDataUri(), direction);
         }
     }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/eb023e57/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/NotFoundExceptionMapper.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/NotFoundExceptionMapper.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/NotFoundExceptionMapper.java
new file mode 100644
index 0000000..512391f
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/NotFoundExceptionMapper.java
@@ -0,0 +1,48 @@
+/*
+ * 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.web.api.config;
+
+import com.sun.jersey.api.NotFoundException;
+import com.sun.jersey.api.Responses;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import org.apache.nifi.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Maps not found exceptions into client responses.
+ */
+@Provider
+public class NotFoundExceptionMapper implements ExceptionMapper<NotFoundException> {
+
+    private static final Logger logger = LoggerFactory.getLogger(NotFoundExceptionMapper.class);
+
+    @Override
+    public Response toResponse(NotFoundException exception) {
+        // log the error
+        logger.info(String.format("%s. Returning %s response.", exception, Response.Status.NOT_FOUND));
+
+        if (logger.isDebugEnabled()) {
+            logger.debug(StringUtils.EMPTY, exception);
+        }
+
+        return Responses.notFound().entity("The specified resource could not be found.").type("text/plain").build();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/eb023e57/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
index bf4f245..e034baa 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
@@ -270,6 +270,7 @@
     <bean class="org.apache.nifi.web.api.config.NodeReconnectionExceptionMapper" scope="singleton"/>
     <bean class="org.apache.nifi.web.api.config.PrimaryRoleAssignmentExceptionMapper" scope="singleton"/>
     <bean class="org.apache.nifi.web.api.config.ResourceNotFoundExceptionMapper" scope="singleton"/>
+    <bean class="org.apache.nifi.web.api.config.NotFoundExceptionMapper" scope="singleton"/>
     <bean class="org.apache.nifi.web.api.config.UnknownNodeExceptionMapper" scope="singleton"/>
     <bean class="org.apache.nifi.web.api.config.ValidationExceptionMapper" scope="singleton"/>
     <bean class="org.apache.nifi.web.api.config.WebApplicationExceptionMapper" scope="singleton"/>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/eb023e57/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/java/org/apache/nifi/web/ContentViewerController.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/java/org/apache/nifi/web/ContentViewerController.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/java/org/apache/nifi/web/ContentViewerController.java
index d9b082d..4d99a69 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/java/org/apache/nifi/web/ContentViewerController.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/java/org/apache/nifi/web/ContentViewerController.java
@@ -68,10 +68,21 @@ public class ContentViewerController extends HttpServlet {
         final ServletContext servletContext = request.getServletContext();
         final ContentAccess contentAccess = (ContentAccess) servletContext.getAttribute("nifi-content-access");
         
+        final ContentRequestContext contentRequest = getContentRequest(request);
+        if (contentRequest.getDataUri() == null) {
+            request.setAttribute("title", "Error");
+            request.setAttribute("messages", "The data reference must be specified.");
+            
+            // forward to the error page
+            final ServletContext viewerContext = servletContext.getContext("/nifi");
+            viewerContext.getRequestDispatcher("/message").forward(request, response);
+            return;
+        }
+        
         // get the content
         final DownloadableContent downloadableContent;
         try {
-            downloadableContent = contentAccess.getContent(getContentRequest(request));
+            downloadableContent = contentAccess.getContent(contentRequest);
         } catch (final ResourceNotFoundException rnfe) {
             request.setAttribute("title", "Error");
             request.setAttribute("messages", "Unable to find the specified content");
@@ -138,9 +149,6 @@ public class ContentViewerController extends HttpServlet {
         final String mimeType = mediatype.toString();
         
         // add attributes needed for the header
-        final StringBuffer requestUrl = request.getRequestURL();
-        request.setAttribute("requestUrl", requestUrl.toString());
-        request.setAttribute("dataRef", request.getParameter("ref"));
         request.setAttribute("filename", downloadableContent.getFilename());
         request.setAttribute("contentType", mimeType);
         
@@ -148,8 +156,6 @@ public class ContentViewerController extends HttpServlet {
         request.getRequestDispatcher("/WEB-INF/jsp/header.jsp").include(request, response);
         
         // remove the attributes needed for the header
-        request.removeAttribute("requestUrl");
-        request.removeAttribute("dataRef");
         request.removeAttribute("filename");
         request.removeAttribute("contentType");
         

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/eb023e57/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/header.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/header.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/header.jsp
index 82382f6..b847dd5 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/header.jsp
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/WEB-INF/jsp/header.jsp
@@ -31,8 +31,8 @@
         <script type="text/javascript">
             var $$ = $.noConflict(true);
             $$(document).ready(function () {
-                var url = '${requestUrl}';
-                var ref = '${param.ref}';
+                var url = $$('#requestUrl').text();
+                var ref = $$('#ref').text();
                 
                 // create the parameters
                 var params = {
@@ -40,14 +40,14 @@
                 };
                 
                 // include the cluster node if appropriate
-                var clusterNodeId = '${param.clusterNodeId}';
-                if (clusterNodeId !== null && clusterNodeId !== '') {
+                var clusterNodeId = $$('#clusterNodeId').text();
+                if (clusterNodeId !== '') {
                     params['clusterNodeId'] = clusterNodeId;
                 }
                 
                 // determine the appropriate mode to select initially
-                var initialMode = '${param.mode}';
-                if (initialMode === null && initialMode === '') {
+                var initialMode = $$('#mode').text();
+                if (initialMode === '') {
                     initialMode = 'Original';
                 }
                 
@@ -85,8 +85,12 @@
         </script>
     </head>
     <body class="message-pane">
+        <span id="ref" class="hidden"><%= org.apache.nifi.util.EscapeUtils.escapeHtml(request.getParameter("ref")) %></span>
+        <span id="clusterNodeId" class="hidden"><%= request.getParameter("clusterNodeId") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getParameter("clusterNodeId")) %></span>
+        <span id="mode" class="hidden"><%= request.getParameter("mode") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getParameter("mode")) %></span>
+        <span id="requestUrl" class="hidden"><%= org.apache.nifi.util.EscapeUtils.escapeHtml(request.getRequestURL().toString()) %></span>
         <div id="view-as-label">View as</div>
         <div id="view-as" class="pointer button-normal"></div>
-        <div id="content-filename"><span class="content-label">filename</span>${filename}</div>
-        <div id="content-type"><span class="content-label">content type</span>${contentType}</div>
+        <div id="content-filename"><span class="content-label">filename</span><%= request.getAttribute("filename") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getAttribute("filename").toString()) %></div>
+        <div id="content-type"><span class="content-label">content type</span><%= request.getAttribute("contentType") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getAttribute("contentType").toString()) %></div>
         <div class="message-pane-message-box">
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/eb023e57/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/js/hexview/hexview.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/js/hexview/hexview.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/js/hexview/hexview.js
index 7c461d5..65fee95 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/js/hexview/hexview.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-content-viewer/src/main/webapp/js/hexview/hexview.js
@@ -148,7 +148,7 @@ $(document).ready(function () {
 
             $("table", div).addClass("hexviewerwindow_table");
             $("table", div).append("<tr></tr>").addClass("hexviewerwindow");
-            $("table tr:last", div).append("<td>" + (decimal_offset ? ("00000000"+offset).slice(-8) : "0x" + dec_to_hex8(offset)) + "</td>");
+            $("table tr:last", div).append("<td>" + escapeHtml((decimal_offset ? ("00000000"+offset).slice(-8) : "0x" + dec_to_hex8(offset))) + "</td>");
             $("table tr td:last", div).addClass("hexviewerwindow_offset");
 
             var runlen = 0;
@@ -162,7 +162,7 @@ $(document).ready(function () {
                     num += dec2_to_hex(line_data.charCodeAt(i+j));
                 }
 
-                $("table tr:last", div).append("<td>" + (hide_0x ? "" : "0x") + num + "</td>");
+                $("table tr:last", div).append("<td>" + escapeHtml((hide_0x ? "" : "0x") + num) + "</td>");
 
                 apply_highlights(offset+i);
             }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/eb023e57/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/pom.xml
index 5aa9a06..2aadb03 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/pom.xml
@@ -26,6 +26,7 @@
         <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
+            <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
@@ -33,6 +34,11 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>javax.servlet.jsp</groupId>
             <artifactId>javax.servlet.jsp-api</artifactId>
             <scope>provided</scope>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/eb023e57/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/WEB-INF/jsp/documentation.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/WEB-INF/jsp/documentation.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/WEB-INF/jsp/documentation.jsp
index aea08d0..9690def 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/WEB-INF/jsp/documentation.jsp
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/WEB-INF/jsp/documentation.jsp
@@ -30,7 +30,7 @@
     <body id="documentation-body">
         <div id="banner-header" class="main-banner-header"></div>
         <div id="banner-footer" class="main-banner-footer"></div>
-        <span id="initial-selection" style="display: none;">${param.select}</span>
+        <span id="initial-selection" style="display: none;"><%= request.getParameter("select") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getParameter("select")) %></span>
         <div id="documentation-header" class="documentation-header">
             <div id="nf-title">NiFi Documentation</div>
             <div id="nf-version"></div>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/eb023e57/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/js/application.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/js/application.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/js/application.js
index 3efdbcc..1261810 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/js/application.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/js/application.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global top */
+
 $(document).ready(function () {
 
     var isUndefined = function (obj) {
@@ -139,7 +142,7 @@ $(document).ready(function () {
                 if (isDefinedAndNotNull(response.banners)) {
                     if (isDefinedAndNotNull(response.banners.headerText) && response.banners.headerText !== '') {
                         // update the header text
-                        var bannerHeader = $('#banner-header').html(response.banners.headerText).show();
+                        var bannerHeader = $('#banner-header').text(response.banners.headerText).show();
 
                         // show the banner
                         var updateTop = function (elementId) {
@@ -155,7 +158,7 @@ $(document).ready(function () {
 
                     if (isDefinedAndNotNull(response.banners.footerText) && response.banners.footerText !== '') {
                         // update the footer text and show it
-                        var bannerFooter = $('#banner-footer').html(response.banners.footerText).show();
+                        var bannerFooter = $('#banner-footer').text(response.banners.footerText).show();
 
                         var updateBottom = function (elementId) {
                             var element = $('#' + elementId);

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/eb023e57/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
index f2d4861..eeaeb39 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
@@ -79,6 +79,7 @@
                             <packageRoot>org.apache.nifi.web.jsp</packageRoot>
                             <keepSources>true</keepSources>
                             <verbose>true</verbose>
+                            <useProvidedScope>true</useProvidedScope>
                             <excludes>
                                 **/canvas.jsp,
                                 **/summary.jsp,
@@ -620,6 +621,11 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
             <scope>provided</scope>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/eb023e57/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/message-page.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/message-page.jsp b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/message-page.jsp
index 0dc4f9d..796877f 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/message-page.jsp
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/message-page.jsp
@@ -18,7 +18,7 @@
 <!DOCTYPE html>
 <html xmlns="http://www.w3.org/1999/xhtml">
     <head>
-        <title>${title}</title>
+        <title><%= request.getAttribute("title") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getAttribute("title").toString()) %></title>
         <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
         <link rel="shortcut icon" href="images/nifi16.ico"/>
         <link href="/nifi/css/message-pane.css" rel="stylesheet" type="text/css" />
@@ -27,8 +27,8 @@
 
     <body class="message-pane">
         <div class="message-pane-message-box">
-            <p class="message-pane-title">${title}</p>
-            <p class="message-pane-content">${messages}</p>
+            <p class="message-pane-title"><%= request.getAttribute("title") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getAttribute("title").toString()) %></p>
+            <p class="message-pane-content"><%= request.getAttribute("messages") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getAttribute("messages").toString()) %></p>
         </div>
     </body>
 </html>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/eb023e57/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-content-viewer/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-content-viewer/pom.xml b/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-content-viewer/pom.xml
index 1b13178..576a328 100644
--- a/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-content-viewer/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-content-viewer/pom.xml
@@ -29,6 +29,11 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>commons-codec</groupId>
             <artifactId>commons-codec</artifactId>
             <scope>provided</scope>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/eb023e57/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-content-viewer/src/main/webapp/WEB-INF/jsp/codemirror.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-content-viewer/src/main/webapp/WEB-INF/jsp/codemirror.jsp b/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-content-viewer/src/main/webapp/WEB-INF/jsp/codemirror.jsp
index 65ad826..abcc409 100644
--- a/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-content-viewer/src/main/webapp/WEB-INF/jsp/codemirror.jsp
+++ b/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-content-viewer/src/main/webapp/WEB-INF/jsp/codemirror.jsp
@@ -20,13 +20,16 @@
 <script type="text/javascript" src="../nifi/js/codemirror/lib/codemirror-compressed.js"></script>
 <script type="text/javascript" src="../nifi/js/jquery/jquery-2.1.1.min.js"></script>
 
-<textarea id="codemirror-content">${content}</textarea>
+<textarea id="codemirror-content"><%= request.getAttribute("content") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getAttribute("content").toString()) %></textarea>
+<span id="codemirror-mode" style="display: none;"><%= org.apache.nifi.util.EscapeUtils.escapeHtml(request.getAttribute("mode").toString()) %></span> 
 
 <script type="text/javascript">
     $(document).ready(function() {
+        var mode = $('#codemirror-mode').text();
+        
         var field = document.getElementById('codemirror-content');
         var editor = CodeMirror.fromTextArea(field, {
-            mode: '${mode}',
+            mode: mode,
             lineNumbers: true,
             matchBrackets: true,
             foldGutter: true,

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/eb023e57/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/pom.xml b/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/pom.xml
index 12a0329..14bc768 100644
--- a/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/pom.xml
@@ -35,6 +35,11 @@
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-custom-ui-utilities</artifactId>
         </dependency>
         <dependency>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/eb023e57/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp b/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp
index 1d7c17b..b142c18 100644
--- a/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp
+++ b/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp
@@ -59,10 +59,10 @@
         <title>Update Attribute</title>
     </head>
     <body>
-        <div id="attribute-updater-processor-id" class="hidden">${param.id}</div>
-        <div id="attribute-updater-client-id" class="hidden">${param.clientId}</div>
-        <div id="attribute-updater-revision" class="hidden">${param.revision}</div>
-        <div id="attribute-updater-editable" class="hidden">${param.editable}</div>
+        <div id="attribute-updater-processor-id" class="hidden"><%= request.getParameter("id") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getParameter("id")) %></div>
+        <div id="attribute-updater-client-id" class="hidden"><%= request.getParameter("clientId") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getParameter("clientId")) %></div>
+        <div id="attribute-updater-revision" class="hidden"><%= request.getParameter("revision") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getParameter("revision")) %></div>
+        <div id="attribute-updater-editable" class="hidden"><%= request.getParameter("editable") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getParameter("editable")) %></div>
         <div id="update-attributes-content">
             <div id="rule-list-panel">
                 <div id="flowfile-policy-container">


[34/62] [abbrv] incubator-nifi git commit: NIFI-486: Fixed NPE that occurs if processor allows for controller service as optional property and no value set

Posted by ma...@apache.org.
NIFI-486: Fixed NPE that occurs if processor allows for controller service as optional property and no value set


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/50986932
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/50986932
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/50986932

Branch: refs/heads/NIFI-25
Commit: 50986932d6cdd0ffc7a8f0a828808bb72bb4fdf5
Parents: 4a16845
Author: Mark Payne <ma...@hotmail.com>
Authored: Mon Apr 6 17:59:07 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Mon Apr 6 17:59:07 2015 -0400

----------------------------------------------------------------------
 .../nifi/controller/scheduling/StandardProcessScheduler.java     | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/50986932/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java
index 89850cc..43e05dd 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/scheduling/StandardProcessScheduler.java
@@ -297,7 +297,9 @@ public final class StandardProcessScheduler implements ProcessScheduler {
                         final Class<? extends ControllerService> serviceDefinition = descriptor.getControllerServiceDefinition();
                         if ( serviceDefinition != null ) {
                             final String serviceId = processContext.getProperty(descriptor).getValue();
-                            serviceIds.add(serviceId);
+                            if ( serviceId != null ) {
+                            	serviceIds.add(serviceId);
+                            }
                         }
                     }
                     


[25/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
Squashed commit of the following:

commit 9cfc3ab42fbe46af113a1f0db41aa95e6e0b4568
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 31 16:32:13 2015 -0400

    NIFI-250:
    - Updating test to fix compilation error after merge.

commit 266d44b11c92cea51f882483adcfa898feeaac44
Merge: 97632fb e7d6d94
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 31 16:11:12 2015 -0400

    Merge branch 'develop' into NIFI-250

commit 97632fbce67850c66b765f667ec2a1ed6e540914
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 31 12:12:01 2015 -0400

    NIFI-250:
    - Removing extra call to show configuration dialog.

commit 23465a1e116ee10c2c73a9ed25d7b382096f9b52
Merge: d4321f5 1abee29
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 31 11:09:32 2015 -0400

    Merge branch 'NIFI-250' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit 1abee2964380bf2aee91189f471035fe3df92919
Author: Mark Payne <ma...@hotmail.com>
Date:   Tue Mar 31 10:58:53 2015 -0400

    NIFI-250: Fixed bugs with configuring reporting tasks on restart

commit 712327fe8eac0c96a4d676a1ffe2210801d97200
Merge: abd1dc3 1cee436
Author: Mark Payne <ma...@hotmail.com>
Date:   Tue Mar 31 08:41:56 2015 -0400

    Merge branch 'NIFI-250' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit abd1dc3362ef6297eb3f044aadc58c181114fe06
Author: Mark Payne <ma...@hotmail.com>
Date:   Tue Mar 31 08:41:45 2015 -0400

    NIFI-250: Fixed bug that caused IllegalArgumentException when loading reporting tasks on NCM if property is not set

commit d4321f50d6adc1534d318405fb23838032c054d7
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 31 07:39:47 2015 -0400

    NIFI-250:
    - Updating Javadocs.

commit 5394a826b07bb897299bebefe275f633c02e95ce
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 31 07:30:57 2015 -0400

    NIFI-250:
    - Updating messages included in exceptions during error conditions when interacting with a custom UI.

commit 1cee4367888d899d9396f7e48f2badfa8e11f07c
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 30 14:54:16 2015 -0400

    NIFI-250:
    - Fixing the width of the referencing components when the content overflows.

commit b32e2712c1735d57bf1d34debb0268e42a544c56
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 30 14:19:49 2015 -0400

    NIFI-250:
    - Using a List when (de)serializing allowable values. This is preferable than calling out a specific Set implementation.

commit 2949beb6f57a7af469257a27adffa9397e234d7b
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 30 13:20:55 2015 -0400

    NIFI-250:
    - Only recording previously configured controller service when attempting to change a controller service.
    - Only polling schedulable components when stopping. No need to poll during start requests.

commit 0b77160da76877b35a5777b0bcf552d5985ba64d
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 30 13:17:44 2015 -0400

    NIFI-250:
    - Adding tooltip to referencing components section of the enable/disable controller service dialog.
    - Fixing typo.

commit 37d2bd1e1e564c516cb2d96c7f1b361c9034110a
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 30 12:10:44 2015 -0400

    NIFI-250:
    - Fixing controller service ancestor calculation.

commit 2673c39a62462e5ade8363b379424241029f2058
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 30 12:09:37 2015 -0400

    NIFI-250:
    - Correcting the number of displayed controller service types.
    - Adding more indentation.

commit 0a7c55705fcdb485b6a8278fe39dd31f6e21639e
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 27 15:23:55 2015 -0400

    NIFI-250:
    - Reloading newly and previously referenced controller services when modifying the referencing controller service. (ie adding/changing/removing the reference).

commit 0d975028efdb07439ea21f1e3654b2c3cc939a93
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 27 13:14:19 2015 -0400

    NIFI-250:
    - Fixing list item wrapping

commit 1efa053b6af6b4d70bed608247b42bd9b92a9127
Merge: eaa8c51 92df0d7
Author: Mark Payne <ma...@hotmail.com>
Date:   Fri Mar 27 12:42:38 2015 -0400

    Merge branch 'NIFI-250' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit eaa8c51f973b837ee5c41bb9fb7d09e98cd41824
Author: Mark Payne <ma...@hotmail.com>
Date:   Fri Mar 27 12:42:32 2015 -0400

    NIFI-250: Fixed bug when restoring Reporting Tasks on NCM; added documentation to some services; fixed bugs in Standard Validators

commit 92df0d7cc7074f4a14c571d1d69b2845b9a8ad7a
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 27 09:39:27 2015 -0400

    NIFI-250:
    - Reverting change to reload service and referencing components on done to do always. Since the enable/disable request may poll or be canceled by a user.

commit be3254c947352486e5fe992184f8de6a5b20369f
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 27 09:22:22 2015 -0400

    NIFI-250:
    - Adding a check to ensure we don't attempt to reload a controller service that has been removed but is still referenced by another controller service.

commit 21ab41fbe1cbd3460eb544ba96c18e120e7e67e3
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 27 09:16:24 2015 -0400

    NIFI-250:
    - Fixing typo in the scope tooltip.

commit 56f8dd972b4565a367fe317deee5826c215874bd
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 27 08:30:52 2015 -0400

    NIFI-250:
    - Ensuring the settings (controller service/reporting task) dialog is closed prior to opening a custom UI.

commit 3eb1ac16a59738db683739e48d362260cf2c4e03
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 27 08:00:40 2015 -0400

    NIFI-250:
    - Ensuring the style on the comments field is reset when changing its value.

commit b51af3cb7a60fb11ddc2dedd328e35a92d39f0e3
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Mar 26 15:12:09 2015 -0400

    NIFI-250:
    - Fixing issues when clicking Add before selecting the type of controller service and reporting task to create.

commit 5b56982d20b3a24ca59362a482c39ef6d4329b8d
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Mar 26 11:55:56 2015 -0400

    NIFI-250:
    - Using the correct state variable when determining how to update a given controller service.

commit 6c678bfe6adf9e15b294523603dbf230d731b3c9
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Mar 26 10:42:24 2015 -0400

    NIFI-250:
    - Prevented reading of the node response because when clustered the responses are merged and we need to use the updated entity.

commit ffb4e6b68b57f23b41703613557f7bf0da5bbce5
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Mar 26 09:13:23 2015 -0400

    NIFI-250:
    - Only attempting to reload a controller service when one is actually referenced.

commit c61a2afbb8b0696b883c06dfd92abb3f3b111bb6
Merge: 8a830be a5e140f
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Mar 26 09:02:20 2015 -0400

    Merge branch 'develop' into NIFI-250

    Conflicts:
    	nifi/nifi-api/src/main/java/org/apache/nifi/components/ValidationContext.java
    	nifi/nifi-mock/src/main/java/org/apache/nifi/util/MockValidationContext.java
    	nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContext.java

commit 8a830bef412dfa44a54c840e24545defeed83f15
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Mar 26 08:10:47 2015 -0400

    NIFI-250:
    - Rendering reporting tasks in controller service referencing components.
    - Ensuring referenced controller services are reloading when a reporting task changes.
    - Automatically selecting reporting task tab and item in table when linking to one via controller service referencing components.

commit 47e84c009db874b5f2151bd5eb92b403461c5730
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Mar 26 08:04:17 2015 -0400

    NIFI-250:
    - Fixing issue preventing reporting task from being included in controller service referencing components.

commit fe24f368e8e317400d77491e904de6f76639fd53
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Mar 26 07:04:51 2015 -0400

    NIFI-250:
    - Handling the appropriate type of exception when creating a controller service.

commit 4f01cd090972fd6b2712d914e7d2ea8eca8627b1
Merge: 8bef904 91e002e
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Mar 26 06:55:03 2015 -0400

    Merge branch 'NIFI-250' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit 91e002eb90795a1c2d2711c3e20db520fce155ff
Merge: 47435c8 5db358f
Author: Mark Payne <ma...@hotmail.com>
Date:   Wed Mar 25 15:46:19 2015 -0400

    Merge branch 'NIFI-250' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit 47435c8bb7207358dbfef2fcfdad5e3fdd054434
Author: Mark Payne <ma...@hotmail.com>
Date:   Wed Mar 25 15:46:09 2015 -0400

    NIFI-250: Removed controller-services.xml and reporting-tasks.xml and associated properties

commit 8bef904df75e1ca190075c5b78e6f858031ec4e8
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Mar 25 15:29:18 2015 -0400

    NIFI-250:
    - Including properties and descriptors in controller service referencing components.

commit 5db358f4e580dc5a1129444ffc203a4041720160
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Mar 25 15:27:35 2015 -0400

    NIFI-250:
    - Populating the reporting task details when modifying a service that it references.

commit 975db1a7b33383200b2045fd1c97e29216d1c2b3
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Mar 25 15:07:30 2015 -0400

    NIFI-250:
    - Fixing issue when reloading transitive controller service referencing components.
    - Allowing reporting tasks to be reloaded (when they reference controller services).

commit 17b0fa7df170fa50f33236863481f008356cb1b4
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Mar 25 15:03:39 2015 -0400

    NIFI-250:
    - Addressing issues setting reporting task comments.
    - Including properties and descriptors in controller service referencing components.

commit f71588065d788de4d3957593d88473c6bc13c2a1
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Mar 25 11:07:13 2015 -0400

    NIFI-250:
    - Reordering the invocation of the modal close handler and the hiding of the dialog.
    - Separating loading from showing the settings so the actions can be performed independently. This allows the transition back to the controller service/reporting task table more smooth when viewing usage or a custom UI.

commit b8b9f2c2509d8101d2f749e4ce7c58ed7a52be96
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Mar 25 09:57:37 2015 -0400

    NIFI-250:
    - Ensuring requests are not attempted to be replicated if a custom UI provides an invalid component id.

commit 2dccb5b4d98a194a7cee307b2252962eebdb67b1
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Mar 25 09:28:55 2015 -0400

    NIFI-250:
    - Adding better handling when a request is interpreted as a request to create a processor/controller service/reporting task and the type is not specified.

commit e8294f29e2f5aef8cec0117cd09a2242b5ef2ca9
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 24 15:31:40 2015 -0400

    NIFI-250:
    - Reopening the settings to the controller services/reporting tasks table after showing usage or custom UIs.

commit d8a27993d0d7a7650f59d595c58eb99e5140c82c
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 24 14:15:08 2015 -0400

    NIFI-250:
    - Adding support to configure reporting task comments. Comments still need to be persisted to flow xml.

commit ec21493622a9cc9ce0857c0b37d48a08f120a770
Author: Mark Payne <ma...@hotmail.com>
Date:   Tue Mar 24 13:07:05 2015 -0400

    NIFI-250: Restore Comments and AnnotationData for Reporting Tasks on NCM; these were inadvertently skipped

commit de21b6090c821a0dd4421d0637a2fd87a5a894d7
Merge: 4dcb9fd 953cb12
Author: Mark Payne <ma...@hotmail.com>
Date:   Tue Mar 24 12:49:48 2015 -0400

    Merge branch 'NIFI-250' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit 953cb1227af70e846e47416ffdbc6eb3a1686d6a
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 24 12:28:30 2015 -0400

    NIFI-250:
    - Providing access to the merged node response when clustered. This is necessary as custom UIs will need to access to these details.
    - Ensuring the cluster processor endpoint is merged appropriately.

commit 4dcb9fd761dfaec98790c41ca16344a1d0b89340
Author: Mark Payne <ma...@hotmail.com>
Date:   Tue Mar 24 10:16:47 2015 -0400

    NIFI-278: Added documentation and ensured that all annotations invoke with consistent arguments

commit 80b8c602433cc7b6589b417524a23b63c891d3f1
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 24 08:58:30 2015 -0400

    NIFI-250:
    - Updating custom UI to fully support the new model (by removing references to processors).

commit 691b4617d5568cd9a28f38d081b85d233ccbea44
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 24 08:57:34 2015 -0400

    NIFI-250:
    - Updating custom UIs to support both old (deprecated) and new models.
    - Ensuring the annotation data is populated in outgoing DTOs.
    - Ensuring the component type is populated in the details for custom UIs.
    - Renaming the js module for custom UIs to exclude the reference to processors.

commit dbaa219df12207d80b9e5f110443886b95bb5a7c
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 23 14:32:28 2015 -0400

    NIFI-250:
    - Renaming the standard nifi web configuration context.
    - Adding support for controller services and reporting tasks to the nifi web configuration context.

commit 3e7ca3838039d724eba019214b073a8640e61149
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 23 13:09:15 2015 -0400

    NIFI-250:
    - Continuing to merge changes from the content viewer with the newly supported component UI extensions.

commit d3bb3ab829bbeaa93453509e33a7b3f165365987
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 23 11:34:18 2015 -0400

    NIFI-250:
    - Only prompting users for the property name and immediately adding to the table. In the background, the property descriptor is loaded from the processor and then used when editing the property.

commit 1c96bd08f7903f9acc845a542ccb7196f8a264c0
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 23 09:49:18 2015 -0400

    NIFI-250:
    - Removing unneeded artifact.

commit 1de514adf7a3dacc51823932b70de3f8f64c369a
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 23 09:36:07 2015 -0400

    NIFI-250:
    - More clean up post merge with the data viewer.

commit d19471d6da17974ab6485f3653b4d793b30e895c
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 23 08:20:04 2015 -0400

    Resolving conflicts when merging develop into NIFI-250

commit 65e35e49a5bb1d81cf0933220bf1204131dc72d5
Merge: a4555d1 e05c9fd
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 23 08:09:33 2015 -0400

    Merge branch 'develop' into NIFI-250

    Conflicts:
    	nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
    	nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
    	nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
    	nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/pom.xml

commit a4555d1a17252bedf503e443223670d34c5acd0d
Merge: 6a31b94 fcff5c4
Author: Mark Payne <ma...@hotmail.com>
Date:   Fri Mar 20 15:52:18 2015 -0400

    Merge branch 'NIFI-250' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit 6a31b94b39c07f943d9145889bc96078921cb2d0
Author: Mark Payne <ma...@hotmail.com>
Date:   Fri Mar 20 15:52:09 2015 -0400

    NIFI-250: Fixed bug with templates related to services referencing other services

commit fcff5c40a22a42bedbdd488dc540f4e80d6caf68
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 20 15:35:54 2015 -0400

    NIFI-250:
    - Adding endpoints for obtaining [processor|controller service|reporting task] property descriptors.

commit e0e2d161b5f577b2d3c012843c6fde2123cb3411
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 20 15:33:36 2015 -0400

    NIFI-250:
    - Adding endpoints for obtaining [processor|controller service|reporting task] property descriptors.

commit bd999d16ad804a0c71e6188dac0da57ceda58996
Author: Mark Payne <ma...@hotmail.com>
Date:   Fri Mar 20 14:51:26 2015 -0400

    NIFI-250: Include referenced controller services in templates

commit 02afcfbf4585eba10be1aeb90674c43933b63d88
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 20 14:39:15 2015 -0400

    NIFI-250:
    - Removing link to jQuery migration script.

commit 10ebbf0c3e9155e43dfbe866c42a27a021d3dc42
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 20 14:38:47 2015 -0400

    NIFI-250:
    - Specifying the correct UI extension type before populate the service/task DTO.

commit 07941b5209fa182b02ac7e2d66ebade0416c4319
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 20 14:38:09 2015 -0400

    NIFI-250:
    - Closing the controller service and reporting task shell whenever a custom UI is opened.

commit 5c98ccdbfb15d330ecce118feab98ac0681d6f1a
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 20 13:09:38 2015 -0400

    NIFI-250:
    - Renaming interface that custom UIs use to interact with the NiFi instance.
    - Javadocs.
    - Ported UpdateAttribute to use updated custom UI model.
    - Fixed bug when discovering types of components that support custom UIs.

commit 24d787e5d40949659427e72d126dd2a225b6ff86
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 20 10:51:15 2015 -0400

    NIFI-250:
    - Updating the way in which UI extensions interact with the underlying NiFi instance.

commit ffa919a349906ef3a8fe178ab448fe69bf1bd4b6
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Mar 19 14:12:11 2015 -0400

    NIFI-250: Fixed NPE

commit c63a8c05ff64196a0fc77a2386c60bac88784ad0
Merge: 800f80b 42f8e81
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Mar 19 09:31:46 2015 -0400

    NIFI-250: Fixed handling of controller services on startup and resolved merge conflicts

commit 800f80bc05ead5f097c9e948f619e0dd681ca592
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Mar 19 09:28:05 2015 -0400

    NIFI-250: Fixed controller service handling on startup

commit 42f8e819811835c94744276cde741ebcdbb1a57e
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Mar 18 21:22:44 2015 -0400

    NIFI-250:
    - Updating UI extension type.

commit 6c058778534adae967ee34ec5fb2b8c20b66d43b
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Mar 18 16:09:44 2015 -0400

    NIFI-250:
    - Starting to refactor support for UI extensions.
    - Deprecating previous support.

commit f0c660c25ee0e2b1a6acd9b8e876144cad9b0d6b
Merge: b45c09c e569af6
Author: Mark Payne <ma...@hotmail.com>
Date:   Wed Mar 18 10:12:13 2015 -0400

    Merge branch 'develop' into NIFI-250

commit b45c09cacdbaf93f79dad95a9a258d82fed40831
Author: Mark Payne <ma...@hotmail.com>
Date:   Wed Mar 18 10:09:40 2015 -0400

    NIFI-368: Implemented methods that were not implemented from interface

commit 454c837ce1e63f780e648e86495c4e5c038fc090
Merge: d7210da e750579
Author: Mark Payne <ma...@hotmail.com>
Date:   Wed Mar 18 09:37:10 2015 -0400

    Merge branch 'NIFI-250' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit d7210da40eda444267133fdfbbf2a8a378590e82
Author: Mark Payne <ma...@hotmail.com>
Date:   Wed Mar 18 09:37:06 2015 -0400

    NIFI-250: Fix the way that services are restored at startup

commit e750579d77ea3b69b438b57d5f468af6f51ff049
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Mar 18 07:40:20 2015 -0400

    NIFI-250:
    - Ensuring controller service comments are included when saving.
    - Adding a label to the comments in the service details dialog.

commit f10bb997b84cce32d30ded918a93ea8f9d511016
Merge: 25ca4f8 c118ead
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Mar 18 06:58:44 2015 -0400

    Merge branch 'develop' into NIFI-250

commit 25ca4f89e875bb8b7c89b75756793e1863190621
Merge: 769ed68 1cca300
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 17 16:03:08 2015 -0400

    Merge branch 'develop' into NIFI-250

commit 769ed68c51233443bd210e6af3ee6c0b96a5de7d
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 17 10:47:43 2015 -0400

    NIFI-425:
    - Reseting the filter in the new controller service and new reporting task dialog.
    - Setting the initial focus.
    - Fixing the close handler configuration.

commit 985aa134c4c5a43b1570b669e0d32c40be28e152
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 17 09:49:09 2015 -0400

    NIFI-250:
    - Addressing cancel action when initiated by pressing escape. Previously it was canceling every dialog that was open when it should have only applied to the top most.

commit bf7027c0760b433c3580a77282df24ed5aac67f2
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 17 08:32:13 2015 -0400

    NIFI-425:
    - Filtering is not clear when closing the new processor dialog.

commit aa21d073d55dff3702dbb811b80d0947f745538a
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 17 08:18:06 2015 -0400

    NIFI-250:
    - Merging develop into NIFI-250.

commit c28d9f57287b5b95d6451a5365dbc2b3d4552422
Merge: 5a6723c 761e64a
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 17 08:16:36 2015 -0400

    NIFI-250: Merge develop into NIFI-250

commit 5a6723c0465d15e603ee7cbec9b1e58a80f0f036
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 16 15:56:14 2015 -0400

    NIFI-250:
    - Fixing enable button label.

commit 02b9e4b0a91e65625f10437bf9459e8005b3459c
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 16 13:20:16 2015 -0400

    NIFI-250:
    - Setting the correct visibility on the controller service name field.

commit 24c86facff21f133a62bffe2f5927a45e1b9d059
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 16 12:34:17 2015 -0400

    NIFI-250:
    - Using .html() instead of .text() as the service/task description is already escaped.

commit dddf5e8841977321cce2f287be2728f3163ad5a4
Merge: 30f9323 eb757a4
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 16 11:41:26 2015 -0400

    Merge branch 'develop' into NIFI-250

commit 30f9323a0cdf6f783c94ff0c7120b2c042dd8194
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 16 11:38:08 2015 -0400

    NIFI-250:
    - Ensuring the new property dialog is only destroy when appropriate. There were cases when we wanted to clear the property table but leave the new property dialog intact.

commit af855ba123db2dc8b9d5e96f47aabb5d76055130
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 16 10:19:28 2015 -0400

    NIFI-250:
    - Using the correct element names when attempting to parse the reporting task configuration file.

commit 5a57670c70012e5d8e091076f74c14b1fa78a735
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 16 09:21:21 2015 -0400

    NIFI-250:
    - Ensuring the editable flag is set correctly when creating a property table.
    - Properly cleaning up the new property dialog.

commit 19b6845b66da5e45d8a974f7d4d7cd01712ef698
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 16 08:43:55 2015 -0400

    NIFI-250:
    - Ensuring the actions columns is always last.
    - Fixing field visibility (between read only and editable).

commit 17b512ea8e720115b6bce1c5f65797d256392781
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 16 07:45:05 2015 -0400

    NIFI-250:
    - Using a utility function for populating fields.
    - Using a label for scheduling strategy rather than the enum value.

commit 92cbce496936c7e166c8666b379dcc8582b6d121
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 16 07:30:50 2015 -0400

    NIFI-250:
    - Adjusting the layout of the reporting task dialog.
    - Renaming the reporting task run status column.

commit 2c6f3a97276975899be6cc0266d6770bc88e1718
Merge: a1a7691 2de2134
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 16 07:04:11 2015 -0400

    Merge branch 'develop' into NIFI-250

commit a1a769104881eaf44ca9736cd185edc972eb5807
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 13 14:48:08 2015 -0400

    NIFI-250:
    - Updating the controller service dialog to support a read only and editable modes.

commit 40670067cb67d9df8a1817fd0c5b3a86d627d3a9
Merge: b1d65e5 d122a83
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 13 14:17:05 2015 -0400

    Merge branch 'NIFI-250' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit b1d65e560a00925345222508c370b91a878a9ab2
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 13 14:16:44 2015 -0400

    NIFI-250:
    - Updating the controller service dialog to support a read only and editable modes.
    - Ensuring the new property dialog is cleaned up if a property table instance is re-initialized (dom leak).
    - Better rending controller service state.
    - Fixing issue with component history when opening the processor details dialog.

commit d122a83633683285cc6fd6daf65f0f533a0da2ca
Author: Mark Payne <ma...@hotmail.com>
Date:   Fri Mar 13 12:01:00 2015 -0400

    NIFI-250: Fixed NPE

commit 52ea335d7c7e75b01f604a13c15f2b30c1b8df5c
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 13 11:07:31 2015 -0400

    NIFI-250:
    - Only handling new controller service state if it's actually different than the current value.

commit 50f0c123bcb8474b110aac0abc528c0f9b6fe24a
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 13 10:46:49 2015 -0400

    NIFI-250:
    - Including active threads in the reporting task dto.
    - Verifying actions taken against component referencing controller services.
    - Rendering the run state for reporting tasks (including active thread counts).

commit cd69a423dd6411cdefe1a3131865fc45f440d426
Merge: 6b36aef f556490
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 13 10:11:22 2015 -0400

    Merge branch 'NIFI-250' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit 6b36aefef942482a665182594fd3bb1044cad627
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 13 10:11:17 2015 -0400

    NIFI-250:
    - Adding active thread count on reporting tasks.
    - Merging clustered responses for controller service and reporting task endpoints.

commit f556490fb789706d4670ba6aef717de82285f29d
Author: Mark Payne <ma...@hotmail.com>
Date:   Fri Mar 13 09:16:41 2015 -0400

    NIFI-250: Added verify methods to controller service provider

commit 79ddcb828e678e38ffb67f524bed03a72a3361dc
Merge: d5a9a1a 7198912
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 13 06:49:10 2015 -0400

    Merge branch 'develop' into NIFI-250

commit d5a9a1a625e28f3d0a7d6ed72019e84088a05794
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Mar 12 12:36:17 2015 -0400

    NIFI-250:
    - Updating button visibility to always show the enable/run buttons when the service/task is disabled/stopped. Previously the enable/run buttons were only visible the component did not have any validation errors. However, this caused the buttons to shift and the alignment to be off.

commit 8cc58fe80bf402bb594e391f980e565f1a2318f3
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Mar 12 12:11:33 2015 -0400

    NIFI-250:
    - Adding a button to link to the usage of a controller service or reporting task.

commit c9604a3fcacf4c33606ca0be767c9d89ef4424a0
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Mar 12 11:10:39 2015 -0400

    NIFI-250:
    - Adding support for configuring the scheduling strategy and scheduling period for reporting tasks.

commit ca7b65262113a61ef9716dec0367fc24e19f5a05
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Mar 12 08:16:13 2015 -0400

    NIFI-250:
    - Trying to ensure accurate details when enabling/disabling controller service and minimize web requests.
    - Fixing issues with the enabled flag in the controller service and reporting task dialog.

commit ae3c29aa562f15b8af89afdeec15475a2dd96fb5
Merge: 4f41202 56cc186
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Mar 12 06:50:16 2015 -0400

    Merge branch 'develop' into NIFI-250

commit 4f41202e9df732211f9a9b2ccd54d1f652984d8b
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Mar 11 16:36:56 2015 -0400

    NIFI-250:
    - Updating controller service auditing to ensure transitive referenced components are reported correctly.
    - Restoring reference to reporting task provider.

commit fe09680deaa76e9de7c962c98e50492173a1748e
Merge: 4003542 85e38dc
Author: Mark Payne <ma...@hotmail.com>
Date:   Wed Mar 11 15:48:04 2015 -0400

    Merge branch 'NIFI-250' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit 40035429f886a501312b25b14f94cab38ed0c97f
Author: Mark Payne <ma...@hotmail.com>
Date:   Wed Mar 11 15:47:58 2015 -0400

    NIFI-250: Added missing methods to ReportingTaskProvider

commit 85e38dc0555bf6a37ec378fc19b3a7811f8a32b3
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Mar 11 15:40:23 2015 -0400

    NIFI-250:
    - Setting the default name of the reporting task.

commit 74d45aefbf6f74b04cd23808e4bb866cce027dc9
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Mar 11 15:26:45 2015 -0400

    NIFI-250:
    - Adding methods to obtain all reporting tasks.
    - Adding parameters to update the scheduled state of a reporting task.
    - Adding action buttons to the reporting task table.

commit 2211741cad47ba24027f78f2e005cac888c8cb70
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Mar 11 13:50:02 2015 -0400

    NIFI-250:
    - Fixing issues showing the progress when enabling/disabling a controller service.
    - Ensure state for the service and its referencing components at all time since the action could be cancelled at any time.

commit fc76a6165910441a372234dd3a4e626a83cbba48
Author: Mark Payne <ma...@hotmail.com>
Date:   Wed Mar 11 10:41:48 2015 -0400

    NIFI-250: Fixed import that was accidentally removed

commit 0d8cc34ae16979853a05b0ef214cd62694a31daf
Merge: baa0e74 048c5d9
Author: Mark Payne <ma...@hotmail.com>
Date:   Wed Mar 11 08:45:36 2015 -0400

    Merge branch 'NIFI-250' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit baa0e74cc37a6c74097da82ebf85d3b6e8e01d1a
Author: Mark Payne <ma...@hotmail.com>
Date:   Wed Mar 11 08:45:33 2015 -0400

    NIFI-250: Load controller services in correct order instead of arbitrary order

commit 048c5d9aa757e3d2917d558857a59c4b6a6221f0
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 10 16:39:15 2015 -0400

    NIFI-250:
    - Updating the progress label when enabling/disabling controller services and their referencing components.

commit 51d70bff34174e19cd844404b8c0a61be79a8f39
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Mar 10 14:32:30 2015 -0400

    NIFI-250:
    - Showing the progress of enabling/disabling of a controller service so the user knows what the status of each step. This is especially important when the action fails someplace in the middle.

commit 5cc6fda21b9081e426b8a65132d0d971ef645c15
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 9 16:38:39 2015 -0400

    NIFI-250:
    - Updating the enable/disable dialog to show the current status while waiting for referencing components.

commit 6d12f134ff22927d02c7c500a5650b855fa52289
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 9 14:57:18 2015 -0400

    NIFI-250:
    - Adding a close button after enabling/disabling a controller service. This gives the user an opportunity to review issues prior to closing the dialog.

commit eb6c5b5e23e5b0af4fdb88c6264ba0b05efdbb94
Merge: bde21cb 3533a4a
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 9 14:14:07 2015 -0400

    Merge branch 'develop' into NIFI-250

commit bde21cb8a6227e9b59f1af7266e949a5cd41b87f
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 9 09:39:18 2015 -0400

    NIFI-250:
    - Updating the layout of the enable/disable dialogs.
    - Showing referencing components validation errors where appropriate.

commit 73459ccb5cfbc4aa81201e6476bb47ae450b49f7
Merge: 6f86758 342ca17
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Mar 9 06:48:31 2015 -0400

    Merge branch 'develop' into NIFI-250

commit 6f867585e1fade734dfecca434942d85fe20701d
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 6 15:47:56 2015 -0500

    NIFI-250:
    - Including validation errors in the controller service referencing components dto.

commit b1dcab62a7f2ff6f2d15ef559058c7ec2358541e
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 6 11:30:14 2015 -0500

    NIFI-250:
    - Fixing missing import.

commit 91f69b0cf23422838ca1f055064ae90e8749738f
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 6 10:16:23 2015 -0500

    NIFI-250:
    - Adding a factory bean for obtaining the appropriate ReportingTaskProvider given the configured operating mode (cluster/node).
    - Fixing copy/paste error with the ControllerServiceProvicer factory bean.

commit ed22742c0f27c66205838237f312ddac01a8f61a
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 6 10:14:26 2015 -0500

    NIFI-250:
    - JS global hint.

commit bc508cd905daefc08389e921df6c681fa67dafdf
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 6 09:42:48 2015 -0500

    NIFI-250:
    - Reseting the table size when opening the new controller service/reporting task dialog.
    - Fixing typo during scroll events over the breadcrumbs.
    - JS global hint.

commit e5a314116bb43c54c2a920a7930423de038004cc
Merge: 6b429bf bd74063
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 6 08:19:37 2015 -0500

    Merge branch 'develop' into NIFI-250

commit 6b429bf42b5f31e6b7bfc18d932c9cd48a67e702
Merge: f34e343 883c4ac
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Mar 6 08:19:26 2015 -0500

    Merge branch 'NIFI-250' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit 883c4ac9d7a1cdbd6989fb1312624a752c490461
Merge: 83eff8d 2d4aebf
Author: Mark Payne <ma...@hotmail.com>
Date:   Wed Mar 4 15:50:58 2015 -0500

    Merge branch 'NIFI-250' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit 83eff8d6df7b485a4d0eb03f176e4a28ff81df3b
Author: Mark Payne <ma...@hotmail.com>
Date:   Wed Mar 4 15:50:30 2015 -0500

    NIFI-250: Creating ReportingTaskProvider

commit f34e34391716c0168b74241c284db3ffab444fe5
Merge: 2d4aebf 5e0026c
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Mar 4 09:30:23 2015 -0500

    Merge branch 'develop' into NIFI-250

commit 2d4aebf33b50b7603f2c19ba63d368bf125ce498
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Feb 20 15:08:39 2015 -0500

    NIFI-250:
    - Continuing to setup Reporting Task management in the UI.

commit c1077baf95f8c7e5ffc16ccea190b56af59c3fcc
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Feb 20 13:39:11 2015 -0500

    NIFI-250:
    - Enabling/Disabling controller service and referencing components in the appropriate order.
    - Continuing to setup Reporting Task management in the UI.

commit 573a8aa353bb47cbfc782d06786f31a93faa3d7a
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Feb 19 16:30:50 2015 -0500

    NIFI-250:
    - Using controller service state field instead of enable flag.
    - Continuing to setup Reporting Tasks.

commit a227fe46b542fdcd6dd6fe57f11d678da237bc98
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Feb 19 15:56:38 2015 -0500

    NIFI-250: Fixed bug that occurred in refactoring

commit be00d0caf34cb2ec4f192563cb2e8707d145e8b1
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Feb 19 15:20:24 2015 -0500

    NIFI-250: Added overloaded constructor to MockControllerServiceINitializationContext that allows no logger to be provided and autocreates one

commit f246565f7e5344db91f731e003c7e35e1d7cbe95
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Feb 19 15:04:13 2015 -0500

    NIFI-250: Incorporate new logic for controller service state

commit 852cc607f6fad61178f7c9d3a26807c24eee4e0e
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Feb 19 15:03:54 2015 -0500

    NIFI-368: generate bulletins when there are problems communicating with reporting tasks and controller services

commit ee63a10b8654c7a635e359eba9a7531aaf70fe6d
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Feb 19 14:55:11 2015 -0500

    NIFI-250:
    - Removing enabled flag from controller service DTOs.

commit 767f37b8311f5fbbcfa5aacd6ef1230103034f09
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Feb 19 14:46:32 2015 -0500

    NIFI-250: Updated javadocs to clarify how lifecycle annotations are used; cleaned up handling of exceptions in scheduler

commit 5f2a4358869853deff7747ae3908d6952ee94001
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Feb 19 14:45:46 2015 -0500

    NIFI-368: added componentlog to the initialization contexts for appropriate components and made abstract components return logger via getLogger() methods

commit 8f78d619751c2bdb23f6c192a92716517922846a
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Feb 19 14:29:21 2015 -0500

    NIFI-368: Extracted methods from ProcessorLog out to ComponentLog and made ProcessorLog extend from ComponentLog. This maintains backward compatibility while providing more generic loggers for other components

commit 7de30ab15ad9570233c4bff68f37acf324a66dda
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Feb 19 13:30:37 2015 -0500

    NIFI-250: Refactoring of controller service states

commit 81d84546e7b456ae55cefe8892ddfb71a729bac9
Merge: 2ac6432 fac6cd7
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Feb 19 10:38:22 2015 -0500

    Merge branch 'NIFI-250' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit fac6cd7ac6dc1ed883a18369ebfbbde0886b11ec
Merge: a3836d8 57b5d58
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Feb 19 07:50:29 2015 -0500

    Merge branch 'develop' into NIFI-250

commit a3836d832d4cdc75a6b9317a04fed00c9b062c22
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Feb 18 15:17:48 2015 -0500

    NIFI-250:
    - Starting to add an endpoint for Reporting Tasks.

commit ffa7fd06d1c85e8aa45568e27f155309fb9bede9
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Feb 18 13:38:30 2015 -0500

    NIFI-250:
    - Adding the scope to the disable controller service dialog.

commit bbe185609ad037b8f1ed6529e25bea1b60613653
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Feb 18 13:38:11 2015 -0500

    NIFI-250:
    - Updating the controller services references endpoint to accept either an enabled flag for referencing controller services or a state for referencing schedulable components.

commit 4efd9622109f584daddb76d6b5de2399bd7d8209
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Feb 18 09:49:34 2015 -0500

    NIFI-250:
    - Updating the polling for controller service status when disabling.

commit 2ac643292c691d451cf98e2a309bff72182b0a88
Merge: d30a184 6457929
Author: Mark Payne <ma...@hotmail.com>
Date:   Wed Feb 18 08:15:45 2015 -0500

    Merge branch 'NIFI-250' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit 6457929b25ba5923bebeb6ab4566e4208d1a092e
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Feb 17 15:32:31 2015 -0500

    NIFI-250:
    - Adding auditing to activating/deactivating controller service references.

commit e61a01ac6968ba2f1e4f8fa1d3ca9c2cbca400e6
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Feb 17 14:55:44 2015 -0500

    NIFI-250:
    - Only showing the enable button when the controller service is valid.
    - Adding polling during deactivation of controller service references to know when it's ok to attempt to disable the controller service.

commit f22407f59347010aede5c12cd64652a381f4fd59
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Feb 17 14:54:32 2015 -0500

    NIFI-250:
    - Updating the controller service references endpoint to accept an activated flag that drivens whether processors/reporting tasks are started/stopped and controller services enabled/disabled.

commit e4e61daa26c996d3234982cf1409bb501631b9d0
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Feb 17 12:48:39 2015 -0500

    NIFI-250:
    - Updating fill color dialog styling for use in updated modal plugin.
    - Making the fill color dialog draggable.

commit 4ce7202d93551acaea8e6f782df694eee1435ec5
Merge: 2769db7 0047fa4
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Feb 17 12:36:47 2015 -0500

    Merge branch 'develop' into NIFI-250

    Conflicts:
    	nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css

commit 2769db7b4e783540b03bf2fee800a56d86e79d4d
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Feb 11 16:17:03 2015 -0500

    NIFI-250:
    - Adding an enable controller service dialog.

commit d30a1843c24caccf822811649332dad7e1aa5b17
Author: Mark Payne <ma...@hotmail.com>
Date:   Wed Feb 11 16:08:20 2015 -0500

    NIFI-250: Deleted dead code

commit 62b0669e121e817cd80ba21b8104ad9e210ea531
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Feb 11 11:25:04 2015 -0500

    NIFI-250:
    - Renaming references to referencing components to be more accurate.
    - Fixing borders around the referencing components.

commit 819c76b885268db294b62e11095faf52eaebf57c
Merge: ceede01 371e010
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Feb 11 11:14:37 2015 -0500

    Merge branch 'NIFI-250' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit 371e0100d35864f0092b133a7decac34ffb4be33
Author: Mark Payne <ma...@hotmail.com>
Date:   Wed Feb 11 11:11:49 2015 -0500

    NIFI-250: Move activate/deactive methods for controller services' referencing components to ControllerServiceProvider

commit ceede01b8ffe57c003ac246b51854dd6b01ad70e
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Feb 11 10:45:14 2015 -0500

    NIFI-250:
    - Adding endpoints for retrieving and updating controller service referencing components.

commit 8a414b3ca9a542170cf31d6396c3e3e2947b90fc
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Feb 11 10:43:37 2015 -0500

    NIFI-250:
    - Adding endpoints for retrieving and updating controller service referencing components.

commit ed4d22c1ff17395a683a83d9f57dfdc842ce175f
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Feb 11 09:22:18 2015 -0500

    NIFI-250:
    - Additionally refactoring of removal of ControllerServiceProvider usage.

commit 0d2041b05ffc618c2393713e96c44cf8384aa6e3
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Feb 11 08:54:19 2015 -0500

    NIFI-250:
    - Replacing usage of ControllerServiceProvider with ControllerServiceLookup.

commit 3d38d8c98c180f5446b0f2dafea78eeec38e432e
Merge: 4bc5ed1 56a6bc4
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Feb 11 07:09:05 2015 -0500

    Merge branch 'develop' into NIFI-250

commit 4bc5ed13ef981b49390b1e8460105e8ed227b446
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Feb 10 14:34:48 2015 -0500

    NIFI-250:
    - Rendering the references in the disable controller service dialog.
    - Reloading controller service table when appropriate.
    - Padding in dialogs with a border.

commit 5390c7626a99c076daf05d06118815f902ab7b51
Author: Mark Payne <ma...@hotmail.com>
Date:   Tue Feb 10 11:54:17 2015 -0500

    NIFI-250: Fixed NPE in StandardSSLContextService

commit c25a2caecd31638d3564ea4892986d0580234783
Author: Mark Payne <ma...@hotmail.com>
Date:   Tue Feb 10 11:53:51 2015 -0500

    NIFI-250: Persist Controller Services and Reporting Tasks on NCM

commit 33f551630ac2664cf938834c7e450d04da7ec9f0
Author: Mark Payne <ma...@hotmail.com>
Date:   Tue Feb 10 11:08:05 2015 -0500

    NIFI-250: Remove references to Controller Service when the referencing component is removed

commit ea8cb59848e5cc75c201ce1b9521aa56eb293674
Author: Mark Payne <ma...@hotmail.com>
Date:   Tue Feb 10 09:48:01 2015 -0500

    NIFI-250: Expose number of active threads in ProcessorNode, ReportingTaskNode

commit 40e25066742f910a2c34881eb216a18d5233cdf1
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Feb 9 14:56:22 2015 -0500

    NIFI-250:
    - Reloading components that reference controller services when appropriate.
    - Created an icon for enabling a controller service.

commit 4481213849cee69537c23d54871c4dc07bd7fb87
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Feb 9 13:28:20 2015 -0500

    NIFI-250:
    - Updating dialogs to address issues when showing the borders within the modal plugin (especially in the controller service configuration dialog).
    - Fixing availability when creating a controller service in standalone.

commit 94d112b6ec90cc9df4adc9b85cc9df45908a1f36
Merge: c7ee77b b64fe47
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Feb 9 09:48:23 2015 -0500

    Merge branch 'develop' into NIFI-250

commit c7ee77be9781057109021c9db6981d9905397b3e
Merge: 7da7c93 0133f84
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Feb 9 07:35:46 2015 -0500

    Merge branch 'develop' into NIFI-250

commit 7da7c9327029a84581c5aff5f3435f2706d2c18e
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Feb 6 15:32:09 2015 -0500

    NIFI-250:
    - Updating the UI to gather the availability for a controller service during creation.
    - Removing the concept of availability from the core as the service/task can only be available on the cluster manager OR nodes.
    - Updating web tier to support this concept.

commit 55949a428f45b7e615c09d6f870196fcc95acfe7
Author: Mark Payne <ma...@hotmail.com>
Date:   Fri Feb 6 10:45:59 2015 -0500

    NIFI-250: Removed 'Availability' concept from reporting tasks and controller services because it is implicitly, depending on if on node or ncm

commit d95ca18a0cc6834defbf184e2cae5ee93228e832
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Feb 6 08:55:07 2015 -0500

    NIFI-250:
    - Javadocs.

commit 6892f19d61ebd4ae227b43452ba9c4303bd522eb
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Feb 6 07:52:38 2015 -0500

    NIFI-250:
    - Including the user who last modified the flow in the refresh tooltip.

commit 9f9466cb418b01295ecbe727dca07e82ddda9cb5
Merge: 4a8da60 d36a71c
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Feb 6 07:17:11 2015 -0500

    Merge branch 'develop' into NIFI-250

commit 4a8da60334f4b78a123906a6a712d95ec873c687
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Feb 5 15:59:35 2015 -0500

    NIFI-250:
    - Refactoring revision checking so that we can lock appropriately on the Cluster Manager to manage controller services running there while other concurrent requests can be replicated amongst the cluster.

commit 17add531f656b6d8065bed74a887acaa00a07f9a
Merge: 3425dee 4a49ba7
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Feb 5 07:13:56 2015 -0500

    Merge branch 'develop' into NIFI-250

commit 3425dee1f2aca679235b434b40bb49be8099ff55
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Feb 4 14:04:38 2015 -0500

    NIFI-250:
    - Invoking the appropriate save when updating controller services.

commit 6b91546d9d04dcb127cada3cdc907e9ef98abf42
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Feb 4 13:13:09 2015 -0500

    NIFI-250:
    - Fixing use of the optimistic locking manager factory bean.

commit cc8b096ffa11ee44f6d5fb5f07de51826dd27b20
Merge: 13fb1a7 ed53b46
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Feb 4 12:44:56 2015 -0500

    Merge branch 'develop' into NIFI-250

    Conflicts:
    	nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/history/nf-history-table.js
    	nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
    	nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/PostHTTP.java

commit 13fb1a758b59cd7e205caed694f28b4d0dba9d82
Merge: 22822d3 037f36d
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Feb 2 09:27:26 2015 -0500

    Merge branch 'develop' into NIFI-250

commit 22822d33a2159d1f9a2b12caafbce7c2b46ebe52
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Feb 2 09:10:12 2015 -0500

    NIFI-250:
    - Updating the ControllerService endpoint to specify the availability of the service.
    - Updating the optimistic locking manager to ensure the proper revision is checked.

commit 346cc0cf14aad3f2240336eda711e63f29e62a7c
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Feb 2 07:55:57 2015 -0500

    NIFI-250:
    - Using the ControllerServiceProvider instead of delegating directly to the FlowController. This will allow us to work with the NCM when clustered.

commit 0cb1adbc1f1bf0272be813bf58b90444243b65b5
Merge: d1caa7e cc25d1d
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Feb 2 07:52:38 2015 -0500

    Merge branch 'NIFI-250' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit d1caa7ee3e2e8662eeb2b368b2ed81fface22711
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Feb 2 07:52:32 2015 -0500

    NIFI-250:
    - Updating REST API for managing controller services.
    - Starting to add a dialog for disabling a controller service.

commit cc25d1d21cae8b30eff0db66d1ad9b6bbae5351d
Author: Mark Payne <ma...@hotmail.com>
Date:   Fri Jan 30 14:43:54 2015 -0500

    NIFI-250: Updated to pass Availability when creating controller services

commit e056bb7d70de8e1dea7bab9f59d9f466aef45eda
Author: Mark Payne <ma...@hotmail.com>
Date:   Fri Jan 30 14:37:42 2015 -0500

    NIFI-250: Fixed bug that caused EOFException if no reporting tasks defined on NCM

commit 4ae2a10e6d130353ea34aa403d3bcbad7a2ab4e8
Author: Mark Payne <ma...@hotmail.com>
Date:   Fri Jan 30 13:01:00 2015 -0500

    Load Controller Services from flow.tar file instead and external file

commit 52149d8510d8c517063bfd6359bde3950862db77
Author: Mark Payne <ma...@hotmail.com>
Date:   Fri Jan 30 12:44:30 2015 -0500

    NIFI-250: Change Availability to just NODE or NCM; update NCM to store controller services and reporting tasks in separate entries in tar file instead of in flow.xml

commit 3102e08378b34cb869c708faa05c427de6b68139
Author: Mark Payne <ma...@hotmail.com>
Date:   Fri Jan 30 10:18:54 2015 -0500

    NIFI-250: Updated ControllerServiceProvider to have a 'createControllerService' that takes no identifier and randomly generates one

commit 3344cef3365dfc620f11aaf18fcfc8ba7d58e16a
Author: Mark Payne <ma...@hotmail.com>
Date:   Fri Jan 30 09:05:58 2015 -0500

    NIFI-250: Updated controller services to use appropriate defaults and use .identifiesControllerService instead of using the old way of obtaining controller services; do not fail to startup if controller service is invalid

commit 1682e47aa3820bca6ec80e6009a16d0572fb1a9b
Merge: fbc14e0 e7beef8
Author: Mark Payne <ma...@hotmail.com>
Date:   Fri Jan 30 08:36:55 2015 -0500

    Merge branch 'NIFI-250' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit 2de754a11af2f3d545ae9358c06dd6838e3d920c
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Jan 30 08:35:18 2015 -0500

    NIFI-250:
    - Showing controller service validation errors.

commit 600f8f7869161b50dc9939401ce8a91a64e3462a
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Jan 30 07:46:47 2015 -0500

    NIFI-250:
    - Adding support to go to a controller service.

commit e7beef8d224797c057650787c4041b15dbef87af
Merge: 1ebaf1d bafa945
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Jan 30 07:22:25 2015 -0500

    Merge branch 'develop' into NIFI-250

commit fbc14e0a3a46c0511d6135e86797cfc14c81d7d5
Merge: 9a6acab 1ebaf1d
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Jan 29 16:26:08 2015 -0500

    Merge branch 'NIFI-250' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit 1ebaf1d29c7294b07f68def9d463c791f6d68cf9
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Jan 29 13:06:54 2015 -0500

    NIFI-250:
    - Addressing the border visibility when toggling a reference block.

commit b98a7220c04582ad7305429ddee850096c2139cd
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Jan 29 12:56:15 2015 -0500

    NIFI-250:
    - Showing components that reference a given controller service.

commit 9a6acab3734fbbb670fb5db2388ac85ef9b6fc6d
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Jan 29 11:24:56 2015 -0500

    NIFI-250: Do not try to load controller services and reporting tasks from old .xml files because they are now in flow.xml

commit 60ad998153f6c391f4a36f25b91629bde7d3e98b
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Jan 29 11:24:11 2015 -0500

    NIFI-250: Load Controller Services before processors or reporting tasks on restart

commit cfaafa3c291bbbe1009c10c10204befea0482bbe
Merge: 52d329c 4737a41
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Jan 29 10:55:09 2015 -0500

    Merge branch 'NIFI-250' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit 4737a41a9ce7de540c7dce1d19e54802ce9127a9
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Jan 29 10:51:18 2015 -0500

    NIFI-250:
    - Addressing some issues with reloading controller services on restart.
    - Starting to add the controller service references to the dialog.
    - Reloading processors and controller services before configuration to ensure their property descriptors are current.

commit 52d329ca036e059f50a169b726f4c202859ed0e1
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Jan 29 10:02:15 2015 -0500

    NIFI-250: Restore appropriate reporting task id on restart

commit 59f5b95c3ac3b1660da35aec63c22b23347a5aa3
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Jan 29 09:56:55 2015 -0500

    NIFI-250: Restore Controller Service using appropriate ID on restart

commit d60710b6fbbddc8bb1b6d8f5a7b078bbf7e4bd0a
Merge: f18aca1 bb628b0
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Jan 29 09:05:57 2015 -0500

    Merge branch 'NIFI-250' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit f18aca1e0a6a701b9390cbe80e131296fbcbc7e7
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Jan 29 09:05:43 2015 -0500

    NIFI-250: Fixed bug where validator returns controller service identifier instead of name in description. Fixed bug where controller serivce references are not updated when property is removed from processor/service/reporting task

commit 31caadb20a85c2afa53969b1bd60f1979e1f004f
Author: Mark Payne <ma...@hotmail.com>
Date:   Thu Jan 29 08:49:44 2015 -0500

    NIFI-250: Fixed bug that caused IllegalStateException if property not set on controller service or reporting task

commit bb628b07a905ddb7b6dbf0c7c216d00627f82668
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Jan 28 15:55:00 2015 -0500

    NIFI-250:
    - Adding support for showing property history for controller services.

commit 0d1f80f22ecae9d8c5cc63fb7224ac551c0571d2
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Jan 28 15:54:26 2015 -0500

    NIFI-250:
    - Adding support for showing property history for controller services.

commit 5f571be29a4d934ec65701093dcc67964664b3f7
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Jan 28 15:02:02 2015 -0500

    NIFI-250:
    - Allowing read only users to view the controller settings (including controller services and reporting tasks).

commit 4564a137cbd929b7620c1008a1fc14fa60680421
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Jan 28 14:28:47 2015 -0500

    NIFI-250:
    - Showing refresh notice in the settings tab when the flow changes externally (another user).

commit c9d9d3199a40b602f1dfb2ccc532c264b4e223ab
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Jan 28 13:13:36 2015 -0500

    NIFI-250:
    - Auditing controller service actions.

commit 0aee34c287c28a01022f23a8a5809de489282734
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Jan 28 12:40:02 2015 -0500

    NIFI-250:
    - Auditing controller service actions.

commit d90e9d6a7203db4e612ca27fb2567dd2b92c8d88
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Jan 28 10:51:36 2015 -0500

    NIFI-250:
    - Documentation.
    - Prepping the list of controller service references.

commit 6581eb1360902225312d532c07b86343df175bc5
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Jan 28 10:32:15 2015 -0500

    NIFI-250:
    - Including the controller service references.

commit 68583ab90a50f5929859144326dc105cc4753acd
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Jan 28 09:36:34 2015 -0500

    NIFI-250:
    - Fixing the NODE_ONLY value.

commit e06e423ff191696f0ab2a081434ac7b26e183b91
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Jan 28 09:30:33 2015 -0500

    NIFI-250:
    - Only showing the availability field when clustered.

commit 91aa952f017dac0bf8cb1597ec3440f9bd8cda7a
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Jan 28 09:24:27 2015 -0500

    NIFI-250:
    - Adding support to enable/disable controller services through the actions in the table.

commit 5d5cb8f106c630e92852cf0fdefdad1c602c4520
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Jan 28 08:35:17 2015 -0500

    NIFI-250:
    - Wiring up updating and deleting controller services.
    - Ensuring the new property dialog is closed when clearing the grid.

commit 2303570eb68da63ebbd3a438555e3f523ec7308d
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Jan 28 08:33:42 2015 -0500

    NIFI-250:
    - Adding parameters to the update controller service endpoint.

commit 593afddaf0d1a0746064ad0cf3b74a7b06dc7031
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Jan 27 16:21:41 2015 -0500

    NIFI-250:
    - Documentation.
    - Updating how actions are triggered in the controller service table.

commit cf188ebc838ba3f0c26ae14c30e73b8a33510735
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Jan 27 16:20:35 2015 -0500

    NIFI-250:
    - Starting to implement the controller service DAO.

commit 526e18d00a1bdf8a63ebc70fd257ea23bf678425
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Jan 27 15:20:01 2015 -0500

    NIFI-250:
    - Using the property table plugin in the read only processor details dialog.
    - Clean up.

commit 2d7c700d173e757ca51b7b9832c9b385a67061c5
Merge: ec082f1 ea17dbe
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Jan 27 13:53:02 2015 -0500

    Merge branch 'NIFI-250' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit ec082f1eead6b37e3b26e579f958cc0cde154d22
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Jan 27 13:52:42 2015 -0500

    NIFI-250:
    - Creating a jQuery plugin for rendering a table of properties (since we now want to configure processors, controller services, and reporting tasks).

commit 5785198f66c2d3d2aa17767a8941f194afc5638b
Author: Mark Payne <ma...@hotmail.com>
Date:   Tue Jan 27 09:56:50 2015 -0500

    NIFI-250: Removed TODO comment that was completed

commit ea17dbec6e1b4384c91fab9e54e30b29c6190c8d
Author: Mark Payne <ma...@hotmail.com>
Date:   Tue Jan 27 09:48:45 2015 -0500

    NIFI-250: Renamed 'getComment' and 'setComment' in ControllerServiceNode and ReportingTaskNode to 'getComments' and 'setComments' to be more consistent with how naming throughout the rest of the app

commit f14a34f6686621501ddcc68efbcc64f001c96ab8
Author: Mark Payne <ma...@hotmail.com>
Date:   Tue Jan 27 09:22:40 2015 -0500

    NIFI-250: Updated flow.xml schema to take reporting tasks and controller services into account; removed schemas for reporting tasks and controller services.

commit 24b4f1cf1109af53d6fd94d81fe81141e7dd0423
Merge: 4de0fd0 cb84829
Author: Mark Payne <ma...@hotmail.com>
Date:   Tue Jan 27 08:36:57 2015 -0500

    Merge branch 'NIFI-250' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit cb84829b31cd709cedf866ac41854c3a2e74a158
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Jan 27 08:29:50 2015 -0500

    NIFI-250:
    - Renaming comment to comments.
    - Ensuring type is included in DTO and create request.

commit 4de0fd02678ce13d5a8b08edbc35a677560ded4d
Author: Mark Payne <ma...@hotmail.com>
Date:   Mon Jan 26 19:05:31 2015 -0500

    NIFI-250: Only run controller services and reporting tasks on the nodes/ncm according to their Availability

commit 2df4500c05ac91d46614e9ca41974fed890d2646
Author: Mark Payne <ma...@hotmail.com>
Date:   Mon Jan 26 15:17:04 2015 -0500

    NIFI-250: Implemented fingerprinting and reloading of controller services and reporting tasks from flow.xml

commit 86d15f9e1c3b28a9ae587a1cb0c73cd2b6e8b51c
Author: Mark Payne <ma...@hotmail.com>
Date:   Mon Jan 26 13:55:55 2015 -0500

    NIFI-250: Serialize and deserialize controller services and reporting tasks in the flow.xml

commit ff43b039085c56bb5f53148e25878ad71972a3db
Merge: 7a3d208 2da5792
Author: Mark Payne <ma...@hotmail.com>
Date:   Mon Jan 26 11:53:17 2015 -0500

    NIFI-250: Merged changes

commit 7a3d208f031122ab13349ace8aeafac9fe5c54ac
Author: Mark Payne <ma...@hotmail.com>
Date:   Mon Jan 26 11:42:01 2015 -0500

    NIFI-250: Allow Controller Services to be created without id and properties; allow controller service lookup to provide name of controller service given an id

commit 80f02e47256590c57a811b79040b70c827e71ff2
Author: Mark Payne <ma...@hotmail.com>
Date:   Mon Jan 26 10:31:06 2015 -0500

    Added Eclipse-specific files to .gitignore

commit 2da57924286fe1f9ae320e270ba0aba77a08d8b7
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Jan 26 09:39:14 2015 -0500

    NIFI-250:
    - Ensuring the tables are resized when appropriate.

commit 1854ebed22387d0f1a82bd9c52ab1ab5ae5c7e21
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Jan 26 09:38:59 2015 -0500

    NIFI-250:
    - Fixing NPE in stubbed out method.

commit 3e77e7d32e2c4e39a8247eacb44681a57bc3b618
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Jan 26 09:29:34 2015 -0500

    NIFI-250:
    - Supporting updated API.

commit e989215fa539f61eba7c915d1b04adebcc771c69
Merge: 0c021c7 6b560b9
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Jan 26 09:19:12 2015 -0500

    Merge branch 'develop' into NIFI-250

commit 0c021c782589b4fe439dcb0f81d229fde6cd9327
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Jan 26 09:18:06 2015 -0500

    NIFI-250:
    - Starting to load the controller services and prepare for adding them.

commit 35616e9ad64cd2985cf2e2cd1a2aa5fce33d980d
Merge: 6622317 33cee9d
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Jan 26 07:15:00 2015 -0500

    Merge branch 'develop' into NIFI-250

commit 6622317c1bcfb6b0a8f15cd1889fd644b2264a3a
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Jan 23 10:54:34 2015 -0500

    NIFI-250:
    - Adding a field to collect the controller service name during creation.

commit cb4c654c7f244ee7222fb444ccca096b8f56e164
Merge: e604caf 43e646e
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Jan 23 09:30:27 2015 -0500

    Merge branch 'develop' into NIFI-250

commit e604cafe79f5a90e9179a9f2e46e3cbbb7f47380
Author: Matt Gilman <ma...@gmail.com>
Date:   Thu Jan 22 15:24:17 2015 -0500

    NIFI-250:
    - Adding an endpoint to manage ControllerServices.

commit c8ea6830672350e1469a21eeda2ce02a6f6236ac
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Jan 21 08:03:15 2015 -0500

    NIFI-250:
    - Moving files into the appropriate renamed directories.

commit f2b9f2c8d418d14fd982d721ccbb6b2bb0874de1
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Jan 21 08:02:50 2015 -0500

    NIFI-250:
    - Moving files into the appropriate renamed directories.

commit c5d452c1435d3d32df3da4100ca740ca372adb3d
Merge: e8d2bbf cff8b0d
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Jan 21 07:37:09 2015 -0500

    Merge branch 'develop' into NIFI-250

commit e8d2bbfe3c286fc21dea04e993771985d843b6e6
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Jan 20 13:35:18 2015 -0500

    NIFI-250:
    - Making the new controller services table collapsible.
    - Addressing issues with filtering, rollup, tags, and collapsing.

commit 4836385628b5622aa77c8adacd3a5241f8ee1829
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Jan 20 12:28:41 2015 -0500

    NIFI-250:
    - Ensuring the entire hierarchy is returned for each service type.

commit 66bd61e07bff17ddf7d672088e13d0b01bbfc96f
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Jan 20 08:40:37 2015 -0500

    Merge branch 'NIFI-250' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

    Conflicts:
    	nifi/nar-bundles/framework-bundle/framework/web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
    	nifi/nar-bundles/framework-bundle/framework/web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java

commit 1392f328c905a7919d2555767cb53395409e3e1e
Merge: 39a77e4 b29b61a
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Jan 20 08:34:05 2015 -0500

    Merge branch 'NIFI-250' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

    Conflicts:
    	nifi/nar-bundles/framework-bundle/framework/web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
    	nifi/nar-bundles/framework-bundle/framework/web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java

commit 39a77e4828e41f635f2f85c68b78ca9e21ad1970
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Jan 13 14:52:23 2015 -0500

    NIFI-250:
    - Creating endpoints for returning the available controller services and reporting tasks.
    - Made the Setting tabbed to provide a place for configuring controller services and reporting tasks.

commit 58b02e41eec54b1eff86a90205ab69f007ad1b00
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Jan 16 12:02:33 2015 -0500

    NIFI-250: merging from develop

commit b204688bfa738c47bed71a1aab6c46171b21107f
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Jan 16 11:59:51 2015 -0500

    NIFI-250:
    - Starting to making the available controller services collapsible. Still need to clean up and handle selection properly.

commit b39fdbd6052adf342f72f44764145a3564669920
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Jan 13 14:52:23 2015 -0500

    NIFI-250:
    - Creating endpoints for returning the available controller services and reporting tasks.
    - Made the Setting tabbed to provide a place for configuring controller services and reporting tasks.

    NIFI-250:
    - Extracting the tagcloud into a re-usable jQuery plugin.

    NIFI-250:
    - Creating a new controller service dialog. Dialog may be refactored into a reusable widget if possible.
    - Loading the new controller service dialog with all available controller services.
    - Fixing typos.

commit b29b61a5a8290b1b0a5513786227d7036e37aa6b
Merge: 8976ff4 bb108a0
Author: Matt Gilman <ma...@gmail.com>
Date:   Mon Jan 19 13:15:03 2015 -0500

    Merge branch 'NIFI-250' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

    Conflicts:
    	nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/settings-content.jsp
    	nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/css/settings.css
    	nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
    	nifi/nar-bundles/framework-bundle/framework/web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
    	nifi/nar-bundles/framework-bundle/framework/web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java

commit 8976ff49834ad4116b0fe9425be00e86da74b1a7
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Jan 16 12:02:33 2015 -0500

    NIFI-250: merging from develop

commit 367baad5b6d9d633610e3547c75776338989f785
Author: Matt Gilman <ma...@gmail.com>
Date:   Fri Jan 16 11:59:51 2015 -0500

    NIFI-250:
    - Starting to making the available controller services collapsible. Still need to clean up and handle selection properly.

commit 14da3f3de97f4b72afe591026c2690f92f4f7606
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Jan 13 14:52:23 2015 -0500

    NIFI-250:
    - Creating endpoints for returning the available controller services and reporting tasks.
    - Made the Setting tabbed to provide a place for configuring controller services and reporting tasks.

    NIFI-250:
    - Extracting the tagcloud into a re-usable jQuery plugin.

    NIFI-250:
    - Creating a new controller service dialog. Dialog may be refactored into a reusable widget if possible.
    - Loading the new controller service dialog with all available controller services.
    - Fixing typos.

commit bb108a0960d6095791afd367bee91e62d8d605da
Merge: da18ce0 03d422e
Author: Matt Gilman <ma...@gmail.com>
Date:   Wed Jan 14 13:34:45 2015 -0500

    Merge branch 'NIFI-250' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into NIFI-250

commit da18ce0ab0fee2e497e65ff4af30dd8dac212e96
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Jan 13 14:52:23 2015 -0500

    NIFI-250:
    - Creating endpoints for returning the available controller services and reporting tasks.
    - Made the Setting tabbed to provide a place for configuring controller services and reporting tasks.

commit 03d422e4d4d65ebe4b080fc5ec88cc7aae30ae82
Author: Matt Gilman <ma...@gmail.com>
Date:   Tue Jan 13 14:52:23 2015 -0500

    NIFI-250:
    - Creating endpoints for returning the available controller services and reporting tasks.
    - Made the Setting tabbed to provide a place for configuring controller services and reporting tasks.


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/e9647717
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/e9647717
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/e9647717

Branch: refs/heads/NIFI-25
Commit: e9647717e39d4046092ff669e5ccbcd0c2ffbd89
Parents: e7d6d94
Author: Matt Gilman <ma...@gmail.com>
Authored: Tue Mar 31 16:45:05 2015 -0400
Committer: Matt Gilman <ma...@gmail.com>
Committed: Tue Mar 31 16:45:05 2015 -0400

----------------------------------------------------------------------
 .../nifi/annotation/lifecycle/OnAdded.java      |   13 +-
 .../nifi/annotation/lifecycle/OnDisabled.java   |   29 +-
 .../nifi/annotation/lifecycle/OnEnabled.java    |   34 +-
 .../nifi/annotation/lifecycle/OnRemoved.java    |   14 +-
 .../nifi/annotation/lifecycle/OnShutdown.java   |   13 +-
 .../nifi/annotation/lifecycle/OnStopped.java    |    9 +
 .../annotation/lifecycle/OnUnscheduled.java     |    2 -
 .../nifi/components/PropertyDescriptor.java     |   14 +-
 .../nifi/components/ValidationContext.java      |    9 +
 .../controller/AbstractControllerService.java   |   13 +-
 .../ControllerServiceInitializationContext.java |   10 +
 .../controller/ControllerServiceLookup.java     |   19 +
 .../org/apache/nifi/logging/ComponentLog.java   |  100 +
 .../org/apache/nifi/logging/ProcessorLog.java   |   61 +-
 .../nifi/reporting/AbstractReportingTask.java   |   10 +
 .../ReportingInitializationContext.java         |   10 +
 .../nifi/web/ClusterRequestException.java       |    1 +
 .../org/apache/nifi/web/ComponentDetails.java   |  157 ++
 .../apache/nifi/web/ConfigurationAction.java    |  137 ++
 .../nifi/web/NiFiWebConfigurationContext.java   |  102 ++
 .../web/NiFiWebConfigurationRequestContext.java |   31 +
 .../org/apache/nifi/web/NiFiWebContext.java     |    1 +
 .../apache/nifi/web/NiFiWebContextConfig.java   |    1 +
 .../apache/nifi/web/NiFiWebRequestContext.java  |   58 +
 .../nifi/web/ProcessorConfigurationAction.java  |    1 +
 .../java/org/apache/nifi/web/ProcessorInfo.java |    1 +
 .../main/java/org/apache/nifi/web/Revision.java |   32 +-
 .../org/apache/nifi/web/UiExtensionType.java    |   31 +
 nifi/nifi-assembly/pom.xml                      |    2 -
 .../nifi/processor/util/StandardValidators.java |   20 +-
 .../org/apache/nifi/util/NiFiProperties.java    |    2 -
 .../client/socket/EndpointConnectionPool.java   |    2 +-
 .../socket/TestEndpointConnectionStatePool.java |    4 +-
 ...kControllerServiceInitializationContext.java |   17 +
 .../nifi/util/MockControllerServiceLookup.java  |   11 +
 .../MockProcessorInitializationContext.java     |   10 +
 .../org/apache/nifi/util/MockProcessorLog.java  |   34 +-
 .../MockReportingInitializationContext.java     |   10 +-
 .../apache/nifi/util/MockValidationContext.java |   15 +
 .../nifi/util/StandardProcessorTestRunner.java  |    5 +-
 .../org/apache/nifi/admin/dao/ActionDAO.java    |    6 +-
 .../nifi/admin/dao/impl/StandardActionDAO.java  |   60 +-
 .../apache/nifi/admin/service/AuditService.java |    6 +-
 .../admin/service/action/GetPreviousValues.java |    8 +-
 .../service/impl/StandardAuditService.java      |    4 +-
 .../nifi/web/api/dto/ComponentHistoryDTO.java   |   56 +
 .../web/api/dto/ControllerConfigurationDTO.java |   18 +
 .../nifi/web/api/dto/ControllerServiceDTO.java  |  190 ++
 ...ontrollerServiceReferencingComponentDTO.java |  207 +++
 .../nifi/web/api/dto/DocumentedTypeDTO.java     |   20 +-
 .../apache/nifi/web/api/dto/FlowSnippetDTO.java |   15 +-
 .../nifi/web/api/dto/NiFiComponentDTO.java      |    4 +-
 .../nifi/web/api/dto/ProcessorConfigDTO.java    |  218 +--
 .../nifi/web/api/dto/ProcessorHistoryDTO.java   |   56 -
 .../nifi/web/api/dto/PropertyDescriptorDTO.java |  243 +++
 .../nifi/web/api/dto/ReportingTaskDTO.java      |  228 +++
 .../apache/nifi/web/api/dto/RevisionDTO.java    |   15 +
 .../component/details/ComponentDetailsDTO.java  |    2 +-
 .../component/details/ExtensionDetailsDTO.java  |   41 +
 .../component/details/ProcessorDetailsDTO.java  |   41 -
 .../web/api/entity/ComponentHistoryEntity.java  |   45 +
 .../web/api/entity/ControllerServiceEntity.java |   45 +
 ...ollerServiceReferencingComponentsEntity.java |   46 +
 .../entity/ControllerServiceTypesEntity.java    |   46 +
 .../api/entity/ControllerServicesEntity.java    |   46 +
 .../web/api/entity/ProcessorHistoryEntity.java  |   45 -
 .../api/entity/PropertyDescriptorEntity.java    |   46 +
 .../web/api/entity/ReportingTaskEntity.java     |   45 +
 .../api/entity/ReportingTaskTypesEntity.java    |   46 +
 .../web/api/entity/ReportingTasksEntity.java    |   46 +
 ...kControllerServiceInitializationContext.java |    6 +
 .../mock/MockControllerServiceLookup.java       |   10 +
 .../MockReportingInitializationContext.java     |    8 +-
 .../nifi-framework-cluster-web/pom.xml          |    4 -
 .../context/ClusterContextThreadLocal.java      |    7 +-
 .../ClusterAwareOptimisticLockingManager.java   |   96 -
 .../nifi-framework-cluster/pom.xml              |    4 +
 .../nifi/cluster/flow/ClusterDataFlow.java      |   15 +-
 .../cluster/flow/DataFlowManagementService.java |   17 +
 .../nifi/cluster/flow/impl/DataFlowDaoImpl.java |   43 +-
 .../impl/DataFlowManagementServiceImpl.java     |   65 +-
 .../nifi/cluster/manager/NodeResponse.java      |   14 +
 .../cluster/manager/impl/WebClusterManager.java |  929 ++++++++--
 .../spring/WebClusterManagerFactoryBean.java    |   23 +-
 .../resources/nifi-cluster-manager-context.xml  |    4 +
 .../nifi-framework-core-api/.gitignore          |    1 +
 .../controller/AbstractConfiguredComponent.java |   18 +-
 .../apache/nifi/controller/Availability.java    |   24 -
 .../nifi/controller/ProcessScheduler.java       |   27 +-
 .../apache/nifi/controller/ProcessorNode.java   |   16 +
 .../nifi/controller/ReportingTaskNode.java      |   26 +-
 .../controller/ValidationContextFactory.java    |    4 +
 ...ControllerServiceInstantiationException.java |   51 +
 .../ControllerServiceNotFoundException.java     |   51 -
 .../reporting/ReportingTaskProvider.java        |  103 ++
 .../service/ControllerServiceNode.java          |   39 +-
 .../service/ControllerServiceProvider.java      |   83 +-
 .../service/ControllerServiceReference.java     |    7 +-
 .../service/ControllerServiceState.java         |   45 +
 .../apache/nifi/controller/FlowController.java  |  205 ++-
 .../nifi/controller/FlowFromDOMFactory.java     |   60 +-
 .../nifi/controller/StandardFlowSerializer.java |   68 +-
 .../nifi/controller/StandardFlowService.java    |    9 +-
 .../controller/StandardFlowSynchronizer.java    |  236 ++-
 .../nifi/controller/StandardProcessorNode.java  |   47 +-
 .../apache/nifi/controller/TemplateManager.java |   33 +-
 .../reporting/AbstractReportingTaskNode.java    |   72 +-
 .../reporting/StandardReportingContext.java     |   11 +
 .../StandardReportingInitializationContext.java |   23 +-
 .../scheduling/StandardProcessScheduler.java    |  208 ++-
 .../service/ControllerServiceLoader.java        |  149 +-
 ...dControllerServiceInitializationContext.java |   20 +-
 .../service/StandardControllerServiceNode.java  |  122 +-
 .../StandardControllerServiceProvider.java      |  450 ++++-
 .../StandardControllerServiceReference.java     |   19 +-
 .../controller/tasks/ReportingTaskWrapper.java  |   29 +-
 .../nifi/fingerprint/FingerprintFactory.java    |   79 +
 .../nifi/groups/StandardProcessGroup.java       |   28 +-
 .../nifi/persistence/FlowConfigurationDAO.java  |   25 -
 .../StandardXMLFlowConfigurationDAO.java        |  191 +-
 .../nifi/processor/SimpleProcessLogger.java     |   36 +-
 .../nifi/processor/StandardProcessContext.java  |   10 +
 .../processor/StandardSchedulingContext.java    |    5 +-
 .../processor/StandardValidationContext.java    |   12 +
 .../StandardValidationContextFactory.java       |    5 +
 .../java/org/apache/nifi/util/DomUtils.java     |   10 +
 .../ControllerServiceConfiguration.xsd          |   61 -
 .../src/main/resources/FlowConfiguration.xsd    |   49 +-
 .../resources/ReportingTaskConfiguration.xsd    |   87 -
 .../StandardControllerServiceProviderTest.java  |    2 +-
 .../TestStandardControllerServiceProvider.java  |  385 ++++
 .../controller/service/mock/DummyProcessor.java |   49 +
 .../nifi/controller/service/mock/ServiceA.java  |   49 +
 .../nifi/controller/service/mock/ServiceB.java  |   23 +
 .../processor/TestStandardPropertyValue.java    |   11 +-
 .../nifi-framework/nifi-nar-utils/.gitignore    |    1 +
 .../main/resources/conf/controller-services.xml |   18 -
 .../src/main/resources/conf/nifi.properties     |    2 -
 .../src/main/resources/conf/reporting-tasks.xml |   49 -
 .../java/org/apache/nifi/action/Component.java  |    4 +-
 .../component/details/ExtensionDetails.java     |   34 +
 .../component/details/ProcessorDetails.java     |   34 -
 .../HttpServletConfigurationRequestContext.java |   56 +
 .../nifi/web/HttpServletRequestContext.java     |  100 +
 .../web/HttpServletRequestContextConfig.java    |    1 +
 .../nifi-framework/nifi-web/nifi-jetty/pom.xml  |    5 +
 .../org/apache/nifi/web/server/JettyServer.java |  208 ++-
 .../nifi-web/nifi-ui-extension/pom.xml          |   21 +
 .../apache/nifi/ui/extension/UiExtension.java   |   52 +
 .../nifi/ui/extension/UiExtensionMapping.java   |   52 +
 .../nifi-web/nifi-web-api/pom.xml               |    5 +
 .../nifi/audit/ControllerServiceAuditor.java    |  475 +++++
 .../org/apache/nifi/audit/FunnelAuditor.java    |    8 +-
 .../java/org/apache/nifi/audit/NiFiAuditor.java |   11 +-
 .../java/org/apache/nifi/audit/PortAuditor.java |   17 +-
 .../apache/nifi/audit/ProcessGroupAuditor.java  |   18 +-
 .../org/apache/nifi/audit/ProcessorAuditor.java |   20 +-
 .../apache/nifi/audit/RelationshipAuditor.java  |   18 +-
 .../nifi/audit/RemoteProcessGroupAuditor.java   |   22 +-
 .../apache/nifi/audit/ReportingTaskAuditor.java |  353 ++++
 .../org/apache/nifi/audit/SnippetAuditor.java   |   13 +-
 .../org/apache/nifi/web/NiFiServiceFacade.java  |  206 ++-
 .../nifi/web/StandardNiFiServiceFacade.java     | 1371 ++++++++------
 .../StandardNiFiWebConfigurationContext.java    |  736 ++++++++
 .../apache/nifi/web/StandardNiFiWebContext.java |   28 +-
 .../nifi/web/api/ApplicationResource.java       |   76 +-
 .../apache/nifi/web/api/ClusterResource.java    |    2 +-
 .../apache/nifi/web/api/ConnectionResource.java |    6 +-
 .../apache/nifi/web/api/ControllerResource.java |   99 +-
 .../nifi/web/api/ControllerServiceResource.java |  803 ++++++++
 .../org/apache/nifi/web/api/FunnelResource.java |    6 +-
 .../apache/nifi/web/api/HistoryResource.java    |   70 +-
 .../apache/nifi/web/api/InputPortResource.java  |    6 +-
 .../org/apache/nifi/web/api/LabelResource.java  |    6 +-
 .../apache/nifi/web/api/OutputPortResource.java |    6 +-
 .../nifi/web/api/ProcessGroupResource.java      |   16 +-
 .../apache/nifi/web/api/ProcessorResource.java  |   72 +-
 .../web/api/RemoteProcessGroupResource.java     |   10 +-
 .../nifi/web/api/ReportingTaskResource.java     |  663 +++++++
 .../apache/nifi/web/api/SnippetResource.java    |    6 +-
 .../nifi/web/api/config/ThrowableMapper.java    |    7 +-
 .../org/apache/nifi/web/api/dto/DtoFactory.java |  426 ++++-
 .../nifi/web/controller/ControllerFacade.java   |   75 +-
 .../nifi/web/dao/ControllerServiceDAO.java      |  110 ++
 .../apache/nifi/web/dao/ReportingTaskDAO.java   |   88 +
 .../dao/impl/StandardControllerServiceDAO.java  |  320 ++++
 .../nifi/web/dao/impl/StandardProcessorDAO.java |    5 +
 .../web/dao/impl/StandardReportingTaskDAO.java  |  365 ++++
 .../nifi/web/dao/impl/StandardSnippetDAO.java   |   36 +-
 .../ControllerServiceProviderFactoryBean.java   |   68 +
 .../OptimisticLockingManagerFactoryBean.java    |   67 +
 .../ReportingTaskProviderFactoryBean.java       |   69 +
 .../org/apache/nifi/web/util/Availability.java  |   34 +
 .../org/apache/nifi/web/util/SnippetUtils.java  |  181 +-
 .../src/main/resources/nifi-web-api-context.xml |   60 +-
 .../nifi/integration/util/NiFiTestServer.java   |    5 +
 .../nifi-web-optimistic-locking/pom.xml         |   12 +
 .../apache/nifi/web/ConfigurationRequest.java   |   34 +
 .../apache/nifi/web/ConfigurationSnapshot.java  |   22 +-
 .../org/apache/nifi/web/FlowModification.java   |   57 +
 .../nifi/web/OptimisticLockingManager.java      |   76 +-
 .../web/StandardOptimisticLockingManager.java   |  150 +-
 .../org/apache/nifi/web/UpdateRevision.java     |   31 +
 .../nifi/web/security/user/NiFiUserUtils.java   |   10 +
 .../nifi-framework/nifi-web/nifi-web-ui/pom.xml |   12 +-
 .../main/resources/filters/canvas.properties    |    8 +-
 .../src/main/webapp/WEB-INF/pages/canvas.jsp    |   11 +-
 .../src/main/webapp/WEB-INF/pages/summary.jsp   |    2 +
 .../WEB-INF/partials/canvas/canvas-header.jsp   |    2 +-
 .../canvas/controller-service-configuration.jsp |   90 +
 .../disable-controller-service-dialog.jsp       |   71 +
 .../canvas/enable-controller-service-dialog.jsp |   70 +
 .../canvas/new-controller-service-dialog.jsp    |   53 +
 .../partials/canvas/new-processor-dialog.jsp    |    6 +-
 .../canvas/new-processor-property-dialog.jsp    |   34 -
 .../canvas/new-reporting-task-dialog.jsp        |   53 +
 .../partials/canvas/processor-configuration.jsp |   21 +-
 .../WEB-INF/partials/canvas/registration.jsp    |    2 +-
 .../canvas/reporting-task-configuration.jsp     |  107 ++
 .../partials/canvas/settings-content.jsp        |   98 +-
 .../WEB-INF/partials/processor-details.jsp      |    4 -
 .../nifi-web-ui/src/main/webapp/css/about.css   |    1 -
 .../nifi-web-ui/src/main/webapp/css/canvas.css  |    4 +
 .../webapp/css/connection-configuration.css     |    1 -
 .../src/main/webapp/css/connection-details.css  |    1 -
 .../src/main/webapp/css/controller-service.css  |  269 +++
 .../nifi-web-ui/src/main/webapp/css/dialog.css  |   25 +-
 .../src/main/webapp/css/label-configuration.css |    1 -
 .../nifi-web-ui/src/main/webapp/css/main.css    |   42 +-
 .../css/new-controller-service-dialog.css       |  152 ++
 .../main/webapp/css/new-processor-dialog.css    |   53 +-
 .../webapp/css/new-reporting-task-dialog.css    |  152 ++
 .../src/main/webapp/css/port-configuration.css  |    2 -
 .../src/main/webapp/css/port-details.css        |    2 -
 .../webapp/css/process-group-configuration.css  |    1 -
 .../main/webapp/css/process-group-details.css   |    1 -
 .../main/webapp/css/processor-configuration.css |  153 --
 .../src/main/webapp/css/processor-details.css   |   27 -
 .../src/main/webapp/css/registration.css        |    8 -
 .../css/remote-process-group-configuration.css  |    3 -
 .../src/main/webapp/css/reporting-task.css      |  116 ++
 .../src/main/webapp/css/settings.css            |  138 +-
 .../nifi-web-ui/src/main/webapp/css/shell.css   |    2 +-
 .../src/main/webapp/css/status-history.css      |    1 -
 .../main/webapp/images/buttonNewProperty.png    |  Bin 590 -> 0 bytes
 .../src/main/webapp/images/iconEnable.png       |  Bin 0 -> 472 bytes
 .../src/main/webapp/images/iconUndo.png         |  Bin 642 -> 0 bytes
 .../src/main/webapp/js/jquery/jquery.each.js    |    2 +-
 .../webapp/js/jquery/modal/jquery.modal.css     |   12 +-
 .../main/webapp/js/jquery/modal/jquery.modal.js |  115 +-
 .../js/jquery/nfeditor/jquery.nfeditor.js       |    5 +-
 .../jquery/propertytable/buttonNewProperty.png  |  Bin 0 -> 590 bytes
 .../propertytable/jquery.propertytable.css      |  173 ++
 .../propertytable/jquery.propertytable.js       | 1317 +++++++++++++
 .../main/webapp/js/jquery/tabbs/jquery.tabbs.js |    2 +
 .../js/jquery/tagcloud/jquery.tagcloud.css      |   62 +
 .../js/jquery/tagcloud/jquery.tagcloud.js       |  226 +++
 .../js/nf/bulletin-board/nf-bulletin-board.js   |    3 +
 .../src/main/webapp/js/nf/canvas/nf-actions.js  |    3 +
 .../src/main/webapp/js/nf/canvas/nf-birdseye.js |    3 +
 .../webapp/js/nf/canvas/nf-canvas-header.js     |   64 +-
 .../webapp/js/nf/canvas/nf-canvas-toolbar.js    |    3 +
 .../webapp/js/nf/canvas/nf-canvas-toolbox.js    |  164 +-
 .../main/webapp/js/nf/canvas/nf-canvas-utils.js |    3 +
 .../src/main/webapp/js/nf/canvas/nf-canvas.js   |   43 +-
 .../main/webapp/js/nf/canvas/nf-clipboard.js    |    3 +
 .../main/webapp/js/nf/canvas/nf-connectable.js  |    3 +
 .../js/nf/canvas/nf-connection-configuration.js |    3 +
 .../main/webapp/js/nf/canvas/nf-connection.js   |    3 +
 .../main/webapp/js/nf/canvas/nf-context-menu.js |    3 +
 .../js/nf/canvas/nf-controller-service.js       | 1730 ++++++++++++++++++
 .../js/nf/canvas/nf-custom-processor-ui.js      |   43 -
 .../main/webapp/js/nf/canvas/nf-custom-ui.js    |   47 +
 .../main/webapp/js/nf/canvas/nf-draggable.js    |    3 +
 .../src/main/webapp/js/nf/canvas/nf-funnel.js   |    3 +
 .../src/main/webapp/js/nf/canvas/nf-go-to.js    |    3 +
 .../webapp/js/nf/canvas/nf-graph-control.js     |    3 +
 .../src/main/webapp/js/nf/canvas/nf-graph.js    |    3 +
 .../js/nf/canvas/nf-label-configuration.js      |    3 +
 .../src/main/webapp/js/nf/canvas/nf-label.js    |    3 +
 .../js/nf/canvas/nf-port-configuration.js       |    3 +
 .../main/webapp/js/nf/canvas/nf-port-details.js |    3 +
 .../src/main/webapp/js/nf/canvas/nf-port.js     |    3 +
 .../nf/canvas/nf-process-group-configuration.js |    3 +
 .../js/nf/canvas/nf-process-group-details.js    |    3 +
 .../webapp/js/nf/canvas/nf-process-group.js     |    3 +
 .../js/nf/canvas/nf-processor-configuration.js  |  471 ++---
 .../nf-processor-property-combo-editor.js       |  177 --
 .../canvas/nf-processor-property-nfel-editor.js |  207 ---
 .../js/nf/canvas/nf-processor-property-table.js |  567 ------
 .../canvas/nf-processor-property-text-editor.js |  212 ---
 .../main/webapp/js/nf/canvas/nf-processor.js    |    3 +
 .../main/webapp/js/nf/canvas/nf-registration.js |    7 +-
 .../nf-remote-process-group-configuration.js    |    3 +
 .../canvas/nf-remote-process-group-details.js   |    3 +
 .../nf/canvas/nf-remote-process-group-ports.js  |    3 +
 .../js/nf/canvas/nf-remote-process-group.js     |    3 +
 .../webapp/js/nf/canvas/nf-reporting-task.js    |  700 +++++++
 .../js/nf/canvas/nf-secure-port-details.js      |    3 +
 .../main/webapp/js/nf/canvas/nf-selectable.js   |    3 +
 .../src/main/webapp/js/nf/canvas/nf-settings.js | 1705 ++++++++++++++++-
 .../src/main/webapp/js/nf/canvas/nf-snippet.js  |    3 +
 .../src/main/webapp/js/nf/canvas/nf-storage.js  |    3 +
 .../webapp/js/nf/canvas/nf-toolbar-action.js    |    3 +
 .../webapp/js/nf/cluster/nf-cluster-table.js    |    3 +
 .../src/main/webapp/js/nf/cluster/nf-cluster.js |    3 +
 .../webapp/js/nf/counters/nf-counters-table.js  |    3 +
 .../main/webapp/js/nf/counters/nf-counters.js   |    3 +
 .../webapp/js/nf/history/nf-history-model.js    |    3 +
 .../webapp/js/nf/history/nf-history-table.js    |    7 +-
 .../src/main/webapp/js/nf/history/nf-history.js |    3 +
 .../src/main/webapp/js/nf/nf-client.js          |    3 +
 .../src/main/webapp/js/nf/nf-common.js          |   74 +-
 .../main/webapp/js/nf/nf-connection-details.js  |    3 +
 .../src/main/webapp/js/nf/nf-dialog.js          |   18 +-
 .../main/webapp/js/nf/nf-processor-details.js   |  355 +---
 .../src/main/webapp/js/nf/nf-shell.js           |    3 +
 .../src/main/webapp/js/nf/nf-status-history.js  |    3 +
 .../js/nf/provenance/nf-provenance-lineage.js   |    3 +
 .../js/nf/provenance/nf-provenance-table.js     |    3 +
 .../webapp/js/nf/provenance/nf-provenance.js    |    3 +
 .../webapp/js/nf/summary/nf-summary-table.js    |    2 +
 .../src/main/webapp/js/nf/summary/nf-summary.js |    3 +
 .../js/nf/templates/nf-templates-table.js       |    3 +
 .../main/webapp/js/nf/templates/nf-templates.js |    3 +
 .../main/webapp/js/nf/users/nf-users-table.js   |    3 +
 .../nifi-framework/nifi-web/pom.xml             |    4 +-
 .../standard/TestDetectDuplicate.java           |    5 +-
 .../DistributedMapCacheClientService.java       |   21 +-
 .../DistributedSetCacheClientService.java       |   16 +-
 .../cache/server/AbstractCacheServer.java       |    2 +-
 .../cache/server/DistributedCacheServer.java    |   13 +-
 .../nifi/http/StandardHttpContextMap.java       |    9 +
 .../nifi/ssl/StandardSSLContextService.java     |   39 +-
 .../update/attributes/api/RuleResource.java     |  131 +-
 .../src/main/webapp/META-INF/nifi-processor     |   15 -
 .../META-INF/nifi-processor-configuration       |   15 +
 .../src/main/webapp/WEB-INF/jsp/worksheet.jsp   |    3 +-
 .../src/main/webapp/WEB-INF/web.xml             |    4 +-
 .../src/main/webapp/js/application.js           |   12 +-
 nifi/pom.xml                                    |    5 +
 341 files changed, 21054 insertions(+), 5346 deletions(-)
----------------------------------------------------------------------



[62/62] [abbrv] incubator-nifi git commit: Merge branch 'develop' into NIFI-25

Posted by ma...@apache.org.
Merge branch 'develop' into NIFI-25

Conflicts:
	nifi/nifi-assembly/NOTICE
	nifi/nifi-nar-bundles/pom.xml


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/93a12104
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/93a12104
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/93a12104

Branch: refs/heads/NIFI-25
Commit: 93a121044b1a7efcfc3023178fb39f2f44ad836c
Parents: 373f470 c201aa1
Author: Mark Payne <ma...@hotmail.com>
Authored: Fri Apr 10 09:42:53 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Fri Apr 10 09:42:53 2015 -0400

----------------------------------------------------------------------
 nifi/NOTICE                                     |    5 -
 nifi/nifi-api/pom.xml                           |    2 +-
 .../annotation/behavior/DynamicProperties.java  |   42 +
 .../annotation/behavior/DynamicProperty.java    |   67 +
 .../behavior/DynamicRelationship.java           |   56 +
 .../annotation/behavior/ReadsAttribute.java     |   50 +
 .../annotation/behavior/ReadsAttributes.java    |   44 +
 .../annotation/behavior/WritesAttribute.java    |   51 +
 .../annotation/behavior/WritesAttributes.java   |   44 +
 .../nifi/annotation/documentation/SeeAlso.java  |   59 +
 .../nifi/annotation/lifecycle/OnAdded.java      |   13 +-
 .../nifi/annotation/lifecycle/OnDisabled.java   |   29 +-
 .../nifi/annotation/lifecycle/OnEnabled.java    |   34 +-
 .../nifi/annotation/lifecycle/OnRemoved.java    |   14 +-
 .../nifi/annotation/lifecycle/OnShutdown.java   |   13 +-
 .../nifi/annotation/lifecycle/OnStopped.java    |    9 +
 .../annotation/lifecycle/OnUnscheduled.java     |    2 -
 .../AbstractConfigurableComponent.java          |    5 +-
 .../nifi/components/PropertyDescriptor.java     |   14 +-
 .../nifi/components/ValidationContext.java      |   28 +
 .../controller/AbstractControllerService.java   |   13 +-
 .../ControllerServiceInitializationContext.java |   10 +
 .../controller/ControllerServiceLookup.java     |   19 +
 .../org/apache/nifi/logging/ComponentLog.java   |  100 +
 .../org/apache/nifi/logging/ProcessorLog.java   |   61 +-
 .../apache/nifi/processor/ProcessSession.java   |   40 +-
 .../nifi/reporting/AbstractReportingTask.java   |   10 +
 .../ReportingInitializationContext.java         |   10 +
 .../nifi/web/ClusterRequestException.java       |    1 +
 .../org/apache/nifi/web/ComponentDetails.java   |  157 +
 .../apache/nifi/web/ConfigurationAction.java    |  137 +
 .../nifi/web/NiFiWebConfigurationContext.java   |  102 +
 .../web/NiFiWebConfigurationRequestContext.java |   31 +
 .../org/apache/nifi/web/NiFiWebContext.java     |    1 +
 .../apache/nifi/web/NiFiWebContextConfig.java   |    1 +
 .../apache/nifi/web/NiFiWebRequestContext.java  |   58 +
 .../nifi/web/ProcessorConfigurationAction.java  |    1 +
 .../java/org/apache/nifi/web/ProcessorInfo.java |    1 +
 .../main/java/org/apache/nifi/web/Revision.java |   32 +-
 .../org/apache/nifi/web/UiExtensionType.java    |   31 +
 .../org/apache/nifi/web/ViewableContent.java    |   74 +
 nifi/nifi-assembly/NOTICE                       |   55 +
 nifi/nifi-assembly/pom.xml                      |  928 ++--
 nifi/nifi-bootstrap/pom.xml                     |    2 +-
 .../java/org/apache/nifi/bootstrap/RunNiFi.java |  131 +-
 .../nifi-data-provenance-utils/pom.xml          |    2 +-
 .../nifi-expression-language/pom.xml            |    2 +-
 .../nifi-commons/nifi-flowfile-packager/pom.xml |    2 +-
 .../nifi-hl7-query-language/.gitignore          |    3 +
 .../nifi-hl7-query-language/pom.xml             |  115 +
 .../apache/nifi/hl7/query/antlr/HL7QueryLexer.g |  156 +
 .../nifi/hl7/query/antlr/HL7QueryParser.g       |   91 +
 .../org/apache/nifi/hl7/hapi/EmptyField.java    |   37 +
 .../org/apache/nifi/hl7/hapi/HapiField.java     |   83 +
 .../org/apache/nifi/hl7/hapi/HapiMessage.java   |   94 +
 .../org/apache/nifi/hl7/hapi/HapiSegment.java   |   69 +
 .../apache/nifi/hl7/hapi/SingleValueField.java  |   42 +
 .../java/org/apache/nifi/hl7/io/HL7Reader.java  |   27 +
 .../hl7/io/exception/InvalidHL7Exception.java   |   40 +
 .../org/apache/nifi/hl7/model/HL7Component.java |   24 +
 .../org/apache/nifi/hl7/model/HL7Field.java     |   21 +
 .../org/apache/nifi/hl7/model/HL7Message.java   |   27 +
 .../org/apache/nifi/hl7/model/HL7Segment.java   |   27 +
 .../org/apache/nifi/hl7/query/Declaration.java  |   29 +
 .../org/apache/nifi/hl7/query/HL7Query.java     |  412 ++
 .../org/apache/nifi/hl7/query/QueryResult.java  |   29 +
 .../org/apache/nifi/hl7/query/ResultHit.java    |   25 +
 .../org/apache/nifi/hl7/query/Selection.java    |   37 +
 .../hl7/query/evaluator/BooleanEvaluator.java   |   24 +
 .../nifi/hl7/query/evaluator/Evaluator.java     |   27 +
 .../hl7/query/evaluator/IntegerEvaluator.java   |   26 +
 .../hl7/query/evaluator/StringEvaluator.java    |   25 +
 .../comparison/AbstractComparisonEvaluator.java |  106 +
 .../comparison/AbstractNumericComparison.java   |   67 +
 .../evaluator/comparison/EqualsEvaluator.java   |   32 +
 .../comparison/GreaterThanEvaluator.java        |   34 +
 .../comparison/GreaterThanOrEqualEvaluator.java |   34 +
 .../evaluator/comparison/IsNullEvaluator.java   |   69 +
 .../evaluator/comparison/LessThanEvaluator.java |   31 +
 .../comparison/LessThanOrEqualEvaluator.java    |   31 +
 .../comparison/NotEqualsEvaluator.java          |   32 +
 .../evaluator/comparison/NotEvaluator.java      |   36 +
 .../evaluator/comparison/NotNullEvaluator.java  |   65 +
 .../literal/IntegerLiteralEvaluator.java        |   36 +
 .../literal/StringLiteralEvaluator.java         |   35 +
 .../hl7/query/evaluator/logic/AndEvaluator.java |   43 +
 .../hl7/query/evaluator/logic/OrEvaluator.java  |   43 +
 .../message/DeclaredReferenceEvaluator.java     |   42 +
 .../query/evaluator/message/DotEvaluator.java   |   88 +
 .../query/evaluator/message/FieldEvaluator.java |   67 +
 .../evaluator/message/MessageEvaluator.java     |   34 +
 .../evaluator/message/SegmentEvaluator.java     |   51 +
 .../exception/HL7QueryParsingException.java     |   37 +
 .../nifi/hl7/query/result/MissedResult.java     |   56 +
 .../hl7/query/result/StandardQueryResult.java   |   69 +
 .../hl7/query/result/StandardResultHit.java     |   41 +
 .../org/apache/nifi/hl7/query/TestHL7Query.java |  352 ++
 .../src/test/resources/hyperglycemia            |    5 +
 .../src/test/resources/hypoglycemia             |    5 +
 .../src/test/resources/metabolic-panel          |   23 +
 .../resources/unsolicited-vaccine-update-long   |   16 +
 .../resources/unsolicited-vaccine-update-short  |    4 +
 .../src/test/resources/vaccine-query            |    3 +
 .../src/test/resources/vaers-message-long       |   60 +
 nifi/nifi-commons/nifi-logging-utils/pom.xml    |    2 +-
 .../nifi-processor-utilities/pom.xml            |    2 +-
 .../nifi/processor/util/StandardValidators.java |  116 +-
 .../processor/util/TestStandardValidators.java  |   49 +-
 nifi/nifi-commons/nifi-properties/pom.xml       |    2 +-
 .../org/apache/nifi/util/NiFiProperties.java    |    2 -
 nifi/nifi-commons/nifi-security-utils/pom.xml   |    2 +-
 .../nifi-site-to-site-client/pom.xml            |    8 +-
 .../client/socket/EndpointConnectionPool.java   |   59 +-
 .../nifi/remote/client/socket/SocketClient.java |    9 +-
 .../protocol/socket/SocketClientProtocol.java   |    8 +-
 .../socket/SocketClientTransaction.java         |   25 +-
 .../socket/TestEndpointConnectionStatePool.java |    4 +-
 nifi/nifi-commons/nifi-socket-utils/pom.xml     |    2 +-
 nifi/nifi-commons/nifi-utils/pom.xml            |    4 +-
 .../java/org/apache/nifi/util/EscapeUtils.java  |   42 +
 nifi/nifi-commons/nifi-web-utils/pom.xml        |    2 +-
 nifi/nifi-commons/nifi-write-ahead-log/pom.xml  |    2 +-
 nifi/nifi-commons/pom.xml                       |    3 +-
 nifi/nifi-docs/pom.xml                          |    2 +-
 .../src/main/asciidoc/administration-guide.adoc |   26 +-
 .../src/main/asciidoc/developer-guide.adoc      |   35 +
 nifi/nifi-external/nifi-spark-receiver/pom.xml  |   72 +-
 nifi/nifi-external/pom.xml                      |    2 +-
 .../nifi-processor-bundle-archetype/pom.xml     |    6 +-
 .../META-INF/maven/archetype-metadata.xml       |    2 +-
 .../src/main/java/MyProcessor.java              |    8 +
 .../docs/__package__.MyProcessor/index.html     |   96 -
 nifi/nifi-maven-archetypes/pom.xml              |    5 +-
 nifi/nifi-mock/pom.xml                          |    2 +-
 ...kControllerServiceInitializationContext.java |   17 +
 .../nifi/util/MockControllerServiceLookup.java  |   11 +
 .../MockProcessorInitializationContext.java     |   10 +
 .../org/apache/nifi/util/MockProcessorLog.java  |   34 +-
 .../MockReportingInitializationContext.java     |   10 +-
 .../apache/nifi/util/MockValidationContext.java |   42 +
 .../nifi/util/StandardProcessorTestRunner.java  |    9 +-
 .../nifi-framework-nar/pom.xml                  |    2 +-
 .../nifi-framework/nifi-administration/pom.xml  |    2 +-
 .../org/apache/nifi/admin/dao/ActionDAO.java    |    6 +-
 .../nifi/admin/dao/impl/StandardActionDAO.java  |   60 +-
 .../apache/nifi/admin/service/AuditService.java |    6 +-
 .../admin/service/action/GetPreviousValues.java |    8 +-
 .../service/impl/StandardAuditService.java      |    4 +-
 .../nifi-framework/nifi-client-dto/pom.xml      |    2 +-
 .../nifi/web/api/dto/ComponentHistoryDTO.java   |   56 +
 .../web/api/dto/ControllerConfigurationDTO.java |   18 +
 .../nifi/web/api/dto/ControllerServiceDTO.java  |  190 +
 ...ontrollerServiceReferencingComponentDTO.java |  207 +
 .../nifi/web/api/dto/DocumentedTypeDTO.java     |    6 +-
 .../apache/nifi/web/api/dto/FlowSnippetDTO.java |   15 +-
 .../nifi/web/api/dto/NiFiComponentDTO.java      |    4 +-
 .../nifi/web/api/dto/ProcessorConfigDTO.java    |  218 +-
 .../nifi/web/api/dto/ProcessorHistoryDTO.java   |   56 -
 .../nifi/web/api/dto/PropertyDescriptorDTO.java |  244 +
 .../nifi/web/api/dto/ReportingTaskDTO.java      |  228 +
 .../apache/nifi/web/api/dto/RevisionDTO.java    |   15 +
 .../component/details/ComponentDetailsDTO.java  |    2 +-
 .../component/details/ExtensionDetailsDTO.java  |   41 +
 .../component/details/ProcessorDetailsDTO.java  |   41 -
 .../web/api/entity/ComponentHistoryEntity.java  |   45 +
 .../web/api/entity/ControllerServiceEntity.java |   45 +
 ...ollerServiceReferencingComponentsEntity.java |   46 +
 .../entity/ControllerServiceTypesEntity.java    |   46 +
 .../api/entity/ControllerServicesEntity.java    |   46 +
 .../web/api/entity/ProcessorHistoryEntity.java  |   45 -
 .../api/entity/PropertyDescriptorEntity.java    |   46 +
 .../web/api/entity/ReportingTaskEntity.java     |   45 +
 .../api/entity/ReportingTaskTypesEntity.java    |   46 +
 .../web/api/entity/ReportingTasksEntity.java    |   46 +
 .../nifi-cluster-authorization-provider/pom.xml |    2 +-
 .../nifi-cluster-protocol/.gitignore            |    1 -
 .../nifi-cluster-protocol/pom.xml               |   67 -
 .../protocol/ClusterManagerProtocolSender.java  |   69 -
 .../cluster/protocol/ConnectionRequest.java     |   44 -
 .../cluster/protocol/ConnectionResponse.java    |  141 -
 .../apache/nifi/cluster/protocol/Heartbeat.java |   68 -
 .../nifi/cluster/protocol/NodeBulletins.java    |   44 -
 .../nifi/cluster/protocol/NodeIdentifier.java   |  172 -
 .../cluster/protocol/NodeProtocolSender.java    |   73 -
 .../nifi/cluster/protocol/ProtocolContext.java  |   39 -
 .../cluster/protocol/ProtocolException.java     |   40 -
 .../nifi/cluster/protocol/ProtocolHandler.java  |   44 -
 .../nifi/cluster/protocol/ProtocolListener.java |   72 -
 .../protocol/ProtocolMessageMarshaller.java     |   38 -
 .../protocol/ProtocolMessageUnmarshaller.java   |   38 -
 .../nifi/cluster/protocol/StandardDataFlow.java |  105 -
 .../UnknownServiceAddressException.java         |   39 -
 .../impl/ClusterManagerProtocolSenderImpl.java  |  245 -
 .../ClusterManagerProtocolSenderListener.java   |  118 -
 .../protocol/impl/ClusterServiceDiscovery.java  |  181 -
 .../protocol/impl/ClusterServiceLocator.java    |  229 -
 .../impl/ClusterServicesBroadcaster.java        |  182 -
 .../protocol/impl/CopyingInputStream.java       |   77 -
 .../impl/MulticastProtocolListener.java         |  204 -
 .../protocol/impl/NodeProtocolSenderImpl.java   |  171 -
 .../impl/NodeProtocolSenderListener.java        |  115 -
 .../protocol/impl/SocketProtocolListener.java   |  205 -
 .../protocol/jaxb/JaxbProtocolContext.java      |  148 -
 .../jaxb/message/AdaptedConnectionRequest.java  |   40 -
 .../jaxb/message/AdaptedConnectionResponse.java |  109 -
 .../protocol/jaxb/message/AdaptedCounter.java   |   56 -
 .../protocol/jaxb/message/AdaptedDataFlow.java  |   64 -
 .../protocol/jaxb/message/AdaptedHeartbeat.java |   66 -
 .../jaxb/message/AdaptedNodeBulletins.java      |   50 -
 .../jaxb/message/AdaptedNodeIdentifier.java     |   76 -
 .../jaxb/message/ConnectionRequestAdapter.java  |   41 -
 .../jaxb/message/ConnectionResponseAdapter.java |   55 -
 .../protocol/jaxb/message/DataFlowAdapter.java  |   50 -
 .../protocol/jaxb/message/HeartbeatAdapter.java |   54 -
 .../jaxb/message/JaxbProtocolUtils.java         |   42 -
 .../jaxb/message/NodeBulletinsAdapter.java      |   48 -
 .../jaxb/message/NodeIdentifierAdapter.java     |   51 -
 .../protocol/jaxb/message/ObjectFactory.java    |  104 -
 .../message/ConnectionRequestMessage.java       |   46 -
 .../message/ConnectionResponseMessage.java      |   66 -
 .../ControllerStartupFailureMessage.java        |   49 -
 .../protocol/message/DisconnectMessage.java     |   55 -
 .../protocol/message/ExceptionMessage.java      |   44 -
 .../protocol/message/FlowRequestMessage.java    |   46 -
 .../protocol/message/FlowResponseMessage.java   |   44 -
 .../protocol/message/HeartbeatMessage.java      |   43 -
 .../message/MulticastProtocolMessage.java       |   66 -
 .../protocol/message/NodeBulletinsMessage.java  |   43 -
 .../cluster/protocol/message/PingMessage.java   |   55 -
 .../message/PrimaryRoleAssignmentMessage.java   |   56 -
 .../protocol/message/ProtocolMessage.java       |   61 -
 .../message/ReconnectionFailureMessage.java     |   45 -
 .../message/ReconnectionRequestMessage.java     |   94 -
 .../message/ReconnectionResponseMessage.java    |   32 -
 .../message/ServiceBroadcastMessage.java        |   64 -
 .../MulticastConfigurationFactoryBean.java      |   60 -
 .../ServerSocketConfigurationFactoryBean.java   |   65 -
 .../spring/SocketConfigurationFactoryBean.java  |   66 -
 .../resources/nifi-cluster-protocol-context.xml |  110 -
 .../ClusterManagerProtocolSenderImplTest.java   |  134 -
 .../impl/ClusterServiceDiscoveryTest.java       |  135 -
 .../impl/ClusterServiceLocatorTest.java         |  121 -
 .../impl/ClusterServicesBroadcasterTest.java    |  133 -
 .../impl/MulticastProtocolListenerTest.java     |  171 -
 .../impl/NodeProtocolSenderImplTest.java        |  203 -
 .../impl/testutils/DelayedProtocolHandler.java  |   57 -
 .../testutils/ReflexiveProtocolHandler.java     |   47 -
 .../nifi-framework/nifi-cluster-web/.gitignore  |    1 -
 .../nifi-framework/nifi-cluster-web/pom.xml     |   48 -
 .../nifi/cluster/context/ClusterContext.java    |   59 -
 .../cluster/context/ClusterContextImpl.java     |   69 -
 .../context/ClusterContextThreadLocal.java      |   47 -
 .../ClusterAwareOptimisticLockingManager.java   |   96 -
 .../nifi-framework/nifi-cluster/.gitignore      |    1 -
 .../nifi-framework/nifi-cluster/pom.xml         |  130 -
 .../cluster/client/MulticastTestClient.java     |  151 -
 .../org/apache/nifi/cluster/event/Event.java    |  122 -
 .../apache/nifi/cluster/event/EventManager.java |   65 -
 .../cluster/event/impl/EventManagerImpl.java    |  143 -
 .../cluster/firewall/ClusterNodeFirewall.java   |   35 -
 .../impl/FileBasedClusterNodeFirewall.java      |  207 -
 .../nifi/cluster/flow/ClusterDataFlow.java      |   45 -
 .../apache/nifi/cluster/flow/DaoException.java  |   40 -
 .../apache/nifi/cluster/flow/DataFlowDao.java   |   62 -
 .../cluster/flow/DataFlowManagementService.java |  115 -
 .../nifi/cluster/flow/PersistedFlowState.java   |   37 -
 .../nifi/cluster/flow/StaleFlowException.java   |   42 -
 .../nifi/cluster/flow/impl/DataFlowDaoImpl.java |  600 ---
 .../impl/DataFlowManagementServiceImpl.java     |  356 --
 .../nifi/cluster/manager/ClusterManager.java    |  225 -
 .../cluster/manager/HttpClusterManager.java     |  169 -
 .../cluster/manager/HttpRequestReplicator.java  |   99 -
 .../cluster/manager/HttpResponseMapper.java     |   42 -
 .../nifi/cluster/manager/NodeResponse.java      |  329 --
 .../exception/BlockedByFirewallException.java   |   60 -
 .../manager/exception/ClusterException.java     |   40 -
 .../ConnectingNodeMutableRequestException.java  |   41 -
 ...DisconnectedNodeMutableRequestException.java |   41 -
 .../exception/IllegalClusterStateException.java |   41 -
 .../exception/IllegalNodeDeletionException.java |   41 -
 .../IllegalNodeDisconnectionException.java      |   42 -
 .../IllegalNodeReconnectionException.java       |   41 -
 .../IneligiblePrimaryNodeException.java         |   41 -
 .../exception/MutableRequestException.java      |   42 -
 .../exception/NoConnectedNodesException.java    |   41 -
 .../exception/NoResponseFromNodesException.java |   42 -
 .../exception/NodeDisconnectionException.java   |   41 -
 .../exception/NodeReconnectionException.java    |   40 -
 .../PrimaryRoleAssignmentException.java         |   41 -
 .../SafeModeMutableRequestException.java        |   41 -
 .../manager/exception/UnknownNodeException.java |   41 -
 .../exception/UriConstructionException.java     |   42 -
 .../manager/impl/ClusteredEventAccess.java      |  135 -
 .../manager/impl/ClusteredReportingContext.java |  165 -
 .../manager/impl/HttpRequestReplicatorImpl.java |  531 ---
 .../manager/impl/HttpResponseMapperImpl.java    |   85 -
 .../cluster/manager/impl/WebClusterManager.java | 3628 ---------------
 .../java/org/apache/nifi/cluster/node/Node.java |  252 --
 ...anagerProtocolServiceLocatorFactoryBean.java |  116 -
 ...FileBasedClusterNodeFirewallFactoryBean.java |   58 -
 .../spring/WebClusterManagerFactoryBean.java    |  139 -
 .../reporting/ClusteredReportingTaskNode.java   |   49 -
 .../resources/nifi-cluster-manager-context.xml  |  124 -
 .../event/impl/EventManagerImplTest.java        |  119 -
 .../impl/FileBasedClusterNodeFirewallTest.java  |   98 -
 .../impl/DataFlowManagementServiceImplTest.java |  343 --
 .../impl/HttpRequestReplicatorImplTest.java     |  368 --
 .../impl/HttpResponseMapperImplTest.java        |  126 -
 .../manager/impl/TestWebClusterManager.java     |   54 -
 .../cluster/manager/testutils/HttpRequest.java  |  239 -
 .../cluster/manager/testutils/HttpResponse.java |   93 -
 .../manager/testutils/HttpResponseAction.java   |   60 -
 .../cluster/manager/testutils/HttpServer.java   |  240 -
 .../ClusterManagerProtocolSenderImplTest.java   |  133 -
 .../impl/ClusterServiceLocatorTest.java         |  119 -
 .../impl/ClusterServicesBroadcasterTest.java    |  131 -
 .../impl/MulticastProtocolListenerTest.java     |  171 -
 .../impl/NodeProtocolSenderImplTest.java        |  201 -
 .../impl/SocketProtocolListenerTest.java        |  132 -
 .../testutils/DelayedProtocolHandler.java       |   57 -
 .../testutils/ReflexiveProtocolHandler.java     |   47 -
 .../src/test/resources/logback-test.xml         |   48 -
 .../apache/nifi/cluster/firewall/impl/empty.txt |    0
 .../apache/nifi/cluster/firewall/impl/ips.txt   |   12 -
 .../nifi-framework/nifi-documentation/pom.xml   |   41 +
 .../ConfigurableComponentInitializer.java       |   38 +
 .../apache/nifi/documentation/DocGenerator.java |  179 +
 .../nifi/documentation/DocumentationWriter.java |   33 +
 .../html/HtmlDocumentationWriter.java           |  573 +++
 .../html/HtmlProcessorDocumentationWriter.java  |  275 ++
 .../init/ControllerServiceInitializer.java      |   38 +
 .../init/ProcessorInitializer.java              |   37 +
 .../init/ReportingTaskingInitializer.java       |   37 +
 ...kControllerServiceInitializationContext.java |   46 +
 .../mock/MockControllerServiceLookup.java       |   65 +
 .../MockProcessorInitializationContext.java     |   45 +
 .../MockReportingInitializationContext.java     |   67 +
 .../FullyDocumentedControllerService.java       |   57 +
 .../example/FullyDocumentedProcessor.java       |  121 +
 .../example/FullyDocumentedReportingTask.java   |   50 +
 .../documentation/example/NakedProcessor.java   |   31 +
 .../documentation/example/SampleService.java    |   25 +
 .../html/HtmlDocumentationWriterTest.java       |  101 +
 .../html/ProcessorDocumentationWriterTest.java  |  103 +
 .../nifi/documentation/html/XmlValidator.java   |   50 +
 .../nifi-file-authorization-provider/pom.xml    |    2 +-
 .../nifi-framework-cluster-protocol/.gitignore  |    1 +
 .../nifi-framework-cluster-protocol/pom.xml     |   67 +
 .../protocol/ClusterManagerProtocolSender.java  |   69 +
 .../cluster/protocol/ConnectionRequest.java     |   44 +
 .../cluster/protocol/ConnectionResponse.java    |  141 +
 .../apache/nifi/cluster/protocol/Heartbeat.java |   68 +
 .../nifi/cluster/protocol/NodeBulletins.java    |   44 +
 .../nifi/cluster/protocol/NodeIdentifier.java   |  172 +
 .../cluster/protocol/NodeProtocolSender.java    |   73 +
 .../nifi/cluster/protocol/ProtocolContext.java  |   39 +
 .../cluster/protocol/ProtocolException.java     |   40 +
 .../nifi/cluster/protocol/ProtocolHandler.java  |   44 +
 .../nifi/cluster/protocol/ProtocolListener.java |   72 +
 .../protocol/ProtocolMessageMarshaller.java     |   38 +
 .../protocol/ProtocolMessageUnmarshaller.java   |   38 +
 .../nifi/cluster/protocol/StandardDataFlow.java |  105 +
 .../UnknownServiceAddressException.java         |   39 +
 .../impl/ClusterManagerProtocolSenderImpl.java  |  245 +
 .../ClusterManagerProtocolSenderListener.java   |  118 +
 .../protocol/impl/ClusterServiceDiscovery.java  |  181 +
 .../protocol/impl/ClusterServiceLocator.java    |  229 +
 .../impl/ClusterServicesBroadcaster.java        |  182 +
 .../protocol/impl/CopyingInputStream.java       |   77 +
 .../impl/MulticastProtocolListener.java         |  204 +
 .../protocol/impl/NodeProtocolSenderImpl.java   |  171 +
 .../impl/NodeProtocolSenderListener.java        |  115 +
 .../protocol/impl/SocketProtocolListener.java   |  205 +
 .../protocol/jaxb/JaxbProtocolContext.java      |  148 +
 .../jaxb/message/AdaptedConnectionRequest.java  |   40 +
 .../jaxb/message/AdaptedConnectionResponse.java |  109 +
 .../protocol/jaxb/message/AdaptedCounter.java   |   56 +
 .../protocol/jaxb/message/AdaptedDataFlow.java  |   64 +
 .../protocol/jaxb/message/AdaptedHeartbeat.java |   66 +
 .../jaxb/message/AdaptedNodeBulletins.java      |   50 +
 .../jaxb/message/AdaptedNodeIdentifier.java     |   76 +
 .../jaxb/message/ConnectionRequestAdapter.java  |   41 +
 .../jaxb/message/ConnectionResponseAdapter.java |   55 +
 .../protocol/jaxb/message/DataFlowAdapter.java  |   50 +
 .../protocol/jaxb/message/HeartbeatAdapter.java |   54 +
 .../jaxb/message/JaxbProtocolUtils.java         |   42 +
 .../jaxb/message/NodeBulletinsAdapter.java      |   48 +
 .../jaxb/message/NodeIdentifierAdapter.java     |   51 +
 .../protocol/jaxb/message/ObjectFactory.java    |  104 +
 .../message/ConnectionRequestMessage.java       |   46 +
 .../message/ConnectionResponseMessage.java      |   66 +
 .../ControllerStartupFailureMessage.java        |   49 +
 .../protocol/message/DisconnectMessage.java     |   55 +
 .../protocol/message/ExceptionMessage.java      |   44 +
 .../protocol/message/FlowRequestMessage.java    |   46 +
 .../protocol/message/FlowResponseMessage.java   |   44 +
 .../protocol/message/HeartbeatMessage.java      |   43 +
 .../message/MulticastProtocolMessage.java       |   66 +
 .../protocol/message/NodeBulletinsMessage.java  |   43 +
 .../cluster/protocol/message/PingMessage.java   |   55 +
 .../message/PrimaryRoleAssignmentMessage.java   |   56 +
 .../protocol/message/ProtocolMessage.java       |   61 +
 .../message/ReconnectionFailureMessage.java     |   45 +
 .../message/ReconnectionRequestMessage.java     |   94 +
 .../message/ReconnectionResponseMessage.java    |   32 +
 .../message/ServiceBroadcastMessage.java        |   64 +
 .../MulticastConfigurationFactoryBean.java      |   60 +
 .../ServerSocketConfigurationFactoryBean.java   |   65 +
 .../spring/SocketConfigurationFactoryBean.java  |   66 +
 .../resources/nifi-cluster-protocol-context.xml |  110 +
 .../ClusterManagerProtocolSenderImplTest.java   |  134 +
 .../impl/ClusterServiceDiscoveryTest.java       |  135 +
 .../impl/ClusterServiceLocatorTest.java         |  121 +
 .../impl/ClusterServicesBroadcasterTest.java    |  133 +
 .../impl/MulticastProtocolListenerTest.java     |  171 +
 .../impl/NodeProtocolSenderImplTest.java        |  203 +
 .../impl/testutils/DelayedProtocolHandler.java  |   57 +
 .../testutils/ReflexiveProtocolHandler.java     |   47 +
 .../nifi-framework-cluster-web/.gitignore       |    1 +
 .../nifi-framework-cluster-web/pom.xml          |   44 +
 .../nifi/cluster/context/ClusterContext.java    |   59 +
 .../cluster/context/ClusterContextImpl.java     |   69 +
 .../context/ClusterContextThreadLocal.java      |   42 +
 .../nifi-framework-cluster/.gitignore           |    1 +
 .../nifi-framework-cluster/pom.xml              |  148 +
 .../cluster/client/MulticastTestClient.java     |  151 +
 .../org/apache/nifi/cluster/event/Event.java    |  122 +
 .../apache/nifi/cluster/event/EventManager.java |   65 +
 .../cluster/event/impl/EventManagerImpl.java    |  143 +
 .../cluster/firewall/ClusterNodeFirewall.java   |   35 +
 .../impl/FileBasedClusterNodeFirewall.java      |  207 +
 .../nifi/cluster/flow/ClusterDataFlow.java      |   56 +
 .../apache/nifi/cluster/flow/DaoException.java  |   40 +
 .../apache/nifi/cluster/flow/DataFlowDao.java   |   62 +
 .../cluster/flow/DataFlowManagementService.java |  132 +
 .../nifi/cluster/flow/PersistedFlowState.java   |   37 +
 .../nifi/cluster/flow/StaleFlowException.java   |   42 +
 .../nifi/cluster/flow/impl/DataFlowDaoImpl.java |  615 +++
 .../impl/DataFlowManagementServiceImpl.java     |  413 ++
 .../nifi/cluster/manager/ClusterManager.java    |  225 +
 .../cluster/manager/HttpClusterManager.java     |  169 +
 .../cluster/manager/HttpRequestReplicator.java  |   99 +
 .../cluster/manager/HttpResponseMapper.java     |   42 +
 .../nifi/cluster/manager/NodeResponse.java      |  343 ++
 .../exception/BlockedByFirewallException.java   |   60 +
 .../manager/exception/ClusterException.java     |   40 +
 .../ConnectingNodeMutableRequestException.java  |   41 +
 ...DisconnectedNodeMutableRequestException.java |   41 +
 .../exception/IllegalClusterStateException.java |   41 +
 .../exception/IllegalNodeDeletionException.java |   41 +
 .../IllegalNodeDisconnectionException.java      |   42 +
 .../IllegalNodeReconnectionException.java       |   41 +
 .../IneligiblePrimaryNodeException.java         |   41 +
 .../exception/MutableRequestException.java      |   42 +
 .../exception/NoConnectedNodesException.java    |   41 +
 .../exception/NoResponseFromNodesException.java |   42 +
 .../exception/NodeDisconnectionException.java   |   41 +
 .../exception/NodeReconnectionException.java    |   40 +
 .../PrimaryRoleAssignmentException.java         |   41 +
 .../SafeModeMutableRequestException.java        |   41 +
 .../manager/exception/UnknownNodeException.java |   41 +
 .../exception/UriConstructionException.java     |   42 +
 .../manager/impl/ClusteredEventAccess.java      |  135 +
 .../manager/impl/ClusteredReportingContext.java |  165 +
 .../manager/impl/HttpRequestReplicatorImpl.java |  531 +++
 .../manager/impl/HttpResponseMapperImpl.java    |   85 +
 .../cluster/manager/impl/WebClusterManager.java | 4237 ++++++++++++++++++
 .../java/org/apache/nifi/cluster/node/Node.java |  252 ++
 ...anagerProtocolServiceLocatorFactoryBean.java |  116 +
 ...FileBasedClusterNodeFirewallFactoryBean.java |   58 +
 .../spring/WebClusterManagerFactoryBean.java    |  134 +
 .../reporting/ClusteredReportingTaskNode.java   |   49 +
 .../resources/nifi-cluster-manager-context.xml  |  128 +
 .../event/impl/EventManagerImplTest.java        |  119 +
 .../impl/FileBasedClusterNodeFirewallTest.java  |   98 +
 .../impl/DataFlowManagementServiceImplTest.java |  343 ++
 .../impl/HttpRequestReplicatorImplTest.java     |  368 ++
 .../impl/HttpResponseMapperImplTest.java        |  126 +
 .../manager/impl/TestWebClusterManager.java     |   54 +
 .../cluster/manager/testutils/HttpRequest.java  |  239 +
 .../cluster/manager/testutils/HttpResponse.java |   93 +
 .../manager/testutils/HttpResponseAction.java   |   60 +
 .../cluster/manager/testutils/HttpServer.java   |  240 +
 .../ClusterManagerProtocolSenderImplTest.java   |  133 +
 .../impl/ClusterServiceLocatorTest.java         |  119 +
 .../impl/ClusterServicesBroadcasterTest.java    |  131 +
 .../impl/MulticastProtocolListenerTest.java     |  171 +
 .../impl/NodeProtocolSenderImplTest.java        |  201 +
 .../impl/SocketProtocolListenerTest.java        |  132 +
 .../testutils/DelayedProtocolHandler.java       |   57 +
 .../testutils/ReflexiveProtocolHandler.java     |   47 +
 .../src/test/resources/logback-test.xml         |   48 +
 .../apache/nifi/cluster/firewall/impl/empty.txt |    0
 .../apache/nifi/cluster/firewall/impl/ips.txt   |   12 +
 .../nifi-framework-core-api/.gitignore          |    1 +
 .../nifi-framework-core-api/pom.xml             |    2 +-
 .../controller/AbstractConfiguredComponent.java |   18 +-
 .../apache/nifi/controller/Availability.java    |   24 -
 .../nifi/controller/ProcessScheduler.java       |   27 +-
 .../apache/nifi/controller/ProcessorNode.java   |   16 +
 .../nifi/controller/ReportingTaskNode.java      |   26 +-
 .../controller/ValidationContextFactory.java    |    4 +
 .../exception/ComponentLifeCycleException.java  |   30 +
 ...ControllerServiceInstantiationException.java |   51 +
 .../ControllerServiceNotFoundException.java     |   51 -
 .../exception/ProcessorLifeCycleException.java  |   30 -
 .../reporting/ReportingTaskProvider.java        |  103 +
 .../service/ControllerServiceNode.java          |   39 +-
 .../service/ControllerServiceProvider.java      |   83 +-
 .../service/ControllerServiceReference.java     |    7 +-
 .../service/ControllerServiceState.java         |   45 +
 .../org/apache/nifi/groups/ProcessGroup.java    |   44 +-
 .../nifi-framework/nifi-framework-core/pom.xml  |   21 +-
 .../apache/nifi/controller/FlowController.java  |  280 +-
 .../nifi/controller/FlowFromDOMFactory.java     |   60 +-
 .../nifi/controller/StandardFlowSerializer.java |   68 +-
 .../nifi/controller/StandardFlowService.java    |   30 +-
 .../controller/StandardFlowSynchronizer.java    |  242 +-
 .../nifi/controller/StandardProcessorNode.java  |   47 +-
 .../apache/nifi/controller/TemplateManager.java |   33 +-
 .../reporting/AbstractReportingTaskNode.java    |   76 +-
 .../reporting/StandardReportingContext.java     |   11 +
 .../StandardReportingInitializationContext.java |   23 +-
 .../repository/FileSystemRepository.java        |   78 +-
 .../repository/StandardProcessSession.java      |   11 +-
 .../repository/StandardRepositoryRecord.java    |   15 +-
 .../io/DisableOnCloseInputStream.java           |   93 +
 .../scheduling/StandardProcessScheduler.java    |  210 +-
 .../service/ControllerServiceLoader.java        |  149 +-
 ...dControllerServiceInitializationContext.java |   20 +-
 .../service/StandardControllerServiceNode.java  |  128 +-
 .../StandardControllerServiceProvider.java      |  460 +-
 .../StandardControllerServiceReference.java     |   19 +-
 .../tasks/ContinuallyRunConnectableTask.java    |   15 +-
 .../controller/tasks/ReportingTaskWrapper.java  |   29 +-
 .../nifi/fingerprint/FingerprintFactory.java    |   79 +
 .../nifi/groups/StandardProcessGroup.java       |   90 +-
 .../nifi/persistence/FlowConfigurationDAO.java  |   25 -
 .../StandardXMLFlowConfigurationDAO.java        |  191 +-
 .../nifi/processor/SimpleProcessLogger.java     |   38 +-
 .../nifi/processor/StandardProcessContext.java  |   10 +
 .../processor/StandardSchedulingContext.java    |    5 +-
 .../processor/StandardValidationContext.java    |   37 +-
 .../StandardValidationContextFactory.java       |    5 +
 .../java/org/apache/nifi/util/DomUtils.java     |   10 +
 .../ControllerServiceConfiguration.xsd          |   61 -
 .../src/main/resources/FlowConfiguration.xsd    |   49 +-
 .../resources/ReportingTaskConfiguration.xsd    |   87 -
 .../repository/TestStandardProcessSession.java  |  149 +
 .../StandardControllerServiceProviderTest.java  |   71 +
 .../TestStandardControllerServiceProvider.java  |  385 ++
 .../controller/service/mock/DummyProcessor.java |   49 +
 .../nifi/controller/service/mock/ServiceA.java  |   49 +
 .../nifi/controller/service/mock/ServiceB.java  |   23 +
 .../service/util/TestControllerService.java     |   61 +
 .../processor/TestStandardPropertyValue.java    |   11 +-
 ...org.apache.nifi.controller.ControllerService |   15 +
 .../nifi-framework/nifi-nar-utils/.gitignore    |    1 +
 .../nifi-framework/nifi-nar-utils/pom.xml       |    2 +-
 .../nifi-framework/nifi-resources/LICENSE       |  202 +
 .../nifi-framework/nifi-resources/NOTICE        |    5 +
 .../nifi-framework/nifi-resources/pom.xml       |    2 +-
 .../src/main/assembly/dependencies.xml          |   16 +
 .../src/main/resources/conf/bootstrap.conf      |    5 +
 .../main/resources/conf/controller-services.xml |   18 -
 .../src/main/resources/conf/nifi.properties     |    2 -
 .../src/main/resources/conf/reporting-tasks.xml |   49 -
 .../nifi-framework/nifi-runtime/pom.xml         |    7 +-
 .../src/main/java/org/apache/nifi/NiFi.java     |    3 +
 .../nifi-framework/nifi-security/pom.xml        |    2 +-
 .../nifi-framework/nifi-site-to-site/pom.xml    |    2 +-
 .../nifi/remote/StandardRemoteGroupPort.java    |    6 +-
 .../nifi/remote/StandardRootGroupPort.java      |   11 +-
 .../nifi-framework/nifi-user-actions/pom.xml    |    2 +-
 .../java/org/apache/nifi/action/Component.java  |    4 +-
 .../component/details/ExtensionDetails.java     |   34 +
 .../component/details/ProcessorDetails.java     |   34 -
 .../nifi-web/nifi-custom-ui-utilities/pom.xml   |    2 +-
 .../HttpServletConfigurationRequestContext.java |   56 +
 .../nifi/web/HttpServletRequestContext.java     |  100 +
 .../web/HttpServletRequestContextConfig.java    |    1 +
 .../nifi-framework/nifi-web/nifi-jetty/pom.xml  |   17 +-
 .../org/apache/nifi/web/server/JettyServer.java |  282 +-
 .../nifi-web/nifi-ui-extension/pom.xml          |   21 +
 .../apache/nifi/ui/extension/UiExtension.java   |   52 +
 .../nifi/ui/extension/UiExtensionMapping.java   |   52 +
 .../nifi-web/nifi-web-api/pom.xml               |   14 +-
 .../nifi/audit/ControllerServiceAuditor.java    |  475 ++
 .../org/apache/nifi/audit/FunnelAuditor.java    |    8 +-
 .../java/org/apache/nifi/audit/NiFiAuditor.java |   11 +-
 .../java/org/apache/nifi/audit/PortAuditor.java |   17 +-
 .../apache/nifi/audit/ProcessGroupAuditor.java  |   18 +-
 .../org/apache/nifi/audit/ProcessorAuditor.java |   20 +-
 .../apache/nifi/audit/RelationshipAuditor.java  |   18 +-
 .../nifi/audit/RemoteProcessGroupAuditor.java   |   22 +-
 .../apache/nifi/audit/ReportingTaskAuditor.java |  353 ++
 .../org/apache/nifi/audit/SnippetAuditor.java   |   13 +-
 .../org/apache/nifi/web/NiFiServiceFacade.java  |  208 +-
 .../nifi/web/StandardNiFiContentAccess.java     |  147 +
 .../nifi/web/StandardNiFiServiceFacade.java     | 1374 +++---
 .../StandardNiFiWebConfigurationContext.java    |  736 +++
 .../apache/nifi/web/StandardNiFiWebContext.java |   30 +-
 .../nifi/web/api/ApplicationResource.java       |   76 +-
 .../apache/nifi/web/api/ClusterResource.java    |    2 +-
 .../apache/nifi/web/api/ConnectionResource.java |    6 +-
 .../apache/nifi/web/api/ControllerResource.java |  102 +-
 .../nifi/web/api/ControllerServiceResource.java |  803 ++++
 .../org/apache/nifi/web/api/FunnelResource.java |    6 +-
 .../apache/nifi/web/api/HistoryResource.java    |   70 +-
 .../apache/nifi/web/api/InputPortResource.java  |    6 +-
 .../org/apache/nifi/web/api/LabelResource.java  |    6 +-
 .../apache/nifi/web/api/OutputPortResource.java |    6 +-
 .../nifi/web/api/ProcessGroupResource.java      |   16 +-
 .../apache/nifi/web/api/ProcessorResource.java  |   72 +-
 .../apache/nifi/web/api/ProvenanceResource.java |    2 +-
 .../web/api/RemoteProcessGroupResource.java     |   10 +-
 .../nifi/web/api/ReportingTaskResource.java     |  663 +++
 .../apache/nifi/web/api/SnippetResource.java    |    6 +-
 .../web/api/config/NotFoundExceptionMapper.java |   48 +
 .../nifi/web/api/config/ThrowableMapper.java    |    7 +-
 .../org/apache/nifi/web/api/dto/DtoFactory.java |  303 +-
 .../ApplicationStartupContextListener.java      |    6 +-
 .../nifi/web/controller/ControllerFacade.java   |  114 +-
 .../nifi/web/dao/ControllerServiceDAO.java      |  110 +
 .../apache/nifi/web/dao/ReportingTaskDAO.java   |   88 +
 .../dao/impl/StandardControllerServiceDAO.java  |  320 ++
 .../nifi/web/dao/impl/StandardProcessorDAO.java |   13 +-
 .../web/dao/impl/StandardReportingTaskDAO.java  |  365 ++
 .../nifi/web/dao/impl/StandardSnippetDAO.java   |   36 +-
 .../ControllerServiceProviderFactoryBean.java   |   68 +
 .../OptimisticLockingManagerFactoryBean.java    |   67 +
 .../ReportingTaskProviderFactoryBean.java       |   69 +
 .../org/apache/nifi/web/util/Availability.java  |   34 +
 .../nifi/web/util/DownloadableContent.java      |   47 -
 .../org/apache/nifi/web/util/SnippetUtils.java  |  181 +-
 .../src/main/resources/nifi-web-api-context.xml |   68 +-
 .../nifi/integration/util/NiFiTestServer.java   |    5 +
 .../nifi-web/nifi-web-content-access/pom.xml    |   25 +
 .../java/org/apache/nifi/web/ContentAccess.java |   33 +
 .../apache/nifi/web/ContentRequestContext.java  |   51 +
 .../apache/nifi/web/DownloadableContent.java    |   62 +
 .../nifi-web/nifi-web-content-viewer/.gitignore |    1 +
 .../nifi-web/nifi-web-content-viewer/pom.xml    |   91 +
 .../nifi/web/ContentViewerController.java       |  290 ++
 .../src/main/resources/META-INF/NOTICE          |   19 +
 .../src/main/webapp/WEB-INF/jsp/footer.jsp      |   20 +
 .../src/main/webapp/WEB-INF/jsp/header.jsp      |   96 +
 .../src/main/webapp/WEB-INF/jsp/hexview.jsp     |   32 +
 .../src/main/webapp/WEB-INF/jsp/no-viewer.jsp   |   20 +
 .../src/main/webapp/WEB-INF/web.xml             |   26 +
 .../src/main/webapp/css/main.css                |  113 +
 .../src/main/webapp/js/hexview/LICENSE          |   32 +
 .../main/webapp/js/hexview/hexview.default.css  |   10 +
 .../src/main/webapp/js/hexview/hexview.js       |  199 +
 .../nifi-web/nifi-web-docs/pom.xml              |    8 +-
 .../nifi/web/docs/DocumentationController.java  |    5 +-
 .../main/webapp/WEB-INF/jsp/documentation.jsp   |   20 +-
 .../src/main/webapp/css/component-usage.css     |   98 +-
 .../src/main/webapp/js/application.js           |    7 +-
 .../nifi-web/nifi-web-error/pom.xml             |    2 +-
 .../nifi-web-optimistic-locking/pom.xml         |   14 +-
 .../apache/nifi/web/ConfigurationRequest.java   |   34 +
 .../apache/nifi/web/ConfigurationSnapshot.java  |   22 +-
 .../org/apache/nifi/web/FlowModification.java   |   57 +
 .../nifi/web/OptimisticLockingManager.java      |   76 +-
 .../web/StandardOptimisticLockingManager.java   |  150 +-
 .../org/apache/nifi/web/UpdateRevision.java     |   31 +
 .../nifi-web/nifi-web-security/pom.xml          |    2 +-
 .../nifi/web/security/user/NiFiUserUtils.java   |   10 +
 .../nifi-framework/nifi-web/nifi-web-ui/pom.xml |   33 +-
 .../main/resources/filters/canvas.properties    |    8 +-
 .../src/main/webapp/WEB-INF/pages/canvas.jsp    |   11 +-
 .../main/webapp/WEB-INF/pages/message-page.jsp  |    6 +-
 .../src/main/webapp/WEB-INF/pages/summary.jsp   |    2 +
 .../WEB-INF/partials/canvas/canvas-header.jsp   |    2 +-
 .../canvas/controller-service-configuration.jsp |   90 +
 .../disable-controller-service-dialog.jsp       |   71 +
 .../canvas/enable-controller-service-dialog.jsp |   70 +
 .../canvas/new-controller-service-dialog.jsp    |   53 +
 .../partials/canvas/new-processor-dialog.jsp    |    6 +-
 .../canvas/new-processor-property-dialog.jsp    |   34 -
 .../canvas/new-reporting-task-dialog.jsp        |   53 +
 .../partials/canvas/processor-configuration.jsp |   21 +-
 .../WEB-INF/partials/canvas/registration.jsp    |    2 +-
 .../canvas/reporting-task-configuration.jsp     |  107 +
 .../partials/canvas/settings-content.jsp        |   98 +-
 .../WEB-INF/partials/processor-details.jsp      |    4 -
 .../nifi-web-ui/src/main/webapp/WEB-INF/web.xml |   11 +
 .../nifi-web-ui/src/main/webapp/css/about.css   |    1 -
 .../nifi-web-ui/src/main/webapp/css/canvas.css  |    4 +
 .../webapp/css/connection-configuration.css     |    1 -
 .../src/main/webapp/css/connection-details.css  |    1 -
 .../src/main/webapp/css/controller-service.css  |  269 ++
 .../nifi-web-ui/src/main/webapp/css/dialog.css  |   25 +-
 .../src/main/webapp/css/label-configuration.css |    1 -
 .../nifi-web-ui/src/main/webapp/css/main.css    |   42 +-
 .../css/new-controller-service-dialog.css       |  152 +
 .../main/webapp/css/new-processor-dialog.css    |   53 +-
 .../webapp/css/new-reporting-task-dialog.css    |  152 +
 .../src/main/webapp/css/port-configuration.css  |    2 -
 .../src/main/webapp/css/port-details.css        |    2 -
 .../webapp/css/process-group-configuration.css  |    1 -
 .../main/webapp/css/process-group-details.css   |    1 -
 .../main/webapp/css/processor-configuration.css |  153 -
 .../src/main/webapp/css/processor-details.css   |   27 -
 .../src/main/webapp/css/registration.css        |    8 -
 .../css/remote-process-group-configuration.css  |    3 -
 .../src/main/webapp/css/reporting-task.css      |  116 +
 .../src/main/webapp/css/settings.css            |  123 +-
 .../nifi-web-ui/src/main/webapp/css/shell.css   |    2 +-
 .../src/main/webapp/css/status-history.css      |    1 -
 .../main/webapp/images/buttonNewProperty.png    |  Bin 590 -> 0 bytes
 .../src/main/webapp/images/iconEnable.png       |  Bin 0 -> 472 bytes
 .../src/main/webapp/images/iconUndo.png         |  Bin 642 -> 0 bytes
 .../js/codemirror/addon/fold/foldgutter.css     |   20 +
 .../js/codemirror/lib/codemirror-compressed.js  |   14 +-
 .../webapp/js/jquery/combo/jquery.combo.css     |    8 +
 .../src/main/webapp/js/jquery/jquery.each.js    |    2 +-
 .../webapp/js/jquery/modal/jquery.modal.css     |   12 +-
 .../main/webapp/js/jquery/modal/jquery.modal.js |  117 +-
 .../js/jquery/nfeditor/jquery.nfeditor.js       |    5 +-
 .../jquery/propertytable/buttonNewProperty.png  |  Bin 0 -> 590 bytes
 .../propertytable/jquery.propertytable.css      |  216 +
 .../propertytable/jquery.propertytable.js       | 1608 +++++++
 .../main/webapp/js/jquery/tabbs/jquery.tabbs.js |    2 +
 .../js/jquery/tagcloud/jquery.tagcloud.css      |   62 +
 .../js/jquery/tagcloud/jquery.tagcloud.js       |  226 +
 .../js/nf/bulletin-board/nf-bulletin-board.js   |    3 +
 .../src/main/webapp/js/nf/canvas/nf-actions.js  |   17 +-
 .../src/main/webapp/js/nf/canvas/nf-birdseye.js |    3 +
 .../webapp/js/nf/canvas/nf-canvas-header.js     |   64 +-
 .../webapp/js/nf/canvas/nf-canvas-toolbar.js    |    3 +
 .../webapp/js/nf/canvas/nf-canvas-toolbox.js    |  164 +-
 .../main/webapp/js/nf/canvas/nf-canvas-utils.js |    3 +
 .../src/main/webapp/js/nf/canvas/nf-canvas.js   |  107 +-
 .../main/webapp/js/nf/canvas/nf-clipboard.js    |    3 +
 .../main/webapp/js/nf/canvas/nf-connectable.js  |    3 +
 .../js/nf/canvas/nf-connection-configuration.js |    3 +
 .../main/webapp/js/nf/canvas/nf-connection.js   |    3 +
 .../main/webapp/js/nf/canvas/nf-context-menu.js |    3 +
 .../js/nf/canvas/nf-controller-service.js       | 1743 +++++++
 .../js/nf/canvas/nf-custom-processor-ui.js      |   43 -
 .../main/webapp/js/nf/canvas/nf-custom-ui.js    |   47 +
 .../main/webapp/js/nf/canvas/nf-draggable.js    |    3 +
 .../src/main/webapp/js/nf/canvas/nf-funnel.js   |    3 +
 .../src/main/webapp/js/nf/canvas/nf-go-to.js    |    3 +
 .../webapp/js/nf/canvas/nf-graph-control.js     |    3 +
 .../src/main/webapp/js/nf/canvas/nf-graph.js    |    3 +
 .../js/nf/canvas/nf-label-configuration.js      |    3 +
 .../src/main/webapp/js/nf/canvas/nf-label.js    |    3 +
 .../js/nf/canvas/nf-port-configuration.js       |    3 +
 .../main/webapp/js/nf/canvas/nf-port-details.js |    3 +
 .../src/main/webapp/js/nf/canvas/nf-port.js     |    3 +
 .../nf/canvas/nf-process-group-configuration.js |    3 +
 .../js/nf/canvas/nf-process-group-details.js    |    3 +
 .../webapp/js/nf/canvas/nf-process-group.js     |    3 +
 .../js/nf/canvas/nf-processor-configuration.js  |  500 ++-
 .../nf-processor-property-combo-editor.js       |  177 -
 .../canvas/nf-processor-property-nfel-editor.js |  207 -
 .../js/nf/canvas/nf-processor-property-table.js |  567 ---
 .../canvas/nf-processor-property-text-editor.js |  212 -
 .../main/webapp/js/nf/canvas/nf-processor.js    |    3 +
 .../main/webapp/js/nf/canvas/nf-registration.js |    7 +-
 .../nf-remote-process-group-configuration.js    |    3 +
 .../canvas/nf-remote-process-group-details.js   |    3 +
 .../nf/canvas/nf-remote-process-group-ports.js  |    3 +
 .../js/nf/canvas/nf-remote-process-group.js     |    3 +
 .../webapp/js/nf/canvas/nf-reporting-task.js    |  732 +++
 .../js/nf/canvas/nf-secure-port-details.js      |    3 +
 .../main/webapp/js/nf/canvas/nf-selectable.js   |    3 +
 .../src/main/webapp/js/nf/canvas/nf-settings.js | 1526 ++++++-
 .../src/main/webapp/js/nf/canvas/nf-snippet.js  |    3 +
 .../src/main/webapp/js/nf/canvas/nf-storage.js  |    3 +
 .../webapp/js/nf/canvas/nf-toolbar-action.js    |    3 +
 .../webapp/js/nf/cluster/nf-cluster-table.js    |    3 +
 .../src/main/webapp/js/nf/cluster/nf-cluster.js |    3 +
 .../webapp/js/nf/counters/nf-counters-table.js  |    3 +
 .../main/webapp/js/nf/counters/nf-counters.js   |    3 +
 .../webapp/js/nf/history/nf-history-model.js    |    3 +
 .../webapp/js/nf/history/nf-history-table.js    |    7 +-
 .../src/main/webapp/js/nf/history/nf-history.js |    3 +
 .../src/main/webapp/js/nf/nf-client.js          |    3 +
 .../src/main/webapp/js/nf/nf-common.js          |   83 +-
 .../main/webapp/js/nf/nf-connection-details.js  |    3 +
 .../src/main/webapp/js/nf/nf-dialog.js          |   18 +-
 .../main/webapp/js/nf/nf-processor-details.js   |  358 +-
 .../src/main/webapp/js/nf/nf-shell.js           |    3 +
 .../src/main/webapp/js/nf/nf-status-history.js  |    3 +
 .../js/nf/provenance/nf-provenance-lineage.js   |    3 +
 .../js/nf/provenance/nf-provenance-table.js     |    3 +
 .../webapp/js/nf/provenance/nf-provenance.js    |    3 +
 .../webapp/js/nf/summary/nf-summary-table.js    |    2 +
 .../src/main/webapp/js/nf/summary/nf-summary.js |    3 +
 .../js/nf/templates/nf-templates-table.js       |    3 +
 .../main/webapp/js/nf/templates/nf-templates.js |    3 +
 .../main/webapp/js/nf/users/nf-users-table.js   |    3 +
 .../nifi-framework/nifi-web/pom.xml             |   22 +-
 .../nifi-framework/pom.xml                      |    9 +-
 .../nifi-framework-bundle/pom.xml               |   44 +-
 .../nifi-geo-bundle/nifi-geo-nar/pom.xml        |   33 +
 .../nifi-geo-processors/.gitignore              |    1 +
 .../nifi-geo-bundle/nifi-geo-processors/pom.xml |   43 +
 .../org/apache/nifi/processors/GeoEnrichIP.java |  210 +
 .../nifi/processors/maxmind/DatabaseReader.java |  286 ++
 .../org.apache.nifi.processor.Processor         |   16 +
 nifi/nifi-nar-bundles/nifi-geo-bundle/pom.xml   |   42 +
 .../nifi-hadoop-bundle/nifi-hadoop-nar/pom.xml  |    2 +-
 .../nifi-hdfs-processors/pom.xml                |    2 +-
 .../hadoop/CreateHadoopSequenceFile.java        |    2 +
 .../apache/nifi/processors/hadoop/GetHDFS.java  |   21 +-
 .../processors/hadoop/GetHDFSSequenceFile.java  |    2 +
 .../apache/nifi/processors/hadoop/PutHDFS.java  |   20 +-
 .../additionalDetails.html                      |   46 +
 .../index.html                                  |   88 -
 .../index.html                                  |  162 -
 .../index.html                                  |  150 -
 .../index.html                                  |  159 -
 .../nifi-nar-bundles/nifi-hadoop-bundle/pom.xml |    4 +-
 .../nifi-hadoop-libraries-nar/pom.xml           |    5 +-
 .../nifi-hadoop-libraries-bundle/pom.xml        |    2 +-
 .../nifi-hl7-bundle/nifi-hl7-nar/pom.xml        |   36 +
 .../nifi-hl7-processors/.gitignore              |    1 +
 .../nifi-hl7-bundle/nifi-hl7-processors/pom.xml |  106 +
 .../processors/hl7/ExtractHL7Attributes.java    |  247 +
 .../apache/nifi/processors/hl7/RouteHL7.java    |  217 +
 .../org.apache.nifi.processor.Processor         |   16 +
 .../hl7/TestExtractHL7Attributes.java           |   48 +
 .../src/test/resources/1.hl7                    |   16 +
 .../src/test/resources/hypoglycemia.hl7         |    5 +
 nifi/nifi-nar-bundles/nifi-hl7-bundle/pom.xml   |   33 +
 nifi/nifi-nar-bundles/nifi-jetty-bundle/pom.xml |    2 +-
 .../nifi-kafka-bundle/nifi-kafka-nar/pom.xml    |    2 +-
 .../nifi-kafka-processors/pom.xml               |    2 +-
 .../apache/nifi/processors/kafka/GetKafka.java  |    6 +
 .../apache/nifi/processors/kafka/PutKafka.java  |   90 +-
 .../additionalDetails.html                      |   45 +
 .../index.html                                  |  173 -
 .../additionalDetails.html                      |   45 +
 .../index.html                                  |  189 -
 .../nifi/processors/kafka/TestPutKafka.java     |   76 +-
 nifi/nifi-nar-bundles/nifi-kafka-bundle/pom.xml |    4 +-
 .../nifi-kite-bundle/nifi-kite-nar/pom.xml      |   44 +-
 .../src/main/resources/META-INF/LICENSE         |   38 -
 .../src/main/resources/META-INF/NOTICE          |   37 +-
 .../nifi-kite-processors/pom.xml                |   18 +-
 .../processors/kite/AbstractKiteProcessor.java  |   39 +-
 .../nifi/processors/kite/ConvertCSVToAvro.java  |   29 +-
 .../nifi/processors/kite/ConvertJSONToAvro.java |   20 +-
 .../nifi/processors/kite/JSONFileReader.java    |  114 -
 .../processors/kite/StoreInKiteDataset.java     |   10 +-
 .../data/spi/filesystem/CSVFileReaderFixed.java |  172 -
 .../nifi/processors/kite/TestGetSchema.java     |    3 +
 nifi/nifi-nar-bundles/nifi-kite-bundle/pom.xml  |   18 +-
 .../nifi-language-translation-nar/pom.xml       |   36 +
 .../nifi-yandex-processors/.gitignore           |    1 +
 .../nifi-yandex-processors/pom.xml              |   63 +
 .../nifi/processors/yandex/YandexTranslate.java |  325 ++
 .../processors/yandex/model/Translation.java    |   52 +
 .../nifi/processors/yandex/util/Languages.java  |   86 +
 .../yandex/util/ObjectMapperResolver.java       |   48 +
 .../org.apache.nifi.processor.Processor         |   16 +
 .../processors/yandex/TestYandexTranslate.java  |  141 +
 .../nifi-language-translation-bundle/pom.xml    |   48 +
 .../pom.xml                                     |    2 +-
 .../PersistentProvenanceRepository.java         |   10 +-
 .../TestPersistentProvenanceRepository.java     |   42 +-
 .../nifi-provenance-repository-nar/pom.xml      |    2 +-
 .../nifi-volatile-provenance-repository/pom.xml |    2 +-
 .../nifi-provenance-repository-bundle/pom.xml   |    6 +-
 .../nifi-social-media-nar/pom.xml               |   36 +
 .../nifi-twitter-processors/.gitignore          |    1 +
 .../nifi-twitter-processors/pom.xml             |   60 +
 .../nifi/processors/twitter/GetTwitter.java     |  360 ++
 .../org.apache.nifi.processor.Processor         |   16 +
 .../nifi-social-media-bundle/pom.xml            |   33 +
 .../nifi-standard-content-viewer/pom.xml        |   76 +
 .../web/StandardContentViewerController.java    |  103 +
 .../src/main/resources/META-INF/NOTICE          |   19 +
 .../main/webapp/META-INF/nifi-content-viewer    |    3 +
 .../src/main/webapp/WEB-INF/jsp/codemirror.jsp  |   50 +
 .../src/main/webapp/WEB-INF/web.xml             |   29 +
 .../src/main/webapp/css/main.css                |   20 +
 .../nifi-standard-nar/pom.xml                   |    7 +-
 .../nifi-standard-prioritizers/pom.xml          |    2 +-
 .../nifi-standard-processors/pom.xml            |   88 +-
 .../standard/Base64EncodeContent.java           |    2 +-
 .../processors/standard/CompressContent.java    |    4 +
 .../nifi/processors/standard/ControlRate.java   |    2 +-
 .../processors/standard/DetectDuplicate.java    |   15 +-
 .../processors/standard/DistributeLoad.java     |   24 +-
 .../processors/standard/EvaluateJsonPath.java   |   32 +-
 .../standard/EvaluateRegularExpression.java     |   19 +-
 .../nifi/processors/standard/EvaluateXPath.java |    5 +
 .../processors/standard/EvaluateXQuery.java     |   22 +-
 .../processors/standard/ExecuteProcess.java     |    2 +
 .../standard/ExecuteStreamCommand.java          |   25 +-
 .../nifi/processors/standard/ExtractText.java   |  316 ++
 .../apache/nifi/processors/standard/GetFTP.java |   12 +
 .../nifi/processors/standard/GetFile.java       |   21 +-
 .../nifi/processors/standard/GetHTTP.java       |    8 +-
 .../nifi/processors/standard/GetJMSQueue.java   |    2 +
 .../nifi/processors/standard/GetJMSTopic.java   |    2 +
 .../nifi/processors/standard/GetSFTP.java       |   17 +-
 .../processors/standard/HandleHttpRequest.java  |   26 +-
 .../processors/standard/HandleHttpResponse.java |    8 +-
 .../nifi/processors/standard/HashAttribute.java |   23 +-
 .../nifi/processors/standard/HashContent.java   |    2 +
 .../processors/standard/IdentifyMimeType.java   |   13 +-
 .../nifi/processors/standard/InvokeHTTP.java    |   52 +-
 .../nifi/processors/standard/MergeContent.java  |   17 +
 .../processors/standard/MonitorActivity.java    |   15 +-
 .../nifi/processors/standard/PostHTTP.java      |   13 +-
 .../nifi/processors/standard/PutEmail.java      |  149 +-
 .../apache/nifi/processors/standard/PutFTP.java |   22 +-
 .../nifi/processors/standard/PutFile.java       |    8 +-
 .../apache/nifi/processors/standard/PutJMS.java |    6 +-
 .../nifi/processors/standard/PutSFTP.java       |   10 +-
 .../processors/standard/RouteOnAttribute.java   |   15 +-
 .../processors/standard/RouteOnContent.java     |    5 +
 .../nifi/processors/standard/ScanContent.java   |   18 +-
 .../processors/standard/SegmentContent.java     |   12 +
 .../nifi/processors/standard/SplitContent.java  |  116 +-
 .../nifi/processors/standard/SplitText.java     |   10 +
 .../nifi/processors/standard/TransformXml.java  |   15 +-
 .../nifi/processors/standard/UnpackContent.java |   37 +-
 .../org.apache.nifi.processor.Processor         |    1 +
 .../index.html                                  |   63 -
 .../index.html                                  |  166 -
 .../index.html                                  |  116 -
 .../index.html                                  |   65 -
 .../index.html                                  |  147 -
 .../index.html                                  |  106 -
 .../index.html                                  |   97 -
 .../additionalDetails.html                      |   31 +
 .../index.html                                  |  155 -
 .../index.html                                  |  160 -
 .../index.html                                  |  135 -
 .../additionalDetails.html                      |  149 +
 .../index.html                                  |  311 --
 .../index.html                                  |  100 -
 .../index.html                                  |  111 -
 .../index.html                                  |   64 -
 .../index.html                                  |  227 -
 .../index.html                                  |  186 -
 .../index.html                                  |  143 -
 .../index.html                                  |  118 -
 .../index.html                                  |  122 -
 .../index.html                                  |  250 --
 .../additionalDetails.html                      |   43 +
 .../index.html                                  |  255 --
 .../additionalDetails.html                      |   44 +
 .../index.html                                  |  112 -
 .../index.html                                  |   88 -
 .../index.html                                  |   89 -
 .../additionalDetails.html                      |   59 +
 .../index.html                                  |  136 -
 .../index.html                                  |  181 -
 .../index.html                                  |   86 -
 .../index.html                                  |  144 -
 .../index.html                                  |   80 -
 .../index.html                                  |  347 --
 .../index.html                                  |   64 -
 .../index.html                                  |  143 -
 .../index.html                                  |  187 -
 .../index.html                                  |  114 -
 .../index.html                                  |  283 --
 .../index.html                                  |  109 -
 .../index.html                                  |  152 -
 .../index.html                                  |  281 --
 .../index.html                                  |   91 -
 .../index.html                                  |  114 -
 .../additionalDetails.html                      |   46 +
 .../index.html                                  |  110 -
 .../index.html                                  |   82 -
 .../index.html                                  |   85 -
 .../index.html                                  |  100 -
 .../index.html                                  |  123 -
 .../index.html                                  |  107 -
 .../index.html                                  |   85 -
 .../index.html                                  |  121 -
 .../index.html                                  |   64 -
 .../index.html                                  |   63 -
 .../index.html                                  |  163 -
 .../index.html                                  |   56 -
 .../standard/TestDetectDuplicate.java           |    5 +-
 .../standard/TestEvaluateRegularExpression.java |  319 --
 .../processors/standard/TestExtractText.java    |  314 ++
 .../nifi/processors/standard/TestPutEmail.java  |   40 +-
 .../processors/standard/TestSplitContent.java   |  123 +
 .../nifi-standard-reporting-tasks/pom.xml       |    2 +-
 .../additionalDetails.html                      |   70 +
 .../index.html                                  |   85 -
 .../index.html                                  |   58 -
 .../index.html                                  |   77 -
 .../additionalDetails.html                      |   41 +
 .../index.html                                  |   65 -
 .../nifi-standard-bundle/pom.xml                |   18 +-
 .../pom.xml                                     |    2 +-
 .../pom.xml                                     |    2 +-
 .../DistributedMapCacheClientService.java       |   23 +-
 .../DistributedSetCacheClientService.java       |   16 +-
 .../additionalDetails.html                      |   45 +
 .../index.html                                  |   98 -
 .../index.html                                  |   51 -
 .../nifi-distributed-cache-protocol/pom.xml     |    2 +-
 .../cache/protocol/ProtocolHandshake.java       |    2 +-
 .../nifi-distributed-cache-server/pom.xml       |    2 +-
 .../cache/server/AbstractCacheServer.java       |    2 +-
 .../cache/server/DistributedCacheServer.java    |   13 +-
 .../server/map/DistributedMapCacheServer.java   |    2 +
 .../additionalDetails.html                      |   46 +
 .../index.html                                  |  103 -
 .../nifi-distributed-cache-services-nar/pom.xml |    2 +-
 .../pom.xml                                     |    2 +-
 .../nifi-http-context-map-api/pom.xml           |    4 +-
 .../nifi-http-context-map-nar/pom.xml           |    2 +-
 .../nifi-http-context-map/pom.xml               |    2 +-
 .../nifi/http/StandardHttpContextMap.java       |    9 +
 .../nifi-http-context-map-bundle/pom.xml        |    2 +-
 .../nifi-load-distribution-service-api/pom.xml  |    2 +-
 .../nifi-ssl-context-nar/pom.xml                |    2 +-
 .../nifi-ssl-context-service/pom.xml            |    2 +-
 .../nifi/ssl/StandardSSLContextService.java     |   39 +-
 .../additionalDetails.html                      |   49 +
 .../index.html                                  |   63 -
 .../nifi-ssl-context-bundle/pom.xml             |    2 +-
 .../nifi-ssl-context-service-api/pom.xml        |    2 +-
 .../nifi-standard-services-api-nar/pom.xml      |    2 +-
 .../nifi-standard-services/pom.xml              |    2 +-
 .../nifi-update-attribute-model/pom.xml         |    2 +-
 .../nifi-update-attribute-nar/pom.xml           |    2 +-
 .../nifi-update-attribute-processor/pom.xml     |    2 +-
 .../processors/attributes/UpdateAttribute.java  |    5 +
 .../additionalDetails.html                      |  253 ++
 .../index.html                                  |  253 --
 .../nifi-update-attribute-ui/pom.xml            |    7 +-
 .../update/attributes/api/RuleResource.java     |  131 +-
 .../src/main/webapp/META-INF/nifi-processor     |   15 -
 .../META-INF/nifi-processor-configuration       |   15 +
 .../src/main/webapp/WEB-INF/jsp/worksheet.jsp   |   10 +-
 .../src/main/webapp/WEB-INF/web.xml             |    4 +-
 .../src/main/webapp/js/application.js           |   12 +-
 .../nifi-update-attribute-bundle/pom.xml        |    8 +-
 nifi/nifi-nar-bundles/pom.xml                   |   38 +-
 nifi/pom.xml                                    |  132 +-
 1045 files changed, 54421 insertions(+), 34367 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/93a12104/nifi/nifi-assembly/NOTICE
----------------------------------------------------------------------
diff --cc nifi/nifi-assembly/NOTICE
index 92c48f0,9da37ae..f14c662
--- a/nifi/nifi-assembly/NOTICE
+++ b/nifi/nifi-assembly/NOTICE
@@@ -496,22 -501,39 +501,55 @@@ The following binary components are pro
        Apache License Version 2.0 http://www.apache.org/licenses/.
        (c) Daniel Lemire, http://lemire.me/en/
  
+   (ASLv2) Twitter4J
+     The following NOTICE information applies:
+       Copyright 2007 Yusuke Yamamoto
+       
+       Twitter4J includes software from JSON.org to parse JSON response from the Twitter API. You can see the license term at http://www.JSON.org/license.html
+   
+   (ASLv2) JOAuth
+     The following NOTICE information applies:
+       JOAuth
+       Copyright 2010-2013 Twitter, Inc
+ 
+       Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
+   
+   (ASLv2) Hosebird Client
+     The following NOTICE information applies:
+       Hosebird Client (hbc)
+       Copyright 2013 Twitter, Inc.
+ 
+       Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
+ 
+   (ASLv2) GeoIP2 Java API
+     The following NOTICE information applies:
+       GeoIP2 Java API
+       This software is Copyright (c) 2013 by MaxMind, Inc.
+       
+       This is free software, licensed under the Apache License, Version 2.0.
+       
+   (ASLv2) Google HTTP Client Library for Java
+     The following NOTICE information applies:
+       Google HTTP Client Library for Java
+       
+       This is free software, licensed under the Apache License, Version 2.0.
+ 
 +  (ASLv2) Amazon Web Services SDK
 +    The following NOTICE information applies:
 +		Copyright 2010-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 +
 +		This product includes software developed by
 +		Amazon Technologies, Inc (http://www.amazon.com/).
 +
 +		**********************
 +		THIRD PARTY COMPONENTS
 +		**********************
 +		This software includes third party software subject to the following copyrights:
 +		- XML parsing and utility functions from JetS3t - Copyright 2006-2009 James Murty.
 +		- JSON parsing and utility functions from JSON.org - Copyright 2002 JSON.org.
 +		- PKCS#1 PEM encoded private key parsing and utility functions from oauth.googlecode.com - Copyright 1998-2010 AOL Inc.
 +
 +
  ************************
  Common Development and Distribution License 1.1
  ************************

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/93a12104/nifi/nifi-nar-bundles/pom.xml
----------------------------------------------------------------------
diff --cc nifi/nifi-nar-bundles/pom.xml
index 374304a,50a9407..7d80826
--- a/nifi/nifi-nar-bundles/pom.xml
+++ b/nifi/nifi-nar-bundles/pom.xml
@@@ -34,8 -35,11 +34,12 @@@
          <module>nifi-update-attribute-bundle</module>
          <module>nifi-kafka-bundle</module>
  		<module>nifi-kite-bundle</module>
 +		<module>nifi-aws-bundle</module>
-   </modules>
+ 		<module>nifi-social-media-bundle</module>
+ 		<module>nifi-geo-bundle</module>
+ 		<module>nifi-hl7-bundle</module>
+ 		<module>nifi-language-translation-bundle</module>
+     </modules>
      <dependencyManagement>
          <dependencies>
              <dependency>


[41/62] [abbrv] incubator-nifi git commit: NIFI-492: When attempting to get a connection from the pool, if we create a new one and encounter an error, should ensure we close the new connection

Posted by ma...@apache.org.
NIFI-492: When attempting to get a connection from the pool, if we create a new one and encounter an error, should ensure we close the new connection


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/8d20b820
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/8d20b820
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/8d20b820

Branch: refs/heads/NIFI-25
Commit: 8d20b82095857e06ee58ae892039a5f10613b125
Parents: 39735c3
Author: Mark Payne <ma...@hotmail.com>
Authored: Tue Apr 7 14:16:54 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Tue Apr 7 14:16:54 2015 -0400

----------------------------------------------------------------------
 .../remote/client/socket/EndpointConnectionPool.java  | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/8d20b820/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java b/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java
index daf52b4..1a6dfd5 100644
--- a/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java
+++ b/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java
@@ -314,6 +314,11 @@ public class EndpointConnectionPool {
                         if ( protocol.isDestinationFull() ) {
                             logger.warn("{} {} indicates that port's destination is full; penalizing peer", this, peer);
                             penalize(peer, penalizationMillis);
+                            try {
+                            	peer.close();
+                            } catch (final IOException ioe) {
+                            }
+                            
                             continue;
                         } else if ( protocol.isPortInvalid() ) {
                         	penalize(peer, penalizationMillis);
@@ -359,6 +364,15 @@ public class EndpointConnectionPool {
                     }
                 }
             } while ( connection == null || codec == null || commsSession == null || protocol == null );
+        } catch (final Throwable t) {
+        	if ( commsSession != null ) {
+        		try {
+        			commsSession.close();
+        		} catch (final IOException ioe) {
+        		}
+        	}
+        	
+        	throw t;
         } finally {
             if ( !addBack.isEmpty() ) {
                 connectionQueue.addAll(addBack);


[33/62] [abbrv] incubator-nifi git commit: Merge branch 'develop' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into develop

Posted by ma...@apache.org.
Merge branch 'develop' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into develop


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/e79db821
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/e79db821
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/e79db821

Branch: refs/heads/NIFI-25
Commit: e79db821b613143483c4959177b19aa4428d20a9
Parents: 6bbd172 4a16845
Author: Matt Gilman <ma...@gmail.com>
Authored: Mon Apr 6 07:20:24 2015 -0400
Committer: Matt Gilman <ma...@gmail.com>
Committed: Mon Apr 6 07:20:24 2015 -0400

----------------------------------------------------------------------
 .../cluster/manager/impl/WebClusterManager.java |  4 +--
 .../exception/ComponentLifeCycleException.java  | 30 ++++++++++++++++++++
 .../exception/ProcessorLifeCycleException.java  | 30 --------------------
 .../apache/nifi/controller/FlowController.java  |  6 ++--
 .../reporting/AbstractReportingTaskNode.java    |  4 +--
 .../service/StandardControllerServiceNode.java  |  4 +--
 .../StandardControllerServiceProvider.java      |  4 +--
 .../nifi/groups/StandardProcessGroup.java       |  4 +--
 .../nifi/web/dao/impl/StandardProcessorDAO.java |  8 +++---
 .../web/dao/impl/StandardReportingTaskDAO.java  |  4 +--
 .../processors/standard/EvaluateJsonPath.java   |  1 +
 11 files changed, 50 insertions(+), 49 deletions(-)
----------------------------------------------------------------------



[40/62] [abbrv] incubator-nifi git commit: Merge branch 'develop' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into develop

Posted by ma...@apache.org.
Merge branch 'develop' of http://git-wip-us.apache.org/repos/asf/incubator-nifi into develop


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/39735c30
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/39735c30
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/39735c30

Branch: refs/heads/NIFI-25
Commit: 39735c30bcf2dccb090dd8c60a8668c27d9c7517
Parents: c974ea9 7369730
Author: Mark Payne <ma...@hotmail.com>
Authored: Tue Apr 7 12:46:48 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Tue Apr 7 12:46:48 2015 -0400

----------------------------------------------------------------------
 .../service/StandardControllerServiceNode.java      |  2 +-
 .../js/jquery/propertytable/jquery.propertytable.js | 16 +++++++++++++++-
 2 files changed, 16 insertions(+), 2 deletions(-)
----------------------------------------------------------------------



[20/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ValidationContextFactory.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ValidationContextFactory.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ValidationContextFactory.java
index df3c251..09479d5 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ValidationContextFactory.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ValidationContextFactory.java
@@ -17,6 +17,7 @@
 package org.apache.nifi.controller;
 
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.ValidationContext;
@@ -24,4 +25,7 @@ import org.apache.nifi.components.ValidationContext;
 public interface ValidationContextFactory {
 
     ValidationContext newValidationContext(Map<PropertyDescriptor, String> properties, String annotationData);
+    
+    ValidationContext newValidationContext(Set<String> serviceIdentifiersToNotValidate, Map<PropertyDescriptor, String> properties, String annotationData);
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/exception/ControllerServiceInstantiationException.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/exception/ControllerServiceInstantiationException.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/exception/ControllerServiceInstantiationException.java
new file mode 100644
index 0000000..18cfcda
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/exception/ControllerServiceInstantiationException.java
@@ -0,0 +1,51 @@
+/*
+ * 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.controller.exception;
+
+public class ControllerServiceInstantiationException extends RuntimeException {
+
+    private static final long serialVersionUID = -544424320587059277L;
+
+    /**
+     * Constructs a default exception
+     */
+    public ControllerServiceInstantiationException() {
+        super();
+    }
+
+    /**
+     * @param message
+     */
+    public ControllerServiceInstantiationException(String message) {
+        super(message);
+    }
+
+    /**
+     * @param cause
+     */
+    public ControllerServiceInstantiationException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * @param message
+     * @param cause
+     */
+    public ControllerServiceInstantiationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/exception/ControllerServiceNotFoundException.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/exception/ControllerServiceNotFoundException.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/exception/ControllerServiceNotFoundException.java
deleted file mode 100644
index 4cdbe54..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/exception/ControllerServiceNotFoundException.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.controller.exception;
-
-public class ControllerServiceNotFoundException extends RuntimeException {
-
-    private static final long serialVersionUID = -544424320587059277L;
-
-    /**
-     * Constructs a default exception
-     */
-    public ControllerServiceNotFoundException() {
-        super();
-    }
-
-    /**
-     * @param message
-     */
-    public ControllerServiceNotFoundException(String message) {
-        super(message);
-    }
-
-    /**
-     * @param cause
-     */
-    public ControllerServiceNotFoundException(Throwable cause) {
-        super(cause);
-    }
-
-    /**
-     * @param message
-     * @param cause
-     */
-    public ControllerServiceNotFoundException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/reporting/ReportingTaskProvider.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/reporting/ReportingTaskProvider.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/reporting/ReportingTaskProvider.java
new file mode 100644
index 0000000..bb6f3f7
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/reporting/ReportingTaskProvider.java
@@ -0,0 +1,103 @@
+/*
+ * 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.controller.reporting;
+
+import java.util.Set;
+import org.apache.nifi.controller.ReportingTaskNode;
+
+/**
+ * A ReportingTaskProvider is responsible for providing management of, and access to, Reporting Tasks
+ */
+public interface ReportingTaskProvider {
+
+    /**
+     * Creates a new instance of a reporting task
+     * 
+     * @param type the type (fully qualified class name) of the reporting task to instantiate
+     * @param id the identifier for the Reporting Task
+     * @param firstTimeAdded whether or not this is the first time that the reporting task is being added
+     * to the flow. I.e., this will be true only when the user adds the reporting task to the flow, not when
+     * the flow is being restored after a restart of the software
+     * 
+     * @return the ReportingTaskNode that is used to manage the reporting task
+     * 
+     * @throws ReportingTaskInstantiationException if unable to create the Reporting Task
+     */
+    ReportingTaskNode createReportingTask(String type, String id, boolean firstTimeAdded) throws ReportingTaskInstantiationException;
+    
+    /**
+     * Returns the reporting task that has the given identifier, or <code>null</code> if no reporting task
+     * exists with that ID.
+     * 
+     * @param identifier
+     * @return
+     */
+    ReportingTaskNode getReportingTaskNode(String identifier);
+    
+    /**
+     * Returns a Set of all Reporting Tasks that exist for this service provider.
+     * @return
+     */
+    Set<ReportingTaskNode> getAllReportingTasks();
+    
+    /**
+     * Removes the given reporting task from the flow
+     * 
+     * @param reportingTask
+     * 
+     * @throws IllegalStateException if the reporting task cannot be removed because it is not stopped, or
+     * if the reporting task is not known in the flow
+     */
+    void removeReportingTask(ReportingTaskNode reportingTask);
+    
+    /**
+     * Begins scheduling the reporting task to run and invokes appropriate lifecycle methods
+     * @param reportingTask
+     * 
+     * @throws IllegalStateException if the ReportingTask's state is not STOPPED, or if the Reporting Task has active
+     * threads, or if the ReportingTask is not valid
+     */
+    void startReportingTask(ReportingTaskNode reportingTask);
+    
+    /**
+     * Stops scheduling the reporting task to run and invokes appropriate lifecycle methods
+     * @param reportingTask
+     * 
+     * @throws IllegalStateException if the ReportingTask's state is not RUNNING
+     */
+    void stopReportingTask(ReportingTaskNode reportingTask);
+    
+    
+    /**
+     * Enables the reporting task to be scheduled to run
+     * @param reportingTask
+     * 
+     * @throws IllegalStateException if the ReportingTask's state is not DISABLED
+     */
+    void enableReportingTask(ReportingTaskNode reportingTask);
+    
+    
+    /**
+     * Disables the ability to schedul the reporting task to run
+     * 
+     * @param reportingTask
+     * 
+     * @throws IllegalStateException if the ReportingTask's state is not STOPPED, or if the Reporting Task has active
+     * threads
+     */
+    void disableReportingTask(ReportingTaskNode reportingTask);
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceNode.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceNode.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceNode.java
index 66bad39..50bf469 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceNode.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceNode.java
@@ -16,7 +16,8 @@
  */
 package org.apache.nifi.controller.service;
 
-import org.apache.nifi.controller.Availability;
+import java.util.Set;
+
 import org.apache.nifi.controller.ConfiguredComponent;
 import org.apache.nifi.controller.ControllerService;
 
@@ -26,22 +27,42 @@ public interface ControllerServiceNode extends ConfiguredComponent {
     
     ControllerService getControllerServiceImplementation();
 
-    Availability getAvailability();
-
-    void setAvailability(Availability availability);
-
-    boolean isDisabled();
-
-    void setDisabled(boolean disabled);
-
+    ControllerServiceState getState();
+    void setState(ControllerServiceState state);
+    
     ControllerServiceReference getReferences();
 
     void addReference(ConfiguredComponent referringComponent);
 
     void removeReference(ConfiguredComponent referringComponent);
     
+    void setComments(String comment);
+    String getComments();
+    
     void verifyCanEnable();
     void verifyCanDisable();
+    
+    /**
+     * Verifies that this Controller Service can be disabled if the provided set of 
+     * services are also disabled. This is introduced because we can have an instance
+     * where A references B, which references C, which references A and we want
+     * to disable service C. In this case, the cycle needs to not cause us to fail, 
+     * so we want to verify that C can be disabled if A and B also are. 
+     * 
+     * @param ignoredReferences
+     */
+    void verifyCanDisable(Set<ControllerServiceNode> ignoredReferences);
+    
+    /**
+     * Verifies that this Controller Service can be enabled if the provided set of
+     * services are also enabled. This is introduced because we can have an instance where
+     * A reference B, which references C, which references A and we want to enable
+     * Service A. In this case, the cycle needs to not cause us to fail, so we want to verify
+     * that A can be enabled if A and B also are.
+     * @param ignoredReferences
+     */
+    void verifyCanEnable(Set<ControllerServiceNode> ignoredReferences);
+    
     void verifyCanDelete();
     void verifyCanUpdate();
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceProvider.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceProvider.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceProvider.java
index 5f2fc2e..1901fb6 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceProvider.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceProvider.java
@@ -16,6 +16,9 @@
  */
 package org.apache.nifi.controller.service;
 
+import java.util.Collection;
+import java.util.Set;
+
 import org.apache.nifi.annotation.lifecycle.OnAdded;
 import org.apache.nifi.controller.ControllerServiceLookup;
 
@@ -25,7 +28,7 @@ import org.apache.nifi.controller.ControllerServiceLookup;
 public interface ControllerServiceProvider extends ControllerServiceLookup {
 
     /**
-     * Creates a new Controller Service of the given type and assigns it the given id. If <code>firstTimeadded</code>
+     * Creates a new Controller Service of the specified type and assigns it the given id. If <code>firstTimeadded</code>
      * is true, calls any methods that are annotated with {@link OnAdded}
      *
      * @param type
@@ -61,9 +64,87 @@ public interface ControllerServiceProvider extends ControllerServiceLookup {
     void enableControllerService(ControllerServiceNode serviceNode);
     
     /**
+     * Enables the collection of services. If a service in this collection depends on another service,
+     * the service being depended on must either already be enabled or must be in the collection as well.
+     * @param serviceNodes
+     */
+    void enableControllerServices(Collection<ControllerServiceNode> serviceNodes);
+    
+    /**
      * Disables the given controller service so that it cannot be used by other components. This allows
      * configuration to be updated or allows service to be removed.
      * @param serviceNode
      */
     void disableControllerService(ControllerServiceNode serviceNode);
+    
+    /**
+     * Returns a Set of all Controller Services that exist for this service provider.
+     * @return
+     */
+    Set<ControllerServiceNode> getAllControllerServices();
+    
+    /**
+     * Verifies that all running Processors and Reporting Tasks referencing the Controller Service (or a service
+     * that depends on the provided service) can be stopped. 
+     * @param serviceNode
+     * 
+     * @throws IllegalStateException if any referencing component cannot be stopped
+     */
+    void verifyCanStopReferencingComponents(ControllerServiceNode serviceNode);
+    
+    /**
+     * Recursively unschedules all schedulable components (Processors and Reporting Tasks) that reference the given
+     * Controller Service. For any Controller services that reference this one, its schedulable referencing components will also
+     * be unscheduled.
+     * @param serviceNode
+     */
+    void unscheduleReferencingComponents(ControllerServiceNode serviceNode);
+    
+    /**
+     * Verifies that all Controller Services referencing the provided Controller Service can be disabled. 
+     * @param serviceNode
+     * 
+     * @throws IllegalStateException if any referencing service cannot be disabled
+     */
+    void verifyCanDisableReferencingServices(ControllerServiceNode serviceNode);
+    
+    /**
+     * Disables any Controller Service that references the provided Controller Service. This action is performed recursively
+     * so that if service A references B and B references C, disabling references for C will first disable A, then B.
+     * @param serviceNode
+     */
+    void disableReferencingServices(ControllerServiceNode serviceNode);
+    
+    /**
+     * Verifies that all Controller Services referencing the provided ControllerService can be enabled.
+     * @param serviceNode
+     * 
+     * @throws IllegalStateException if any referencing component cannot be enabled
+     */
+    void verifyCanEnableReferencingServices(ControllerServiceNode serviceNode);
+    
+    
+    /**
+     * Enables all Controller Services that are referencing the given service. If Service A references Service B and Service
+     * B references serviceNode, Service A and B will both be enabled.
+     * @param serviceNode
+     */
+    void enableReferencingServices(ControllerServiceNode serviceNode);
+    
+    /**
+     * Verifies that all enabled Processors referencing the ControllerService (or a service that depends on 
+     * the provided service) can be scheduled to run.
+     * @param serviceNode
+     * 
+     * @throws IllegalStateException if any referencing component cannot be scheduled
+     */
+    void verifyCanScheduleReferencingComponents(ControllerServiceNode serviceNode);
+    
+    /**
+     * Schedules any schedulable component (Processor, ReportingTask) that is referencing the given Controller Service
+     * to run. This is performed recursively, so if a Processor is referencing Service A, which is referencing serviceNode,
+     * then the Processor will also be started.
+     * @param serviceNode
+     */
+    void scheduleReferencingComponents(ControllerServiceNode serviceNode);
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceReference.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceReference.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceReference.java
index 5cb676f..67ffb6c 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceReference.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceReference.java
@@ -41,10 +41,11 @@ public interface ControllerServiceReference {
     Set<ConfiguredComponent> getReferencingComponents();
 
     /**
-     * Returns a {@link Set} of all Processors and Reporting Tasks that are
-     * referencing the Controller Service and are running, in addition to all
+     * Returns a {@link Set} of all Processors, Reporting Tasks, and Controller Services that are
+     * referencing the Controller Service and are running (in the case of Processors and Reporting Tasks)
+     * or enabled (in the case of Controller Services)
      *
      * @return
      */
-    Set<ConfiguredComponent> getRunningReferences();
+    Set<ConfiguredComponent> getActiveReferences();
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceState.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceState.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceState.java
new file mode 100644
index 0000000..2ed8fd9
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/service/ControllerServiceState.java
@@ -0,0 +1,45 @@
+/*
+ * 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.controller.service;
+
+
+/**
+ * Represents the valid states for a Controller Service.
+ */
+public enum ControllerServiceState {
+    /**
+     * Controller Service is disabled and cannot be used.
+     */
+    DISABLED,
+    
+    /**
+     * Controller Service has been disabled but has not yet finished its lifecycle
+     * methods.
+     */
+    DISABLING,
+    
+    /**
+     * Controller Service has been enabled but has not yet finished its lifecycle methods.
+     */
+    ENABLING,
+    
+    /**
+     * Controller Service has been enabled and has finished its lifecycle methods. The Controller SErvice
+     * is ready to be used.
+     */
+    ENABLED;
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
index 06ef203..f3fb67c 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
@@ -52,6 +52,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.admin.service.UserService;
 import org.apache.nifi.annotation.lifecycle.OnAdded;
 import org.apache.nifi.annotation.lifecycle.OnRemoved;
+import org.apache.nifi.annotation.lifecycle.OnShutdown;
 import org.apache.nifi.cluster.BulletinsPayload;
 import org.apache.nifi.cluster.HeartbeatPayload;
 import org.apache.nifi.cluster.protocol.DataFlow;
@@ -62,6 +63,7 @@ import org.apache.nifi.cluster.protocol.NodeProtocolSender;
 import org.apache.nifi.cluster.protocol.UnknownServiceAddressException;
 import org.apache.nifi.cluster.protocol.message.HeartbeatMessage;
 import org.apache.nifi.cluster.protocol.message.NodeBulletinsMessage;
+import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.connectable.Connectable;
 import org.apache.nifi.connectable.ConnectableType;
 import org.apache.nifi.connectable.Connection;
@@ -77,6 +79,8 @@ import org.apache.nifi.controller.exception.ProcessorLifeCycleException;
 import org.apache.nifi.controller.label.Label;
 import org.apache.nifi.controller.label.StandardLabel;
 import org.apache.nifi.controller.reporting.ReportingTaskInstantiationException;
+import org.apache.nifi.controller.reporting.ReportingTaskProvider;
+import org.apache.nifi.controller.reporting.StandardReportingInitializationContext;
 import org.apache.nifi.controller.reporting.StandardReportingTaskNode;
 import org.apache.nifi.controller.repository.ContentRepository;
 import org.apache.nifi.controller.repository.CounterRepository;
@@ -103,6 +107,7 @@ import org.apache.nifi.controller.scheduling.StandardProcessScheduler;
 import org.apache.nifi.controller.scheduling.TimerDrivenSchedulingAgent;
 import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.controller.service.ControllerServiceProvider;
+import org.apache.nifi.controller.service.StandardConfigurationContext;
 import org.apache.nifi.controller.service.StandardControllerServiceProvider;
 import org.apache.nifi.controller.status.ConnectionStatus;
 import org.apache.nifi.controller.status.PortStatus;
@@ -129,6 +134,7 @@ import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.groups.RemoteProcessGroup;
 import org.apache.nifi.groups.RemoteProcessGroupPortDescriptor;
 import org.apache.nifi.groups.StandardProcessGroup;
+import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.logging.LogLevel;
 import org.apache.nifi.logging.LogRepository;
 import org.apache.nifi.logging.LogRepositoryFactory;
@@ -161,6 +167,8 @@ import org.apache.nifi.remote.protocol.socket.SocketFlowFileServerProtocol;
 import org.apache.nifi.reporting.Bulletin;
 import org.apache.nifi.reporting.BulletinRepository;
 import org.apache.nifi.reporting.EventAccess;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.reporting.ReportingInitializationContext;
 import org.apache.nifi.reporting.ReportingTask;
 import org.apache.nifi.reporting.Severity;
 import org.apache.nifi.scheduling.SchedulingStrategy;
@@ -170,6 +178,7 @@ import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.util.ReflectionUtils;
 import org.apache.nifi.web.api.dto.ConnectableDTO;
 import org.apache.nifi.web.api.dto.ConnectionDTO;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
 import org.apache.nifi.web.api.dto.FlowSnippetDTO;
 import org.apache.nifi.web.api.dto.FunnelDTO;
 import org.apache.nifi.web.api.dto.LabelDTO;
@@ -189,7 +198,7 @@ import org.slf4j.LoggerFactory;
 
 import com.sun.jersey.api.client.ClientHandlerException;
 
-public class FlowController implements EventAccess, ControllerServiceProvider, Heartbeater, QueueProvider {
+public class FlowController implements EventAccess, ControllerServiceProvider, ReportingTaskProvider, Heartbeater, QueueProvider {
 
     // default repository implementations
     public static final String DEFAULT_FLOWFILE_REPO_IMPLEMENTATION = "org.apache.nifi.controller.repository.WriteAheadFlowFileRepository";
@@ -374,7 +383,6 @@ public class FlowController implements EventAccess, ControllerServiceProvider, H
         this.properties = properties;
         sslContext = SslContextFactory.createSslContext(properties, false);
         extensionManager = new ExtensionManager();
-        controllerServiceProvider = new StandardControllerServiceProvider();
 
         timerDrivenEngineRef = new AtomicReference<>(new FlowEngine(maxTimerDrivenThreads.get(), "Timer-Driven Process"));
         eventDrivenEngineRef = new AtomicReference<>(new FlowEngine(maxEventDrivenThreads.get(), "Event-Driven Process"));
@@ -398,6 +406,7 @@ public class FlowController implements EventAccess, ControllerServiceProvider, H
 
         processScheduler = new StandardProcessScheduler(this, this, encryptor);
         eventDrivenWorkerQueue = new EventDrivenWorkerQueue(false, false, processScheduler);
+        controllerServiceProvider = new StandardControllerServiceProvider(processScheduler, bulletinRepository);
 
         final ProcessContextFactory contextFactory = new ProcessContextFactory(contentRepository, flowFileRepository, flowFileEventRepository, counterRepositoryRef.get(), provenanceEventRepository);
         processScheduler.setSchedulingAgent(SchedulingStrategy.EVENT_DRIVEN, new EventDrivenSchedulingAgent(
@@ -593,7 +602,10 @@ public class FlowController implements EventAccess, ControllerServiceProvider, H
                             startConnectable(connectable);
                         }
                     } catch (final Throwable t) {
-                        LOG.error("Unable to start {} due to {}", new Object[]{connectable, t});
+                        LOG.error("Unable to start {} due to {}", new Object[]{connectable, t.toString()});
+                        if ( LOG.isDebugEnabled() ) {
+                            LOG.error("", t);
+                        }
                     }
                 }
     
@@ -1063,7 +1075,23 @@ public class FlowController implements EventAccess, ControllerServiceProvider, H
 
             // Trigger any processors' methods marked with @OnShutdown to be called
             rootGroup.shutdown();
-
+            
+            // invoke any methods annotated with @OnShutdown on Controller Services
+            for ( final ControllerServiceNode serviceNode : getAllControllerServices() ) {
+                try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
+                    final ConfigurationContext configContext = new StandardConfigurationContext(serviceNode, controllerServiceProvider);
+                    ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnShutdown.class, serviceNode.getControllerServiceImplementation(), configContext);
+                }
+            }
+            
+            // invoke any methods annotated with @OnShutdown on Reporting Tasks
+            for ( final ReportingTaskNode taskNode : getAllReportingTasks() ) {
+                final ConfigurationContext configContext = taskNode.getConfigurationContext();
+                try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
+                    ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnShutdown.class, taskNode.getReportingTask(), configContext);
+                }
+            }
+            
             try {
                 this.timerDrivenEngineRef.get().awaitTermination(gracefulShutdownSeconds / 2, TimeUnit.SECONDS);
                 this.eventDrivenEngineRef.get().awaitTermination(gracefulShutdownSeconds / 2, TimeUnit.SECONDS);
@@ -1402,6 +1430,30 @@ public class FlowController implements EventAccess, ControllerServiceProvider, H
             validateSnippetContents(requireNonNull(group), dto);
 
             //
+            // Instantiate Controller Services
+            //
+            for ( final ControllerServiceDTO controllerServiceDTO : dto.getControllerServices() ) {
+                final ControllerServiceNode serviceNode = createControllerService(controllerServiceDTO.getType(), controllerServiceDTO.getId(), true);
+                
+                serviceNode.setAnnotationData(controllerServiceDTO.getAnnotationData());
+                serviceNode.setComments(controllerServiceDTO.getComments());
+                serviceNode.setName(controllerServiceDTO.getName());
+            }
+            
+            // configure controller services. We do this after creating all of them in case 1 service
+            // references another service.
+            for ( final ControllerServiceDTO controllerServiceDTO : dto.getControllerServices() ) {
+                final String serviceId = controllerServiceDTO.getId();
+                final ControllerServiceNode serviceNode = getControllerServiceNode(serviceId);
+                
+                for ( final Map.Entry<String, String> entry : controllerServiceDTO.getProperties().entrySet() ) {
+                    if ( entry.getValue() != null ) {
+                        serviceNode.setProperty(entry.getKey(), entry.getValue());
+                    }
+                }
+            }
+            
+            //
             // Instantiate the labels
             //
             for (final LabelDTO labelDTO : dto.getLabels()) {
@@ -1411,7 +1463,7 @@ public class FlowController implements EventAccess, ControllerServiceProvider, H
                     label.setSize(new Size(labelDTO.getWidth(), labelDTO.getHeight()));
                 }
 
-                // TODO: Update the label's "style"
+                label.setStyle(labelDTO.getStyle());
                 group.addLabel(label);
             }
 
@@ -1737,14 +1789,18 @@ public class FlowController implements EventAccess, ControllerServiceProvider, H
         }
 
         // validate that all Processor Types and Prioritizer Types are valid
-        final List<String> processorClasses = new ArrayList<>();
+        final Set<String> processorClasses = new HashSet<>();
         for (final Class<?> c : ExtensionManager.getExtensions(Processor.class)) {
             processorClasses.add(c.getName());
         }
-        final List<String> prioritizerClasses = new ArrayList<>();
+        final Set<String> prioritizerClasses = new HashSet<>();
         for (final Class<?> c : ExtensionManager.getExtensions(FlowFilePrioritizer.class)) {
             prioritizerClasses.add(c.getName());
         }
+        final Set<String> controllerServiceClasses = new HashSet<>();
+        for (final Class<?> c : ExtensionManager.getExtensions(ControllerService.class)) {
+            controllerServiceClasses.add(c.getName());
+        }
 
         final Set<ProcessorDTO> allProcs = new HashSet<>();
         final Set<ConnectionDTO> allConns = new HashSet<>();
@@ -1760,6 +1816,15 @@ public class FlowController implements EventAccess, ControllerServiceProvider, H
                 throw new IllegalStateException("Invalid Processor Type: " + proc.getType());
             }
         }
+        
+        final Set<ControllerServiceDTO> controllerServices = templateContents.getControllerServices();
+        if (controllerServices != null) {
+            for (final ControllerServiceDTO service : controllerServices) {
+                if (!controllerServiceClasses.contains(service.getType())) {
+                    throw new IllegalStateException("Invalid Controller Service Type: " + service.getType());
+                }
+            }
+        }
 
         for (final ConnectionDTO conn : allConns) {
             final List<String> prioritizers = conn.getPrioritizers();
@@ -2480,17 +2545,20 @@ public class FlowController implements EventAccess, ControllerServiceProvider, H
         lookupGroup(groupId).stopProcessing();
     }
 
-    public ReportingTaskNode createReportingTask(final String type, String id) throws ReportingTaskInstantiationException {
-        return createReportingTask(type, id, true);
+    public ReportingTaskNode createReportingTask(final String type) throws ReportingTaskInstantiationException {
+        return createReportingTask(type, true);
+    }
+    
+    public ReportingTaskNode createReportingTask(final String type, final boolean firstTimeAdded) throws ReportingTaskInstantiationException {
+    	return createReportingTask(type, UUID.randomUUID().toString(), firstTimeAdded);
     }
     
-    public ReportingTaskNode createReportingTask(final String type, String id, final boolean firstTimeAdded) throws ReportingTaskInstantiationException {
-        if (type == null) {
+    @Override
+    public ReportingTaskNode createReportingTask(final String type, final String id, final boolean firstTimeAdded) throws ReportingTaskInstantiationException {
+        if (type == null || id == null) {
             throw new NullPointerException();
         }
-
-        id = requireNonNull(id).intern();
-
+        
         ReportingTask task = null;
         final ClassLoader ctxClassLoader = Thread.currentThread().getContextClassLoader();
         try {
@@ -2516,8 +2584,19 @@ public class FlowController implements EventAccess, ControllerServiceProvider, H
 
         final ValidationContextFactory validationContextFactory = new StandardValidationContextFactory(controllerServiceProvider);
         final ReportingTaskNode taskNode = new StandardReportingTaskNode(task, id, this, processScheduler, validationContextFactory);
+        taskNode.setName(task.getClass().getSimpleName());
         
         if ( firstTimeAdded ) {
+            final ComponentLog componentLog = new SimpleProcessLogger(id, taskNode.getReportingTask());
+            final ReportingInitializationContext config = new StandardReportingInitializationContext(id, taskNode.getName(),
+                    SchedulingStrategy.TIMER_DRIVEN, "1 min", componentLog, this);
+
+            try {
+                task.initialize(config);
+            } catch (final InitializationException ie) {
+                throw new ReportingTaskInstantiationException("Failed to initialize reporting task of type " + type, ie);
+            }
+                    
             try (final NarCloseable x = NarCloseable.withNarLoader()) {
                 ReflectionUtils.invokeMethodsWithAnnotation(OnAdded.class, task);
             } catch (final Exception e) {
@@ -2529,30 +2608,33 @@ public class FlowController implements EventAccess, ControllerServiceProvider, H
         return taskNode;
     }
 
+    @Override
     public ReportingTaskNode getReportingTaskNode(final String taskId) {
         return reportingTasks.get(taskId);
     }
 
+    @Override
     public void startReportingTask(final ReportingTaskNode reportingTaskNode) {
         if (isTerminated()) {
             throw new IllegalStateException("Cannot start reporting task " + reportingTaskNode + " because the controller is terminated");
         }
 
         reportingTaskNode.verifyCanStart();
-        
-        processScheduler.schedule(reportingTaskNode);
+       	processScheduler.schedule(reportingTaskNode);
     }
 
+    
+    @Override
     public void stopReportingTask(final ReportingTaskNode reportingTaskNode) {
         if (isTerminated()) {
             return;
         }
 
         reportingTaskNode.verifyCanStop();
-        
         processScheduler.unschedule(reportingTaskNode);
     }
 
+    @Override
     public void removeReportingTask(final ReportingTaskNode reportingTaskNode) {
         final ReportingTaskNode existing = reportingTasks.get(reportingTaskNode.getIdentifier());
         if ( existing == null || existing != reportingTaskNode ) {
@@ -2565,44 +2647,101 @@ public class FlowController implements EventAccess, ControllerServiceProvider, H
             ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnRemoved.class, reportingTaskNode.getReportingTask(), reportingTaskNode.getConfigurationContext());
         }
         
+        for ( final Map.Entry<PropertyDescriptor, String> entry : reportingTaskNode.getProperties().entrySet() ) {
+            final PropertyDescriptor descriptor = entry.getKey();
+            if (descriptor.getControllerServiceDefinition() != null ) {
+                final String value = entry.getValue() == null ? descriptor.getDefaultValue() : entry.getValue();
+                if ( value != null ) {
+                    final ControllerServiceNode serviceNode = controllerServiceProvider.getControllerServiceNode(value);
+                    if ( serviceNode != null ) {
+                        serviceNode.removeReference(reportingTaskNode);
+                    }
+                }
+            }
+        }
+        
         reportingTasks.remove(reportingTaskNode.getIdentifier());
     }
     
-    Collection<ReportingTaskNode> getReportingTasks() {
-        return reportingTasks.values();
+    @Override
+    public Set<ReportingTaskNode> getAllReportingTasks() {
+        return new HashSet<>(reportingTasks.values());
     }
 
-
+    @Override
+    public ControllerServiceNode createControllerService(final String type, final String id, final boolean firstTimeAdded) {
+        return controllerServiceProvider.createControllerService(type, id, firstTimeAdded);
+    }
+    
+    @Override
     public void enableReportingTask(final ReportingTaskNode reportingTaskNode) {
         reportingTaskNode.verifyCanEnable();
-        
         processScheduler.enableReportingTask(reportingTaskNode);
     }
     
+    @Override
     public void disableReportingTask(final ReportingTaskNode reportingTaskNode) {
         reportingTaskNode.verifyCanDisable();
-        
         processScheduler.disableReportingTask(reportingTaskNode);
     }
     
     @Override
+    public void disableReferencingServices(final ControllerServiceNode serviceNode) {
+        controllerServiceProvider.disableReferencingServices(serviceNode);
+    }
+    
+    @Override
+    public void enableReferencingServices(final ControllerServiceNode serviceNode) {
+        controllerServiceProvider.enableReferencingServices(serviceNode);
+    }
+    
+    @Override
+    public void scheduleReferencingComponents(final ControllerServiceNode serviceNode) {
+        controllerServiceProvider.scheduleReferencingComponents(serviceNode);
+    }
+    
+    @Override
+    public void unscheduleReferencingComponents(final ControllerServiceNode serviceNode) {
+        controllerServiceProvider.unscheduleReferencingComponents(serviceNode);
+    }
+    
+    @Override
     public void enableControllerService(final ControllerServiceNode serviceNode) {
-        serviceNode.verifyCanEnable();
         controllerServiceProvider.enableControllerService(serviceNode);
     }
     
     @Override
+    public void enableControllerServices(final Collection<ControllerServiceNode> serviceNodes) {
+        controllerServiceProvider.enableControllerServices(serviceNodes);
+    }
+    
+    @Override
     public void disableControllerService(final ControllerServiceNode serviceNode) {
         serviceNode.verifyCanDisable();
         controllerServiceProvider.disableControllerService(serviceNode);
     }
-
+    
     @Override
-    public ControllerServiceNode createControllerService(final String type, final String id, final boolean firstTimeAdded) {
-        return controllerServiceProvider.createControllerService(type, id.intern(), firstTimeAdded);
+    public void verifyCanEnableReferencingServices(final ControllerServiceNode serviceNode) {
+        controllerServiceProvider.verifyCanEnableReferencingServices(serviceNode);
+    }
+    
+    @Override
+    public void verifyCanScheduleReferencingComponents(final ControllerServiceNode serviceNode) {
+        controllerServiceProvider.verifyCanScheduleReferencingComponents(serviceNode);
     }
 
     @Override
+    public void verifyCanDisableReferencingServices(final ControllerServiceNode serviceNode) {
+        controllerServiceProvider.verifyCanDisableReferencingServices(serviceNode);
+    }
+    
+    @Override
+    public void verifyCanStopReferencingComponents(final ControllerServiceNode serviceNode) {
+        controllerServiceProvider.verifyCanStopReferencingComponents(serviceNode);
+    }
+    
+    @Override
     public ControllerService getControllerService(final String serviceIdentifier) {
         return controllerServiceProvider.getControllerService(serviceIdentifier);
     }
@@ -2623,10 +2762,24 @@ public class FlowController implements EventAccess, ControllerServiceProvider, H
     }
 
     @Override
+    public boolean isControllerServiceEnabling(final String serviceIdentifier) {
+        return controllerServiceProvider.isControllerServiceEnabling(serviceIdentifier);
+    }
+    
+    @Override
+    public String getControllerServiceName(final String serviceIdentifier) {
+    	return controllerServiceProvider.getControllerServiceName(serviceIdentifier);
+    }
+
     public void removeControllerService(final ControllerServiceNode serviceNode) {
         controllerServiceProvider.removeControllerService(serviceNode);
     }
     
+    @Override
+    public Set<ControllerServiceNode> getAllControllerServices() {
+    	return controllerServiceProvider.getAllControllerServices();
+    }
+    
     //
     // Counters
     //

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowFromDOMFactory.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowFromDOMFactory.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowFromDOMFactory.java
index c67181a..85ad159 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowFromDOMFactory.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowFromDOMFactory.java
@@ -26,6 +26,7 @@ import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.nifi.connectable.Size;
+import org.apache.nifi.controller.service.ControllerServiceState;
 import org.apache.nifi.encrypt.StringEncryptor;
 import org.apache.nifi.groups.RemoteProcessGroupPortDescriptor;
 import org.apache.nifi.remote.StandardRemoteProcessGroupPortDescriptor;
@@ -33,6 +34,7 @@ import org.apache.nifi.scheduling.SchedulingStrategy;
 import org.apache.nifi.util.DomUtils;
 import org.apache.nifi.web.api.dto.ConnectableDTO;
 import org.apache.nifi.web.api.dto.ConnectionDTO;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
 import org.apache.nifi.web.api.dto.FlowSnippetDTO;
 import org.apache.nifi.web.api.dto.FunnelDTO;
 import org.apache.nifi.web.api.dto.LabelDTO;
@@ -42,7 +44,7 @@ import org.apache.nifi.web.api.dto.ProcessGroupDTO;
 import org.apache.nifi.web.api.dto.ProcessorConfigDTO;
 import org.apache.nifi.web.api.dto.ProcessorDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO;
-
+import org.apache.nifi.web.api.dto.ReportingTaskDTO;
 import org.w3c.dom.Element;
 import org.w3c.dom.NodeList;
 
@@ -77,6 +79,40 @@ public class FlowFromDOMFactory {
 
         return styles;
     }
+    
+    public static ControllerServiceDTO getControllerService(final Element element, final StringEncryptor encryptor) {
+    	final ControllerServiceDTO dto = new ControllerServiceDTO();
+    	
+    	dto.setId(getString(element, "id"));
+    	dto.setName(getString(element, "name"));
+    	dto.setComments(getString(element, "comment"));
+    	dto.setType(getString(element, "class"));
+
+    	final boolean enabled = getBoolean(element, "enabled");
+    	dto.setState(enabled ? ControllerServiceState.ENABLED.name() : ControllerServiceState.DISABLED.name());
+    	
+        dto.setProperties(getProperties(element, encryptor));
+        dto.setAnnotationData(getString(element, "annotationData"));
+
+        return dto;
+    }
+    
+    public static ReportingTaskDTO getReportingTask(final Element element, final StringEncryptor encryptor) {
+    	final ReportingTaskDTO dto = new ReportingTaskDTO();
+    	
+    	dto.setId(getString(element, "id"));
+    	dto.setName(getString(element, "name"));
+    	dto.setComments(getString(element, "comment"));
+    	dto.setType(getString(element, "class"));
+    	dto.setSchedulingPeriod(getString(element, "schedulingPeriod"));
+    	dto.setState(getString(element, "scheduledState"));
+    	dto.setSchedulingStrategy(getString(element, "schedulingStrategy"));
+    	
+    	dto.setProperties(getProperties(element, encryptor));
+    	dto.setAnnotationData(getString(element, "annotationData"));
+
+    	return dto;
+    }
 
     public static ProcessGroupDTO getProcessGroup(final String parentId, final Element element, final StringEncryptor encryptor) {
         final ProcessGroupDTO dto = new ProcessGroupDTO();
@@ -310,7 +346,6 @@ public class FlowFromDOMFactory {
         final ProcessorConfigDTO configDto = new ProcessorConfigDTO();
         dto.setConfig(configDto);
         configDto.setComments(getString(element, "comment"));
-        configDto.setAnnotationData(getString(element, "annotationData"));
         configDto.setConcurrentlySchedulableTaskCount(getInt(element, "maxConcurrentTasks"));
         final String schedulingPeriod = getString(element, "schedulingPeriod");
         configDto.setSchedulingPeriod(schedulingPeriod);
@@ -334,14 +369,8 @@ public class FlowFromDOMFactory {
             configDto.setRunDurationMillis(TimeUnit.NANOSECONDS.toMillis(runDurationNanos));
         }
 
-        final LinkedHashMap<String, String> properties = new LinkedHashMap<>();
-        final List<Element> propertyNodeList = getChildrenByTagName(element, "property");
-        for (final Element propertyElement : propertyNodeList) {
-            final String name = getString(propertyElement, "name");
-            final String value = decrypt(getString(propertyElement, "value"), encryptor);
-            properties.put(name, value);
-        }
-        configDto.setProperties(properties);
+        configDto.setProperties(getProperties(element, encryptor));
+        configDto.setAnnotationData(getString(element, "annotationData"));
 
         final Set<String> autoTerminatedRelationships = new HashSet<>();
         final List<Element> autoTerminateList = getChildrenByTagName(element, "autoTerminatedRelationship");
@@ -353,6 +382,17 @@ public class FlowFromDOMFactory {
         return dto;
     }
 
+    private static LinkedHashMap<String, String> getProperties(final Element element, final StringEncryptor encryptor) {
+    	final LinkedHashMap<String, String> properties = new LinkedHashMap<>();
+        final List<Element> propertyNodeList = getChildrenByTagName(element, "property");
+        for (final Element propertyElement : propertyNodeList) {
+            final String name = getString(propertyElement, "name");
+            final String value = decrypt(getString(propertyElement, "value"), encryptor);
+            properties.put(name, value);
+        }
+        return properties;
+    }
+    
     private static String getString(final Element element, final String childElementName) {
         final List<Element> nodeList = getChildrenByTagName(element, childElementName);
         if (nodeList == null || nodeList.isEmpty()) {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSerializer.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSerializer.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSerializer.java
index e08a94d..7cd9d3b 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSerializer.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSerializer.java
@@ -40,6 +40,8 @@ import org.apache.nifi.connectable.Port;
 import org.apache.nifi.connectable.Position;
 import org.apache.nifi.connectable.Size;
 import org.apache.nifi.controller.label.Label;
+import org.apache.nifi.controller.service.ControllerServiceNode;
+import org.apache.nifi.controller.service.ControllerServiceState;
 import org.apache.nifi.encrypt.StringEncryptor;
 import org.apache.nifi.flowfile.FlowFilePrioritizer;
 import org.apache.nifi.groups.ProcessGroup;
@@ -47,7 +49,6 @@ import org.apache.nifi.groups.RemoteProcessGroup;
 import org.apache.nifi.processor.Relationship;
 import org.apache.nifi.remote.RemoteGroupPort;
 import org.apache.nifi.remote.RootGroupPort;
-
 import org.w3c.dom.DOMException;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -79,6 +80,18 @@ public class StandardFlowSerializer implements FlowSerializer {
             addTextElement(rootNode, "maxTimerDrivenThreadCount", controller.getMaxTimerDrivenThreadCount());
             addTextElement(rootNode, "maxEventDrivenThreadCount", controller.getMaxEventDrivenThreadCount());
             addProcessGroup(rootNode, controller.getGroup(controller.getRootGroupId()), "rootGroup");
+            
+            final Element controllerServicesNode = doc.createElement("controllerServices");
+            rootNode.appendChild(controllerServicesNode);
+            for ( final ControllerServiceNode serviceNode : controller.getAllControllerServices() ) {
+            	addControllerService(controllerServicesNode, serviceNode, encryptor);
+            }
+            
+            final Element reportingTasksNode = doc.createElement("reportingTasks");
+            rootNode.appendChild(reportingTasksNode);
+            for ( final ReportingTaskNode taskNode : controller.getAllReportingTasks() ) {
+            	addReportingTask(reportingTasksNode, taskNode, encryptor);
+            }
 
             final DOMSource domSource = new DOMSource(doc);
             final StreamResult streamResult = new StreamResult(new BufferedOutputStream(os));
@@ -300,8 +313,16 @@ public class StandardFlowSerializer implements FlowSerializer {
         addTextElement(element, "schedulingStrategy", processor.getSchedulingStrategy().name());
         addTextElement(element, "runDurationNanos", processor.getRunDuration(TimeUnit.NANOSECONDS));
 
-        // properties.
-        for (final Map.Entry<PropertyDescriptor, String> entry : processor.getProperties().entrySet()) {
+        addConfiguration(element, processor.getProperties(), processor.getAnnotationData(), encryptor);
+        
+        for (final Relationship rel : processor.getAutoTerminatedRelationships()) {
+            addTextElement(element, "autoTerminatedRelationship", rel.getName());
+        }
+    }
+    
+    private static void addConfiguration(final Element element, final Map<PropertyDescriptor, String> properties, final String annotationData, final StringEncryptor encryptor) {
+    	final Document doc = element.getOwnerDocument();
+    	for (final Map.Entry<PropertyDescriptor, String> entry : properties.entrySet()) {
             final PropertyDescriptor descriptor = entry.getKey();
             String value = entry.getValue();
 
@@ -322,14 +343,9 @@ public class StandardFlowSerializer implements FlowSerializer {
             element.appendChild(propElement);
         }
 
-        final String annotationData = processor.getAnnotationData();
         if (annotationData != null) {
             addTextElement(element, "annotationData", annotationData);
         }
-
-        for (final Relationship rel : processor.getAutoTerminatedRelationships()) {
-            addTextElement(element, "autoTerminatedRelationship", rel.getName());
-        }
     }
 
     private void addConnection(final Element parentElement, final Connection connection) {
@@ -390,11 +406,43 @@ public class StandardFlowSerializer implements FlowSerializer {
         parentElement.appendChild(element);
     }
 
-    private void addTextElement(final Element element, final String name, final long value) {
+    
+    public static void addControllerService(final Element element, final ControllerServiceNode serviceNode, final StringEncryptor encryptor) {
+    	final Element serviceElement = element.getOwnerDocument().createElement("controllerService");
+    	addTextElement(serviceElement, "id", serviceNode.getIdentifier());
+    	addTextElement(serviceElement, "name", serviceNode.getName());
+    	addTextElement(serviceElement, "comment", serviceNode.getComments());
+    	addTextElement(serviceElement, "class", serviceNode.getControllerServiceImplementation().getClass().getCanonicalName());
+    	
+    	final ControllerServiceState state = serviceNode.getState();
+    	final boolean enabled = (state == ControllerServiceState.ENABLED || state == ControllerServiceState.ENABLING);
+        addTextElement(serviceElement, "enabled", String.valueOf(enabled));
+        
+        addConfiguration(serviceElement, serviceNode.getProperties(), serviceNode.getAnnotationData(), encryptor);
+        
+    	element.appendChild(serviceElement);
+    }
+    
+    public static void addReportingTask(final Element element, final ReportingTaskNode taskNode, final StringEncryptor encryptor) {
+    	final Element taskElement = element.getOwnerDocument().createElement("reportingTask");
+    	addTextElement(taskElement, "id", taskNode.getIdentifier());
+    	addTextElement(taskElement, "name", taskNode.getName());
+    	addTextElement(taskElement, "comment", taskNode.getComments());
+    	addTextElement(taskElement, "class", taskNode.getReportingTask().getClass().getCanonicalName());
+        addTextElement(taskElement, "schedulingPeriod", taskNode.getSchedulingPeriod());
+        addTextElement(taskElement, "scheduledState", taskNode.getScheduledState().name());
+        addTextElement(taskElement, "schedulingStrategy", taskNode.getSchedulingStrategy().name());
+    	
+    	addConfiguration(taskElement, taskNode.getProperties(), taskNode.getAnnotationData(), encryptor);
+    	
+    	element.appendChild(taskElement);
+    }
+    
+    private static void addTextElement(final Element element, final String name, final long value) {
         addTextElement(element, name, String.valueOf(value));
     }
 
-    private void addTextElement(final Element element, final String name, final String value) {
+    private static void addTextElement(final Element element, final String name, final String value) {
         final Document doc = element.getOwnerDocument();
         final Element toAdd = doc.createElement(name);
         toAdd.setTextContent(value);

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowService.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowService.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowService.java
index 64ce5c4..fcfee83 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowService.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowService.java
@@ -81,8 +81,6 @@ public class StandardFlowService implements FlowService, ProtocolHandler {
 
     private final FlowController controller;
     private final Path flowXml;
-    private final Path taskConfigXml;
-    private final Path serviceConfigXml;
     private final FlowConfigurationDAO dao;
     private final int gracefulShutdownSeconds;
     private final boolean autoResumeState;
@@ -154,14 +152,12 @@ public class StandardFlowService implements FlowService, ProtocolHandler {
         this.controller = controller;
         this.encryptor = encryptor;
         flowXml = Paths.get(properties.getProperty(NiFiProperties.FLOW_CONFIGURATION_FILE));
-        taskConfigXml = Paths.get(properties.getProperty(NiFiProperties.TASK_CONFIGURATION_FILE));
-        serviceConfigXml = Paths.get(properties.getProperty(NiFiProperties.SERVICE_CONFIGURATION_FILE));
 
         gracefulShutdownSeconds = (int) FormatUtils.getTimeDuration(properties.getProperty(NiFiProperties.FLOW_CONTROLLER_GRACEFUL_SHUTDOWN_PERIOD), TimeUnit.SECONDS);
         autoResumeState = properties.getAutoResumeState();
         connectionRetryMillis = (int) FormatUtils.getTimeDuration(properties.getClusterManagerFlowRetrievalDelay(), TimeUnit.MILLISECONDS);
 
-        dao = new StandardXMLFlowConfigurationDAO(flowXml, taskConfigXml, serviceConfigXml, encryptor);
+        dao = new StandardXMLFlowConfigurationDAO(flowXml, encryptor);
 
         if (configuredForClustering) {
 
@@ -605,7 +601,6 @@ public class StandardFlowService implements FlowService, ProtocolHandler {
         if (firstControllerInitialization) {
             // load the controller services
             logger.debug("Loading controller services");
-            dao.loadControllerServices(controller);
         }
 
         // load the flow
@@ -622,7 +617,7 @@ public class StandardFlowService implements FlowService, ProtocolHandler {
             logger.debug("First controller initialization. Loading reporting tasks and initializing controller.");
 
             // load the controller tasks
-            dao.loadReportingTasks(controller);
+//            dao.loadReportingTasks(controller);
 
             // initialize the flow
             controller.initializeFlow();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSynchronizer.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSynchronizer.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSynchronizer.java
index 05a8f01..201482c 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSynchronizer.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSynchronizer.java
@@ -26,6 +26,7 @@ import java.nio.file.Path;
 import java.nio.file.StandardOpenOption;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -40,6 +41,7 @@ import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.validation.Schema;
 import javax.xml.validation.SchemaFactory;
 
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.nifi.cluster.protocol.DataFlow;
 import org.apache.nifi.cluster.protocol.StandardDataFlow;
 import org.apache.nifi.connectable.Connectable;
@@ -51,23 +53,35 @@ import org.apache.nifi.connectable.Position;
 import org.apache.nifi.connectable.Size;
 import org.apache.nifi.controller.exception.ProcessorInstantiationException;
 import org.apache.nifi.controller.label.Label;
+import org.apache.nifi.controller.reporting.ReportingTaskInstantiationException;
+import org.apache.nifi.controller.reporting.StandardReportingInitializationContext;
+import org.apache.nifi.controller.service.ControllerServiceLoader;
+import org.apache.nifi.controller.service.ControllerServiceNode;
+import org.apache.nifi.controller.service.ControllerServiceState;
+import org.apache.nifi.encrypt.StringEncryptor;
 import org.apache.nifi.events.BulletinFactory;
-import org.apache.nifi.util.file.FileUtils;
 import org.apache.nifi.fingerprint.FingerprintException;
 import org.apache.nifi.fingerprint.FingerprintFactory;
 import org.apache.nifi.flowfile.FlowFilePrioritizer;
 import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.groups.RemoteProcessGroup;
 import org.apache.nifi.groups.RemoteProcessGroupPortDescriptor;
+import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.logging.LogLevel;
 import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.SimpleProcessLogger;
 import org.apache.nifi.remote.RemoteGroupPort;
 import org.apache.nifi.remote.RootGroupPort;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.reporting.ReportingInitializationContext;
 import org.apache.nifi.reporting.Severity;
 import org.apache.nifi.scheduling.SchedulingStrategy;
+import org.apache.nifi.util.DomUtils;
 import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.util.file.FileUtils;
 import org.apache.nifi.web.api.dto.ConnectableDTO;
 import org.apache.nifi.web.api.dto.ConnectionDTO;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
 import org.apache.nifi.web.api.dto.FlowSnippetDTO;
 import org.apache.nifi.web.api.dto.FunnelDTO;
 import org.apache.nifi.web.api.dto.LabelDTO;
@@ -77,9 +91,7 @@ import org.apache.nifi.web.api.dto.ProcessGroupDTO;
 import org.apache.nifi.web.api.dto.ProcessorConfigDTO;
 import org.apache.nifi.web.api.dto.ProcessorDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO;
-
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.nifi.encrypt.StringEncryptor;
+import org.apache.nifi.web.api.dto.ReportingTaskDTO;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
@@ -96,9 +108,11 @@ public class StandardFlowSynchronizer implements FlowSynchronizer {
     private static final Logger logger = LoggerFactory.getLogger(StandardFlowSynchronizer.class);
     public static final URL FLOW_XSD_RESOURCE = StandardFlowSynchronizer.class.getResource("/FlowConfiguration.xsd");
     private final StringEncryptor encryptor;
+    private final boolean autoResumeState;
 
     public StandardFlowSynchronizer(final StringEncryptor encryptor) {
         this.encryptor = encryptor;
+        autoResumeState = NiFiProperties.getInstance().getAutoResumeState();
     }
 
     public static boolean isEmpty(final DataFlow dataFlow, final StringEncryptor encryptor) {
@@ -157,10 +171,26 @@ public class StandardFlowSynchronizer implements FlowSynchronizer {
                         controller.setMaxEventDrivenThreadCount(maxThreadCount / 3);
                     }
 
+                    final Element reportingTasksElement = (Element) DomUtils.getChild(rootElement, "reportingTasks");
+                    final List<Element> taskElements;
+                    if ( reportingTasksElement == null ) {
+                        taskElements = Collections.emptyList();
+                    } else {
+                        taskElements = DomUtils.getChildElementsByTagName(reportingTasksElement, "reportingTask");
+                    }
+                    
+                    final Element controllerServicesElement = (Element) DomUtils.getChild(rootElement, "controllerServices");
+                    final List<Element> controllerServiceElements;
+                    if ( controllerServicesElement == null ) {
+                        controllerServiceElements = Collections.emptyList();
+                    } else {
+                        controllerServiceElements = DomUtils.getChildElementsByTagName(controllerServicesElement, "controllerService");
+                    }
+                    
                     logger.trace("Parsing process group from DOM");
                     final Element rootGroupElement = (Element) rootElement.getElementsByTagName("rootGroup").item(0);
                     final ProcessGroupDTO rootGroupDto = FlowFromDOMFactory.getProcessGroup(null, rootGroupElement, encryptor);
-                    existingFlowEmpty = isEmpty(rootGroupDto);
+                    existingFlowEmpty = taskElements.isEmpty() && controllerServiceElements.isEmpty() && isEmpty(rootGroupDto);
                     logger.debug("Existing Flow Empty = {}", existingFlowEmpty);
                 }
             }
@@ -200,37 +230,64 @@ public class StandardFlowSynchronizer implements FlowSynchronizer {
         // create document by parsing proposed flow bytes
         logger.trace("Parsing proposed flow bytes as DOM document");
         final Document configuration = parseFlowBytes(proposedFlow.getFlow());
-
+        
         // attempt to sync controller with proposed flow
         try {
             if (configuration != null) {
-                // get the root element
-                final Element rootElement = (Element) configuration.getElementsByTagName("flowController").item(0);
-
-                // set controller config
-                logger.trace("Updating flow config");
-                final Integer maxThreadCount = getInteger(rootElement, "maxThreadCount");
-                if (maxThreadCount == null) {
-                    controller.setMaxTimerDrivenThreadCount(getInt(rootElement, "maxTimerDrivenThreadCount"));
-                    controller.setMaxEventDrivenThreadCount(getInt(rootElement, "maxEventDrivenThreadCount"));
-                } else {
-                    controller.setMaxTimerDrivenThreadCount(maxThreadCount * 2 / 3);
-                    controller.setMaxEventDrivenThreadCount(maxThreadCount / 3);
-                }
-
-                // get the root group XML element
-                final Element rootGroupElement = (Element) rootElement.getElementsByTagName("rootGroup").item(0);
-
-                // if this controller isn't initialized or its emtpy, add the root group, otherwise update
-                if (!initialized || existingFlowEmpty) {
-                    logger.trace("Adding root process group");
-                    addProcessGroup(controller, /* parent group */ null, rootGroupElement, encryptor);
-                } else {
-                    logger.trace("Updating root process group");
-                    updateProcessGroup(controller, /* parent group */ null, rootGroupElement, encryptor);
+                synchronized (configuration) {
+                    // get the root element
+                    final Element rootElement = (Element) configuration.getElementsByTagName("flowController").item(0);
+    
+                    // set controller config
+                    logger.trace("Updating flow config");
+                    final Integer maxThreadCount = getInteger(rootElement, "maxThreadCount");
+                    if (maxThreadCount == null) {
+                        controller.setMaxTimerDrivenThreadCount(getInt(rootElement, "maxTimerDrivenThreadCount"));
+                        controller.setMaxEventDrivenThreadCount(getInt(rootElement, "maxEventDrivenThreadCount"));
+                    } else {
+                        controller.setMaxTimerDrivenThreadCount(maxThreadCount * 2 / 3);
+                        controller.setMaxEventDrivenThreadCount(maxThreadCount / 3);
+                    }
+    
+                    // get the root group XML element
+                    final Element rootGroupElement = (Element) rootElement.getElementsByTagName("rootGroup").item(0);
+    
+                    final Element controllerServicesElement = (Element) DomUtils.getChild(rootElement, "controllerServices");
+    	            if ( controllerServicesElement != null ) {
+    	                final List<Element> serviceElements = DomUtils.getChildElementsByTagName(controllerServicesElement, "controllerService");
+    	                
+    	                if ( !initialized || existingFlowEmpty ) {
+    	                    ControllerServiceLoader.loadControllerServices(serviceElements, controller, encryptor, controller.getBulletinRepository(), autoResumeState);
+    	                } else {
+    	                    for ( final Element serviceElement : serviceElements ) {
+    	                        updateControllerService(controller, serviceElement, encryptor);
+    	                    }
+    	                }
+                    }
+    
+                    // if this controller isn't initialized or its emtpy, add the root group, otherwise update
+                    if (!initialized || existingFlowEmpty) {
+                        logger.trace("Adding root process group");
+                        addProcessGroup(controller, /* parent group */ null, rootGroupElement, encryptor);
+                    } else {
+                        logger.trace("Updating root process group");
+                        updateProcessGroup(controller, /* parent group */ null, rootGroupElement, encryptor);
+                    }
+    
+                    final Element reportingTasksElement = (Element) DomUtils.getChild(rootElement, "reportingTasks");
+                    if ( reportingTasksElement != null ) {
+                    	final List<Element> taskElements = DomUtils.getChildElementsByTagName(reportingTasksElement, "reportingTask");
+                    	for ( final Element taskElement : taskElements ) {
+                    		if ( !initialized || existingFlowEmpty ) {
+                    			addReportingTask(controller, taskElement, encryptor);
+                    		} else {
+                    			updateReportingTask(controller, taskElement, encryptor);
+                    		}
+                    	}
+                    }
                 }
             }
-
+    
             logger.trace("Synching templates");
             if ((existingTemplates == null || existingTemplates.length == 0) && proposedFlow.getTemplates() != null && proposedFlow.getTemplates().length > 0) {
                 // need to load templates
@@ -313,7 +370,124 @@ public class StandardFlowSynchronizer implements FlowSynchronizer {
 
         return baos.toByteArray();
     }
+    
+    
+    private void updateControllerService(final FlowController controller, final Element controllerServiceElement, final StringEncryptor encryptor) {
+    	final ControllerServiceDTO dto = FlowFromDOMFactory.getControllerService(controllerServiceElement, encryptor);
+    	
+    	final ControllerServiceState dtoState = ControllerServiceState.valueOf(dto.getState());
+        final boolean dtoEnabled = (dtoState == ControllerServiceState.ENABLED || dtoState == ControllerServiceState.ENABLING);
+        
+        final ControllerServiceNode serviceNode = controller.getControllerServiceNode(dto.getId());
+        final ControllerServiceState serviceState = serviceNode.getState();
+        final boolean serviceEnabled = (serviceState == ControllerServiceState.ENABLED || serviceState == ControllerServiceState.ENABLING);
+        
+    	if (dtoEnabled && !serviceEnabled) {
+    		controller.enableControllerService(controller.getControllerServiceNode(dto.getId()));
+    	} else if (!dtoEnabled && serviceEnabled) {
+    		controller.disableControllerService(controller.getControllerServiceNode(dto.getId()));
+    	}
+    }
+    
+    private void addReportingTask(final FlowController controller, final Element reportingTaskElement, final StringEncryptor encryptor) throws ReportingTaskInstantiationException {
+    	final ReportingTaskDTO dto = FlowFromDOMFactory.getReportingTask(reportingTaskElement, encryptor);
+    	
+    	final ReportingTaskNode reportingTask = controller.createReportingTask(dto.getType(), dto.getId(), false);
+    	reportingTask.setName(dto.getName());
+    	reportingTask.setComments(dto.getComments());
+    	reportingTask.setScheduldingPeriod(dto.getSchedulingPeriod());
+    	reportingTask.setSchedulingStrategy(SchedulingStrategy.valueOf(dto.getSchedulingStrategy()));
+    	
+    	reportingTask.setAnnotationData(dto.getAnnotationData());
+    	
+        for (final Map.Entry<String, String> entry : dto.getProperties().entrySet()) {
+            if (entry.getValue() == null) {
+            	reportingTask.removeProperty(entry.getKey());
+            } else {
+            	reportingTask.setProperty(entry.getKey(), entry.getValue());
+            }
+        }
+        
+        final ComponentLog componentLog = new SimpleProcessLogger(dto.getId(), reportingTask.getReportingTask());
+        final ReportingInitializationContext config = new StandardReportingInitializationContext(dto.getId(), dto.getName(),
+                SchedulingStrategy.valueOf(dto.getSchedulingStrategy()), dto.getSchedulingPeriod(), componentLog, controller);
+        
+        try {
+            reportingTask.getReportingTask().initialize(config);
+        } catch (final InitializationException ie) {
+            throw new ReportingTaskInstantiationException("Failed to initialize reporting task of type " + dto.getType(), ie);
+        }
+        
+        if ( autoResumeState ) {
+	        if ( ScheduledState.RUNNING.name().equals(dto.getState()) ) {
+	        	try {
+	        		controller.startReportingTask(reportingTask);
+	        	} catch (final Exception e) {
+	        		logger.error("Failed to start {} due to {}", reportingTask, e);
+	        		if ( logger.isDebugEnabled() ) {
+	        			logger.error("", e);
+	        		}
+	        		controller.getBulletinRepository().addBulletin(BulletinFactory.createBulletin(
+	        				"Reporting Tasks", Severity.ERROR.name(), "Failed to start " + reportingTask + " due to " + e));
+	        	}
+	        } else if ( ScheduledState.DISABLED.name().equals(dto.getState()) ) {
+	        	try {
+	        		controller.disableReportingTask(reportingTask);
+	        	} catch (final Exception e) {
+	        		logger.error("Failed to mark {} as disabled due to {}", reportingTask, e);
+	        		if ( logger.isDebugEnabled() ) {
+	        			logger.error("", e);
+	        		}
+	        		controller.getBulletinRepository().addBulletin(BulletinFactory.createBulletin(
+	        				"Reporting Tasks", Severity.ERROR.name(), "Failed to mark " + reportingTask + " as disabled due to " + e));
+	        	}
+	        }
+        }
+    }
 
+    private void updateReportingTask(final FlowController controller, final Element reportingTaskElement, final StringEncryptor encryptor) {
+    	final ReportingTaskDTO dto = FlowFromDOMFactory.getReportingTask(reportingTaskElement, encryptor);
+    	final ReportingTaskNode taskNode = controller.getReportingTaskNode(dto.getId());
+    	
+        if (!taskNode.getScheduledState().name().equals(dto.getState())) {
+            try {
+                switch (ScheduledState.valueOf(dto.getState())) {
+                    case DISABLED:
+                    	if ( taskNode.isRunning() ) {
+                    		controller.stopReportingTask(taskNode);
+                    	}
+                    	controller.disableReportingTask(taskNode);
+                        break;
+                    case RUNNING:
+                    	if ( taskNode.getScheduledState() == ScheduledState.DISABLED ) {
+                    		controller.enableReportingTask(taskNode);
+                    	}
+                    	controller.startReportingTask(taskNode);
+                        break;
+                    case STOPPED:
+                        if (taskNode.getScheduledState() == ScheduledState.DISABLED) {
+                        	controller.enableReportingTask(taskNode);
+                        } else if (taskNode.getScheduledState() == ScheduledState.RUNNING) {
+                        	controller.stopReportingTask(taskNode);
+                        }
+                        break;
+                }
+            } catch (final IllegalStateException ise) {
+                logger.error("Failed to change Scheduled State of {} from {} to {} due to {}", taskNode, taskNode.getScheduledState().name(), dto.getState(), ise.toString());
+                logger.error("", ise);
+
+                // create bulletin for the Processor Node
+                controller.getBulletinRepository().addBulletin(BulletinFactory.createBulletin("Node Reconnection", Severity.ERROR.name(),
+                        "Failed to change Scheduled State of " + taskNode + " from " + taskNode.getScheduledState().name() + " to " + dto.getState() + " due to " + ise.toString()));
+
+                // create bulletin at Controller level.
+                controller.getBulletinRepository().addBulletin(BulletinFactory.createBulletin("Node Reconnection", Severity.ERROR.name(),
+                        "Failed to change Scheduled State of " + taskNode + " from " + taskNode.getScheduledState().name() + " to " + dto.getState() + " due to " + ise.toString()));
+            }
+        }
+    }
+    
+    
     private ProcessGroup updateProcessGroup(final FlowController controller, final ProcessGroup parentGroup, final Element processGroupElement, final StringEncryptor encryptor) throws ProcessorInstantiationException {
 
         // get the parent group ID

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardProcessorNode.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardProcessorNode.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardProcessorNode.java
index fe72ae4..355e303 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardProcessorNode.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardProcessorNode.java
@@ -52,6 +52,7 @@ import org.apache.nifi.connectable.Connectable;
 import org.apache.nifi.connectable.ConnectableType;
 import org.apache.nifi.connectable.Connection;
 import org.apache.nifi.connectable.Position;
+import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.controller.service.ControllerServiceProvider;
 import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.logging.LogLevel;
@@ -120,7 +121,7 @@ public class StandardProcessorNode extends ProcessorNode implements Connectable
     private SchedulingStrategy schedulingStrategy;  // guarded by read/write lock
 
     @SuppressWarnings("deprecation")
-    StandardProcessorNode(final Processor processor, final String uuid, final ValidationContextFactory validationContextFactory,
+    public StandardProcessorNode(final Processor processor, final String uuid, final ValidationContextFactory validationContextFactory,
             final ProcessScheduler scheduler, final ControllerServiceProvider controllerServiceProvider) {
         super(processor, uuid, validationContextFactory, controllerServiceProvider);
 
@@ -985,6 +986,16 @@ public class StandardProcessorNode extends ProcessorNode implements Connectable
             readLock.unlock();
         }
     }
+    
+    @Override
+    public int getActiveThreadCount() {
+        readLock.lock();
+        try {
+            return processScheduler.getActiveThreadCount(this);
+        } finally {
+            readLock.unlock();
+        }
+    }
 
     @Override
     public boolean isValid() {
@@ -1182,8 +1193,13 @@ public class StandardProcessorNode extends ProcessorNode implements Connectable
     public void verifyCanStart() {
         readLock.lock();
         try {
-            if (scheduledState.get() != ScheduledState.STOPPED) {
-                throw new IllegalStateException(this + " is not stopped");
+            switch (getScheduledState()) {
+                case DISABLED:
+                    throw new IllegalStateException(this + " cannot be started because it is disabled");
+                case RUNNING:
+                    throw new IllegalStateException(this + " cannot be started because it is already running");
+                case STOPPED:
+                    break;
             }
             verifyNoActiveThreads();
 
@@ -1194,6 +1210,31 @@ public class StandardProcessorNode extends ProcessorNode implements Connectable
             readLock.unlock();
         }
     }
+    
+    @Override
+    public void verifyCanStart(final Set<ControllerServiceNode> ignoredReferences) {
+        switch (getScheduledState()) {
+            case DISABLED:
+                throw new IllegalStateException(this + " cannot be started because it is disabled");
+            case RUNNING:
+                throw new IllegalStateException(this + " cannot be started because it is already running");
+            case STOPPED:
+                break;
+        }
+        verifyNoActiveThreads();
+        
+        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 + " cannot be started because it is not valid: " + result);
+            }
+        }
+    }
 
     @Override
     public void verifyCanStop() {


[21/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/WebClusterManager.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/WebClusterManager.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/WebClusterManager.java
index 4d5455f..eff523a 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/WebClusterManager.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/impl/WebClusterManager.java
@@ -16,12 +16,12 @@
  */
 package org.apache.nifi.cluster.manager.impl;
 
-import java.io.File;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.Serializable;
 import java.net.URI;
-import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -41,7 +41,9 @@ import java.util.TimerTask;
 import java.util.TreeMap;
 import java.util.UUID;
 import java.util.concurrent.CompletionService;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ExecutorCompletionService;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -54,17 +56,20 @@ import javax.net.ssl.SSLContext;
 import javax.ws.rs.HttpMethod;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.StreamingOutput;
-import javax.xml.XMLConstants;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.dom.DOMSource;
-import javax.xml.validation.Schema;
-import javax.xml.validation.SchemaFactory;
-import javax.xml.validation.Validator;
+import javax.xml.transform.stream.StreamResult;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.admin.service.AuditService;
+import org.apache.nifi.annotation.lifecycle.OnAdded;
+import org.apache.nifi.annotation.lifecycle.OnRemoved;
 import org.apache.nifi.cluster.BulletinsPayload;
 import org.apache.nifi.cluster.HeartbeatPayload;
 import org.apache.nifi.cluster.context.ClusterContext;
@@ -122,12 +127,18 @@ import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.controller.ControllerService;
 import org.apache.nifi.controller.Heartbeater;
 import org.apache.nifi.controller.ReportingTaskNode;
+import org.apache.nifi.controller.ScheduledState;
+import org.apache.nifi.controller.StandardFlowSerializer;
 import org.apache.nifi.controller.ValidationContextFactory;
+import org.apache.nifi.controller.exception.ProcessorLifeCycleException;
 import org.apache.nifi.controller.reporting.ClusteredReportingTaskNode;
 import org.apache.nifi.controller.reporting.ReportingTaskInstantiationException;
+import org.apache.nifi.controller.reporting.ReportingTaskProvider;
 import org.apache.nifi.controller.reporting.StandardReportingInitializationContext;
+import org.apache.nifi.controller.scheduling.QuartzSchedulingAgent;
 import org.apache.nifi.controller.scheduling.StandardProcessScheduler;
 import org.apache.nifi.controller.scheduling.TimerDrivenSchedulingAgent;
+import org.apache.nifi.controller.service.ControllerServiceLoader;
 import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.controller.service.ControllerServiceProvider;
 import org.apache.nifi.controller.service.StandardControllerServiceProvider;
@@ -147,10 +158,12 @@ import org.apache.nifi.events.BulletinFactory;
 import org.apache.nifi.events.VolatileBulletinRepository;
 import org.apache.nifi.framework.security.util.SslContextFactory;
 import org.apache.nifi.io.socket.multicast.DiscoverableService;
+import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.logging.NiFiLog;
 import org.apache.nifi.nar.ExtensionManager;
 import org.apache.nifi.nar.NarCloseable;
 import org.apache.nifi.nar.NarThreadContextClassLoader;
+import org.apache.nifi.processor.SimpleProcessLogger;
 import org.apache.nifi.processor.StandardValidationContextFactory;
 import org.apache.nifi.remote.RemoteResourceManager;
 import org.apache.nifi.remote.RemoteSiteListener;
@@ -168,7 +181,11 @@ import org.apache.nifi.scheduling.SchedulingStrategy;
 import org.apache.nifi.util.DomUtils;
 import org.apache.nifi.util.FormatUtils;
 import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.util.ObjectHolder;
+import org.apache.nifi.util.ReflectionUtils;
+import org.apache.nifi.web.OptimisticLockingManager;
 import org.apache.nifi.web.Revision;
+import org.apache.nifi.web.UpdateRevision;
 import org.apache.nifi.web.api.dto.FlowSnippetDTO;
 import org.apache.nifi.web.api.dto.NodeDTO;
 import org.apache.nifi.web.api.dto.ProcessGroupDTO;
@@ -204,6 +221,16 @@ import org.xml.sax.SAXParseException;
 
 import com.sun.jersey.api.client.ClientResponse;
 
+import org.apache.nifi.controller.service.ControllerServiceState;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
+import org.apache.nifi.web.api.dto.ControllerServiceReferencingComponentDTO;
+import org.apache.nifi.web.api.dto.ReportingTaskDTO;
+import org.apache.nifi.web.api.entity.ControllerServiceEntity;
+import org.apache.nifi.web.api.entity.ControllerServiceReferencingComponentsEntity;
+import org.apache.nifi.web.api.entity.ControllerServicesEntity;
+import org.apache.nifi.web.api.entity.ReportingTaskEntity;
+import org.apache.nifi.web.api.entity.ReportingTasksEntity;
+
 /**
  * Provides a cluster manager implementation. The manager federates incoming
  * HTTP client requests to the nodes' external API using the HTTP protocol. The
@@ -222,7 +249,7 @@ import com.sun.jersey.api.client.ClientResponse;
  *
  * @author unattributed
  */
-public class WebClusterManager implements HttpClusterManager, ProtocolHandler, ControllerServiceProvider {
+public class WebClusterManager implements HttpClusterManager, ProtocolHandler, ControllerServiceProvider, ReportingTaskProvider {
 
     public static final String ROOT_GROUP_ID_ALIAS = "root";
     public static final String BULLETIN_CATEGORY = "Clustering";
@@ -279,6 +306,7 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
 
     public static final Pattern PROCESSORS_URI_PATTERN = Pattern.compile("/nifi-api/controller/process-groups/(?:(?:root)|(?:[a-f0-9\\-]{36}))/processors");
     public static final Pattern PROCESSOR_URI_PATTERN = Pattern.compile("/nifi-api/controller/process-groups/(?:(?:root)|(?:[a-f0-9\\-]{36}))/processors/[a-f0-9\\-]{36}");
+    public static final Pattern CLUSTER_PROCESSOR_URI_PATTERN = Pattern.compile("/nifi-api/cluster/processors/[a-f0-9\\-]{36}");
 
     public static final Pattern REMOTE_PROCESS_GROUPS_URI_PATTERN = Pattern.compile("/nifi-api/controller/process-groups/(?:(?:root)|(?:[a-f0-9\\-]{36}))/remote-process-groups");
     public static final Pattern REMOTE_PROCESS_GROUP_URI_PATTERN = Pattern.compile("/nifi-api/controller/process-groups/(?:(?:root)|(?:[a-f0-9\\-]{36}))/remote-process-groups/[a-f0-9\\-]{36}");
@@ -290,12 +318,19 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
     public static final String PROVENANCE_URI = "/nifi-api/controller/provenance";
     public static final Pattern PROVENANCE_QUERY_URI = Pattern.compile("/nifi-api/controller/provenance/[a-f0-9\\-]{36}");
     public static final Pattern PROVENANCE_EVENT_URI = Pattern.compile("/nifi-api/controller/provenance/events/[0-9]+");
-
+    
+    public static final String CONTROLLER_SERVICES_URI = "/nifi-api/controller/controller-services/node";
+    public static final Pattern CONTROLLER_SERVICE_URI_PATTERN = Pattern.compile("/nifi-api/controller/controller-services/node/[a-f0-9\\-]{36}");
+    public static final Pattern CONTROLLER_SERVICE_REFERENCES_URI_PATTERN = Pattern.compile("/nifi-api/controller/controller-services/node/[a-f0-9\\-]{36}/references");
+    public static final String REPORTING_TASKS_URI = "/nifi-api/controller/reporting-tasks/node";
+    public static final Pattern REPORTING_TASK_URI_PATTERN = Pattern.compile("/nifi-api/controller/reporting-tasks/node/[a-f0-9\\-]{36}");
+    
     private final NiFiProperties properties;
     private final HttpRequestReplicator httpRequestReplicator;
     private final HttpResponseMapper httpResponseMapper;
     private final DataFlowManagementService dataFlowManagementService;
     private final ClusterManagerProtocolSenderListener senderListener;
+    private final OptimisticLockingManager optimisticLockingManager;
     private final StringEncryptor encryptor;
     private final Queue<Heartbeat> pendingHeartbeats = new ConcurrentLinkedQueue<>();
     private final ReentrantReadWriteLock resourceRWLock = new ReentrantReadWriteLock();
@@ -303,12 +338,11 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
     private final ClusterManagerLock writeLock = new ClusterManagerLock(resourceRWLock.writeLock(), "Write");
 
     private final Set<Node> nodes = new HashSet<>();
-    private final Set<ReportingTaskNode> reportingTasks = new HashSet<>();
+    private final ConcurrentMap<String, ReportingTaskNode> reportingTasks = new ConcurrentHashMap<>();
 
     // null means the dataflow should be read from disk
     private StandardDataFlow cachedDataFlow = null;
     private NodeIdentifier primaryNodeId = null;
-    private Revision revision = new Revision(0L, "");
     private Timer heartbeatMonitor;
     private Timer heartbeatProcessor;
     private volatile ClusterServicesBroadcaster servicesBroadcaster = null;
@@ -329,7 +363,7 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
 
     public WebClusterManager(final HttpRequestReplicator httpRequestReplicator, final HttpResponseMapper httpResponseMapper,
             final DataFlowManagementService dataFlowManagementService, final ClusterManagerProtocolSenderListener senderListener,
-            final NiFiProperties properties, final StringEncryptor encryptor) {
+            final NiFiProperties properties, final StringEncryptor encryptor, final OptimisticLockingManager optimisticLockingManager) {
 
         if (httpRequestReplicator == null) {
             throw new IllegalArgumentException("HttpRequestReplicator may not be null.");
@@ -348,11 +382,11 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
         this.httpResponseMapper = httpResponseMapper;
         this.dataFlowManagementService = dataFlowManagementService;
         this.properties = properties;
-        this.controllerServiceProvider = new StandardControllerServiceProvider();
         this.bulletinRepository = new VolatileBulletinRepository();
         this.instanceId = UUID.randomUUID().toString();
         this.senderListener = senderListener;
         this.encryptor = encryptor;
+        this.optimisticLockingManager = optimisticLockingManager;
         senderListener.addHandler(this);
         senderListener.setBulletinRepository(bulletinRepository);
 
@@ -393,9 +427,15 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
             public void heartbeat() {
             }
         }, this, encryptor);
+        
+        // When we construct the scheduling agents, we can pass null for a lot of the arguments because we are only
+        // going to be scheduling Reporting Tasks. Otherwise, it would not be okay.
         processScheduler.setSchedulingAgent(SchedulingStrategy.TIMER_DRIVEN, new TimerDrivenSchedulingAgent(null, reportingTaskEngine, null, encryptor));
+        processScheduler.setSchedulingAgent(SchedulingStrategy.CRON_DRIVEN, new QuartzSchedulingAgent(null, reportingTaskEngine, null, encryptor));
         processScheduler.setMaxThreadCount(SchedulingStrategy.TIMER_DRIVEN, 10);
         processScheduler.setMaxThreadCount(SchedulingStrategy.CRON_DRIVEN, 10);
+        
+        controllerServiceProvider = new StandardControllerServiceProvider(processScheduler, bulletinRepository);
     }
 
     public void start() throws IOException {
@@ -429,14 +469,20 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
                 }
 
                 // load flow
+                final ClusterDataFlow clusterDataFlow;
                 if (dataFlowManagementService.isFlowCurrent()) {
-                    final ClusterDataFlow clusterDataFlow = dataFlowManagementService.loadDataFlow();
+                    clusterDataFlow = dataFlowManagementService.loadDataFlow();
                     cachedDataFlow = clusterDataFlow.getDataFlow();
                     primaryNodeId = clusterDataFlow.getPrimaryNodeId();
                 } else {
                     throw new IOException("Flow is not current.");
                 }
 
+                final byte[] serializedServices = clusterDataFlow.getControllerServices();
+                if ( serializedServices != null && serializedServices.length > 0 ) {
+                	ControllerServiceLoader.loadControllerServices(this, new ByteArrayInputStream(serializedServices), encryptor, bulletinRepository, properties.getAutoResumeState());
+                }
+                
                 // start multicast broadcasting service, if configured
                 if (servicesBroadcaster != null) {
                     servicesBroadcaster.start();
@@ -446,8 +492,10 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
                 executeSafeModeTask();
 
                 // Load and start running Reporting Tasks
-                final File taskFile = new File(properties.getProperty(NiFiProperties.TASK_CONFIGURATION_FILE));
-                reportingTasks.addAll(loadReportingTasks(taskFile));
+                final byte[] serializedReportingTasks = clusterDataFlow.getReportingTasks();
+                if ( serializedReportingTasks != null && serializedReportingTasks.length > 0 ) {
+                	loadReportingTasks(serializedReportingTasks);
+                }
             } catch (final IOException ioe) {
                 logger.warn("Failed to initialize cluster services due to: " + ioe, ioe);
                 stop();
@@ -861,22 +909,17 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
         reconnectionThread.start();
     }
 
-    private List<ReportingTaskNode> loadReportingTasks(final File taskConfigXml) {
-        final List<ReportingTaskNode> tasks = new ArrayList<>();
-        if (taskConfigXml == null) {
-            logger.info("No controller tasks to start");
-            return tasks;
-        }
+    private Map<String, ReportingTaskNode> loadReportingTasks(final byte[] serialized) {
+        final Map<String, ReportingTaskNode> tasks = new HashMap<>();
 
         try {
-            final URL schemaUrl = getClass().getResource("/ReportingTaskConfiguration.xsd");
-            final Document document = parse(taskConfigXml, schemaUrl);
+            final Document document = parse(serialized);
 
-            final NodeList tasksNodes = document.getElementsByTagName("tasks");
+            final NodeList tasksNodes = document.getElementsByTagName("reportingTasks");
             final Element tasksElement = (Element) tasksNodes.item(0);
 
             //optional properties for all ReportingTasks
-            for (final Element taskElement : DomUtils.getChildElementsByTagName(tasksElement, "task")) {
+            for (final Element taskElement : DomUtils.getChildElementsByTagName(tasksElement, "reportingTask")) {
                 //add global properties common to all tasks
                 Map<String, String> properties = new HashMap<>();
 
@@ -901,17 +944,20 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
                 final String taskSchedulingPeriod = DomUtils.getChild(taskElement, "schedulingPeriod").getTextContent().trim();
                 final String taskClass = DomUtils.getChild(taskElement, "class").getTextContent().trim();
 
-                //optional task-specific properties
-                for (final Element optionalProperty : DomUtils.getChildElementsByTagName(taskElement, "property")) {
-                    final String name = optionalProperty.getAttribute("name");
-                    final String value = optionalProperty.getTextContent().trim();
+                final String scheduleStateValue = DomUtils.getChild(taskElement, "scheduledState").getTextContent().trim();
+                final ScheduledState scheduledState = ScheduledState.valueOf(scheduleStateValue);
+                
+                // Reporting Task Properties
+                for (final Element property : DomUtils.getChildElementsByTagName(taskElement, "property")) {
+                    final String name = DomUtils.getChildText(property, "name");
+                    final String value = DomUtils.getChildText(property, "value");
                     properties.put(name, value);
                 }
 
                 //set the class to be used for the configured reporting task
                 final ReportingTaskNode reportingTaskNode;
                 try {
-                    reportingTaskNode = createReportingTask(taskClass, taskId);
+                    reportingTaskNode = createReportingTask(taskClass, taskId, false);
                 } catch (final ReportingTaskInstantiationException e) {
                     logger.error("Unable to load reporting task {} due to {}", new Object[]{taskId, e});
                     if (logger.isDebugEnabled()) {
@@ -922,27 +968,61 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
 
                 final ReportingTask reportingTask = reportingTaskNode.getReportingTask();
 
-                final ReportingInitializationContext config = new StandardReportingInitializationContext(taskId, taskName, schedulingStrategy, taskSchedulingPeriod, this);
+                final ComponentLog componentLog = new SimpleProcessLogger(taskId, reportingTask);
+                final ReportingInitializationContext config = new StandardReportingInitializationContext(taskId, taskName, 
+                        schedulingStrategy, taskSchedulingPeriod, componentLog, this);
                 reportingTask.initialize(config);
 
+                final String annotationData = DomUtils.getChildText(taskElement, "annotationData");
+                if ( annotationData != null ) {
+                    reportingTaskNode.setAnnotationData(annotationData.trim());
+                }
+                
                 final Map<PropertyDescriptor, String> resolvedProps;
                 try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
                     resolvedProps = new HashMap<>();
                     for (final Map.Entry<String, String> entry : properties.entrySet()) {
                         final PropertyDescriptor descriptor = reportingTask.getPropertyDescriptor(entry.getKey());
-                        resolvedProps.put(descriptor, entry.getValue());
+                        if ( entry.getValue() == null ) {
+                            resolvedProps.put(descriptor, descriptor.getDefaultValue());
+                        } else {
+                            resolvedProps.put(descriptor, entry.getValue());
+                        }
                     }
                 }
 
                 for (final Map.Entry<PropertyDescriptor, String> entry : resolvedProps.entrySet()) {
-                    reportingTaskNode.setProperty(entry.getKey().getName(), entry.getValue());
+                    if ( entry.getValue() != null ) {
+                        reportingTaskNode.setProperty(entry.getKey().getName(), entry.getValue());
+                    }
+                }
+                
+                final String comments = DomUtils.getChildText(taskElement, "comment");
+                if ( comments != null ) {
+                    reportingTaskNode.setComments(comments);
                 }
 
-                processScheduler.schedule(reportingTaskNode);
-                tasks.add(reportingTaskNode);
+                reportingTaskNode.setScheduledState(scheduledState);
+                if ( ScheduledState.RUNNING.equals(scheduledState) ) {
+                    if ( reportingTaskNode.isValid() ) {
+                        try {
+                            processScheduler.schedule(reportingTaskNode);
+                        } catch (final Exception e) {
+                            logger.error("Failed to start {} due to {}", reportingTaskNode, e);
+                            if ( logger.isDebugEnabled() ) {
+                                logger.error("", e);
+                            }
+                        }
+                    } else {
+                        logger.error("Failed to start {} because it is invalid due to {}", reportingTaskNode, reportingTaskNode.getValidationErrors());
+                    }
+                }
+                
+                
+                tasks.put(reportingTaskNode.getIdentifier(), reportingTaskNode);
             }
         } catch (final SAXException | ParserConfigurationException | IOException | DOMException | NumberFormatException | InitializationException t) {
-            logger.error("Unable to load reporting tasks from {} due to {}", new Object[]{taskConfigXml, t});
+            logger.error("Unable to load reporting tasks due to {}", new Object[]{t});
             if (logger.isDebugEnabled()) {
                 logger.error("", t);
             }
@@ -951,7 +1031,9 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
         return tasks;
     }
 
-    private ReportingTaskNode createReportingTask(final String type, final String id) throws ReportingTaskInstantiationException {
+    
+    @Override
+    public ReportingTaskNode createReportingTask(final String type, final String id, final boolean firstTimeAdded) throws ReportingTaskInstantiationException {
         if (type == null) {
             throw new NullPointerException();
         }
@@ -981,14 +1063,22 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
         final ValidationContextFactory validationContextFactory = new StandardValidationContextFactory(this);
         final ReportingTaskNode taskNode = new ClusteredReportingTaskNode(task, id, processScheduler,
                 new ClusteredEventAccess(this), bulletinRepository, controllerServiceProvider, validationContextFactory);
+        taskNode.setName(task.getClass().getSimpleName());
+        
+        reportingTasks.put(id, taskNode);
+        if ( firstTimeAdded ) {
+            try (final NarCloseable x = NarCloseable.withNarLoader()) {
+                ReflectionUtils.invokeMethodsWithAnnotation(OnAdded.class, task);
+            } catch (final Exception e) {
+                throw new ProcessorLifeCycleException("Failed to invoke On-Added Lifecycle methods of " + task, e);
+            }
+        }
+        
         return taskNode;
     }
 
-    private Document parse(final File xmlFile, final URL schemaUrl) throws SAXException, ParserConfigurationException, IOException {
-        final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
-        final Schema schema = schemaFactory.newSchema(schemaUrl);
+    private Document parse(final byte[] serialized) throws SAXException, ParserConfigurationException, IOException {
         final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
-        docFactory.setSchema(schema);
         final DocumentBuilder builder = docFactory.newDocumentBuilder();
 
         builder.setErrorHandler(new org.xml.sax.ErrorHandler() {
@@ -1021,12 +1111,7 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
         });
 
         // build the docuemnt
-        final Document document = builder.parse(xmlFile);
-
-        // ensure schema compliance
-        final Validator validator = schema.newValidator();
-        validator.validate(new DOMSource(document));
-
+        final Document document = builder.parse(new ByteArrayInputStream(serialized));
         return document;
     }
 
@@ -1287,7 +1372,19 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
             writeLock.unlock("handleControllerStartupFailure");
         }
     }
-
+    
+    /**
+     * Adds an instance of a specified controller service.
+     *
+     * @param type
+     * @param id
+     * @param properties
+     * @return
+     */
+    @Override
+    public ControllerServiceNode createControllerService(final String type, final String id, final boolean firstTimeAdded) {
+    	return controllerServiceProvider.createControllerService(type, id, firstTimeAdded);
+    }
 
     @Override
     public ControllerService getControllerService(String serviceIdentifier) {
@@ -1310,11 +1407,16 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
     }
 
     @Override
-    public ControllerServiceNode createControllerService(final String type, final String id, final boolean firstTimeAdded) {
-        return controllerServiceProvider.createControllerService(type, id, firstTimeAdded);
+    public boolean isControllerServiceEnabling(final String serviceIdentifier) {
+        return controllerServiceProvider.isControllerServiceEnabling(serviceIdentifier);
     }
     
     @Override
+    public String getControllerServiceName(final String serviceIdentifier) {
+    	return controllerServiceProvider.getControllerServiceName(serviceIdentifier);
+    }
+
+    @Override
     public void removeControllerService(final ControllerServiceNode serviceNode) {
         controllerServiceProvider.removeControllerService(serviceNode);
     }
@@ -1326,10 +1428,214 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
     }
     
     @Override
+    public void enableControllerServices(final Collection<ControllerServiceNode> serviceNodes) {
+        controllerServiceProvider.enableControllerServices(serviceNodes);
+    }
+    
+    @Override
     public void disableControllerService(final ControllerServiceNode serviceNode) {
         controllerServiceProvider.disableControllerService(serviceNode);
     }
     
+    @Override
+    public Set<ControllerServiceNode> getAllControllerServices() {
+    	return controllerServiceProvider.getAllControllerServices();
+    }
+    
+    
+    @Override
+    public void disableReferencingServices(final ControllerServiceNode serviceNode) {
+        controllerServiceProvider.disableReferencingServices(serviceNode);
+    }
+    
+    @Override
+    public void enableReferencingServices(final ControllerServiceNode serviceNode) {
+        controllerServiceProvider.enableReferencingServices(serviceNode);
+    }
+    
+    @Override
+    public void scheduleReferencingComponents(final ControllerServiceNode serviceNode) {
+        controllerServiceProvider.scheduleReferencingComponents(serviceNode);
+    }
+    
+    @Override
+    public void unscheduleReferencingComponents(final ControllerServiceNode serviceNode) {
+        controllerServiceProvider.unscheduleReferencingComponents(serviceNode);
+    }
+    
+    @Override
+    public void verifyCanEnableReferencingServices(final ControllerServiceNode serviceNode) {
+        controllerServiceProvider.verifyCanEnableReferencingServices(serviceNode);
+    }
+    
+    @Override
+    public void verifyCanScheduleReferencingComponents(final ControllerServiceNode serviceNode) {
+        controllerServiceProvider.verifyCanScheduleReferencingComponents(serviceNode);
+    }
+    
+    @Override
+    public void verifyCanDisableReferencingServices(final ControllerServiceNode serviceNode) {
+        controllerServiceProvider.verifyCanDisableReferencingServices(serviceNode);
+    }
+    
+    @Override
+    public void verifyCanStopReferencingComponents(final ControllerServiceNode serviceNode) {
+        controllerServiceProvider.verifyCanStopReferencingComponents(serviceNode);
+    }
+    
+    private byte[] serialize(final Document doc) throws TransformerException {
+    	final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    	final DOMSource domSource = new DOMSource(doc);
+        final StreamResult streamResult = new StreamResult(baos);
+
+        // configure the transformer and convert the DOM
+        final TransformerFactory transformFactory = TransformerFactory.newInstance();
+        final Transformer transformer = transformFactory.newTransformer();
+        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
+        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+
+        // transform the document to byte stream
+        transformer.transform(domSource, streamResult);
+        return baos.toByteArray();
+    }
+    
+    private byte[] serializeControllerServices() throws ParserConfigurationException, TransformerException {
+    	final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+        final DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
+        final Document document = docBuilder.newDocument();
+    	final Element rootElement = document.createElement("controllerServices");
+    	document.appendChild(rootElement);
+    	
+    	for ( final ControllerServiceNode serviceNode : getAllControllerServices() ) {
+    		StandardFlowSerializer.addControllerService(rootElement, serviceNode, encryptor);
+    	}
+    	
+    	return serialize(document);
+    }
+    
+    private byte[] serializeReportingTasks() throws ParserConfigurationException, TransformerException {
+    	final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+        final DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
+        final Document document = docBuilder.newDocument();
+    	final Element rootElement = document.createElement("reportingTasks");
+    	document.appendChild(rootElement);
+    	
+    	for ( final ReportingTaskNode taskNode : getAllReportingTasks() ) {
+    		StandardFlowSerializer.addReportingTask(rootElement, taskNode, encryptor);
+    	}
+    	
+    	return serialize(document);
+    }
+    
+    
+    public void saveControllerServices() {
+    	try {
+    		dataFlowManagementService.updateControllerServices(serializeControllerServices());
+    	} catch (final Exception e) {
+    		logger.error("Failed to save changes to NCM's Controller Services; changes may be lost on restart due to " + e);
+    		if ( logger.isDebugEnabled() ) {
+    			logger.error("", e);
+    		}
+    		
+    		getBulletinRepository().addBulletin(BulletinFactory.createBulletin("Controller Services", Severity.ERROR.name(), 
+    				"Failed to save changes to NCM's Controller Services; changes may be lost on restart. See logs for more details."));
+    	}
+    }
+    
+    public void saveReportingTasks() {
+    	try {
+    		dataFlowManagementService.updateReportingTasks(serializeReportingTasks());
+    	} catch (final Exception e) {
+    		logger.error("Failed to save changes to NCM's Reporting Tasks; changes may be lost on restart due to " + e);
+    		if ( logger.isDebugEnabled() ) {
+    			logger.error("", e);
+    		}
+    		
+    		getBulletinRepository().addBulletin(BulletinFactory.createBulletin("Reporting Tasks", Severity.ERROR.name(), 
+    				"Failed to save changes to NCM's Reporting Tasks; changes may be lost on restart. See logs for more details."));
+    	}
+    }
+
+    @Override
+    public Set<ReportingTaskNode> getAllReportingTasks() {
+    	readLock.lock();
+    	try {
+    		return new HashSet<>(reportingTasks.values());
+    	} finally {
+    		readLock.unlock("getReportingTasks");
+    	}
+    }
+
+    @Override
+    public ReportingTaskNode getReportingTaskNode(final String taskId) {
+    	readLock.lock();
+    	try {
+    		return reportingTasks.get(taskId);
+    	} finally {
+    		readLock.unlock("getReportingTaskNode");
+    	}
+    }
+
+    @Override
+    public void startReportingTask(final ReportingTaskNode reportingTaskNode) {
+        reportingTaskNode.verifyCanStart();
+       	processScheduler.schedule(reportingTaskNode);
+    }
+
+    
+    @Override
+    public void stopReportingTask(final ReportingTaskNode reportingTaskNode) {
+        reportingTaskNode.verifyCanStop();
+        processScheduler.unschedule(reportingTaskNode);
+    }
+
+    @Override
+    public void removeReportingTask(final ReportingTaskNode reportingTaskNode) {
+    	writeLock.lock();
+    	try {
+	        final ReportingTaskNode existing = reportingTasks.get(reportingTaskNode.getIdentifier());
+	        if ( existing == null || existing != reportingTaskNode ) {
+	            throw new IllegalStateException("Reporting Task " + reportingTaskNode + " does not exist in this Flow");
+	        }
+	        
+	        reportingTaskNode.verifyCanDelete();
+	        
+	        try (final NarCloseable x = NarCloseable.withNarLoader()) {
+	            ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnRemoved.class, reportingTaskNode.getReportingTask(), reportingTaskNode.getConfigurationContext());
+	        }
+	        
+	        for ( final Map.Entry<PropertyDescriptor, String> entry : reportingTaskNode.getProperties().entrySet() ) {
+	            final PropertyDescriptor descriptor = entry.getKey();
+	            if (descriptor.getControllerServiceDefinition() != null ) {
+	                final String value = entry.getValue() == null ? descriptor.getDefaultValue() : entry.getValue();
+	                if ( value != null ) {
+	                    final ControllerServiceNode serviceNode = controllerServiceProvider.getControllerServiceNode(value);
+	                    if ( serviceNode != null ) {
+	                        serviceNode.removeReference(reportingTaskNode);
+	                    }
+	                }
+	            }
+	        }
+	        
+	        reportingTasks.remove(reportingTaskNode.getIdentifier());
+    	} finally {
+    		writeLock.unlock("removeReportingTask");
+    	}
+    }
+    
+    
+    @Override
+    public void disableReportingTask(final ReportingTaskNode reportingTask) {
+        reportingTask.verifyCanDisable();
+        processScheduler.disableReportingTask(reportingTask);
+    }
+    
+    @Override
+    public void enableReportingTask(final ReportingTaskNode reportingTask) {
+        reportingTask.verifyCanEnable();
+        processScheduler.enableReportingTask(reportingTask);
+    }
+    
     
     /**
      * Handle a bulletins message.
@@ -1966,65 +2272,114 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
         // check if this request can change the flow
         final boolean mutableRequest = canChangeNodeState(method, uri);
 
-        // update headers to contain cluster contextual information to send to the node
-        final Map<String, String> updatedHeaders = new HashMap<>(headers);
-        final ClusterContext clusterCtx = new ClusterContextImpl();
-        clusterCtx.setRequestSentByClusterManager(true);                 // indicate request is sent from cluster manager
-        clusterCtx.setRevision(revision);
+        final ObjectHolder<NodeResponse> holder = new ObjectHolder<>(null);
+        final UpdateRevision federateRequest = new UpdateRevision() {
+            @Override
+            public Revision execute(Revision currentRevision) {
+                // update headers to contain cluster contextual information to send to the node
+                final Map<String, String> updatedHeaders = new HashMap<>(headers);
+                final ClusterContext clusterCtx = new ClusterContextImpl();
+                clusterCtx.setRequestSentByClusterManager(true);                 // indicate request is sent from cluster manager
+                clusterCtx.setRevision(currentRevision);
+
+                // serialize cluster context and add to request header
+                final String serializedClusterCtx = WebUtils.serializeObjectToHex(clusterCtx);
+                updatedHeaders.put(CLUSTER_CONTEXT_HTTP_HEADER, serializedClusterCtx);
+
+                // if the request is mutable, we need to verify that it is a valid request for all nodes in the cluster.
+                if (mutableRequest) {
+                    updatedHeaders.put(NCM_EXPECTS_HTTP_HEADER, "150-NodeContinue");
 
-        // serialize cluster context and add to request header
-        final String serializedClusterCtx = WebUtils.serializeObjectToHex(clusterCtx);
-        updatedHeaders.put(CLUSTER_CONTEXT_HTTP_HEADER, serializedClusterCtx);
+                    final Set<NodeResponse> nodeResponses;
+                    if (entity == null) {
+                        nodeResponses = httpRequestReplicator.replicate(nodeIds, method, uri, parameters, updatedHeaders);
+                    } else {
+                        nodeResponses = httpRequestReplicator.replicate(nodeIds, method, uri, entity, updatedHeaders);
+                    }
 
-        // if the request is mutable, we need to verify that it is a valid request for all nodes in the cluster.
-        if (mutableRequest) {
-            updatedHeaders.put(NCM_EXPECTS_HTTP_HEADER, "150-NodeContinue");
+                    updatedHeaders.remove(NCM_EXPECTS_HTTP_HEADER);
 
-            final Set<NodeResponse> nodeResponses;
-            if (entity == null) {
-                nodeResponses = httpRequestReplicator.replicate(nodeIds, method, uri, parameters, updatedHeaders);
-            } else {
-                nodeResponses = httpRequestReplicator.replicate(nodeIds, method, uri, entity, updatedHeaders);
-            }
+                    for (final NodeResponse response : nodeResponses) {
+                        if (response.getStatus() != NODE_CONTINUE_STATUS_CODE) {
+                            final String nodeDescription = response.getNodeId().getApiAddress() + ":" + response.getNodeId().getApiPort();
+                            final ClientResponse clientResponse = response.getClientResponse();
+                            if (clientResponse == null) {
+                                throw new IllegalClusterStateException("Node " + nodeDescription + " is unable to fulfill this request due to: Unexpected Response Code " + response.getStatus());
+                            }
+                            final String nodeExplanation = clientResponse.getEntity(String.class);
+                            throw new IllegalClusterStateException("Node " + nodeDescription + " is unable to fulfill this request due to: " + nodeExplanation, response.getThrowable());
+                        }
+                    }
 
-            updatedHeaders.remove(NCM_EXPECTS_HTTP_HEADER);
+                    // set flow state to unknown to denote a mutable request replication in progress
+                    logger.debug("Setting Flow State to UNKNOWN due to mutable request to {} {}", method, uri);
+                    notifyDataFlowManagmentServiceOfFlowStateChange(PersistedFlowState.UNKNOWN);
+                }
 
-            for (final NodeResponse response : nodeResponses) {
-                if (response.getStatus() != NODE_CONTINUE_STATUS_CODE) {
-                    final String nodeDescription = response.getNodeId().getApiAddress() + ":" + response.getNodeId().getApiPort();
-                    final ClientResponse clientResponse = response.getClientResponse();
-                    if (clientResponse == null) {
-                        throw new IllegalClusterStateException("Node " + nodeDescription + " is unable to fulfill this request due to: Unexpected Response Code " + response.getStatus());
+                // replicate request
+                final Set<NodeResponse> nodeResponses;
+                try {
+                    if (entity == null) {
+                        nodeResponses = httpRequestReplicator.replicate(nodeIds, method, uri, parameters, updatedHeaders);
+                    } else {
+                        nodeResponses = httpRequestReplicator.replicate(nodeIds, method, uri, entity, updatedHeaders);
+                    }
+                } catch (final UriConstructionException uce) {
+                    // request was not replicated, so mark the flow with its original state
+                    if (mutableRequest) {
+                        notifyDataFlowManagmentServiceOfFlowStateChange(originalPersistedFlowState);
                     }
-                    final String nodeExplanation = clientResponse.getEntity(String.class);
-                    throw new IllegalClusterStateException("Node " + nodeDescription + " is unable to fulfill this request due to: " + nodeExplanation, response.getThrowable());
-                }
-            }
 
-            // set flow state to unknown to denote a mutable request replication in progress
-            logger.debug("Setting Flow State to UNKNOWN due to mutable request to {} {}", method, uri);
-            notifyDataFlowManagmentServiceOfFlowStateChange(PersistedFlowState.UNKNOWN);
-        }
+                    throw uce;
+                }
 
-        // replicate request
-        final Set<NodeResponse> nodeResponses;
-        try {
-            if (entity == null) {
-                nodeResponses = httpRequestReplicator.replicate(nodeIds, method, uri, parameters, updatedHeaders);
-            } else {
-                nodeResponses = httpRequestReplicator.replicate(nodeIds, method, uri, entity, updatedHeaders);
-            }
-        } catch (final UriConstructionException uce) {
-            // request was not replicated, so mark the flow with its original state
-            if (mutableRequest) {
-                notifyDataFlowManagmentServiceOfFlowStateChange(originalPersistedFlowState);
+                // merge the response
+                final NodeResponse clientResponse = mergeResponses(uri, method, nodeResponses, mutableRequest);
+                holder.set(clientResponse);
+                
+                // if we have a response get the updated cluster context for auditing and revision updating
+                Revision updatedRevision = null;
+                if (mutableRequest && clientResponse != null) {
+                    try {
+                        // get the cluster context from the response header
+                        final String serializedClusterContext = clientResponse.getClientResponse().getHeaders().getFirst(CLUSTER_CONTEXT_HTTP_HEADER);
+                        if (StringUtils.isNotBlank(serializedClusterContext)) {
+                            // deserialize object
+                            final Serializable clusterContextObj = WebUtils.deserializeHexToObject(serializedClusterContext);
+
+                            // if we have a valid object, audit the actions
+                            if (clusterContextObj instanceof ClusterContext) {
+                                final ClusterContext clusterContext = (ClusterContext) clusterContextObj;
+                                if (auditService != null) {
+                                    try {
+                                        auditService.addActions(clusterContext.getActions());
+                                    } catch (Throwable t) {
+                                        logger.warn("Unable to record actions: " + t.getMessage());
+                                        if (logger.isDebugEnabled()) {
+                                            logger.warn(StringUtils.EMPTY, t);
+                                        }
+                                    }
+                                }
+                                updatedRevision = clusterContext.getRevision();
+                            }
+                        }
+                    } catch (final ClassNotFoundException cnfe) {
+                        logger.warn("Classpath issue detected because failed to deserialize cluster context from node response due to: " + cnfe, cnfe);
+                    }
+                }
+                
+                return updatedRevision;
             }
-
-            throw uce;
+        };
+        
+        // federate the request and lock on the revision
+        if (mutableRequest) {
+            optimisticLockingManager.setRevision(federateRequest);
+        } else {
+            federateRequest.execute(optimisticLockingManager.getLastModification().getRevision());
         }
-
-        final NodeResponse clientResponse = mergeResponses(uri, method, nodeResponses, mutableRequest);
-        return clientResponse;
+        
+        return holder.get();
     }
 
     private static boolean isProcessorsEndpoint(final URI uri, final String method) {
@@ -2032,7 +2387,7 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
     }
 
     private static boolean isProcessorEndpoint(final URI uri, final String method) {
-        if (("GET".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) && PROCESSOR_URI_PATTERN.matcher(uri.getPath()).matches()) {
+        if (("GET".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) && (PROCESSOR_URI_PATTERN.matcher(uri.getPath()).matches() || CLUSTER_PROCESSOR_URI_PATTERN.matcher(uri.getPath()).matches()) ) {
             return true;
         } else if ("POST".equalsIgnoreCase(method) && PROCESSORS_URI_PATTERN.matcher(uri.getPath()).matches()) {
             return true;
@@ -2079,13 +2434,51 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
     private static boolean isProvenanceEventEndpoint(final URI uri, final String method) {
         return "GET".equalsIgnoreCase(method) && PROVENANCE_EVENT_URI.matcher(uri.getPath()).matches();
     }
+    
+    private static boolean isControllerServicesEndpoint(final URI uri, final String method) {
+        return "GET".equalsIgnoreCase(method) && CONTROLLER_SERVICES_URI.equals(uri.getPath());
+    }
+    
+    private static boolean isControllerServiceEndpoint(final URI uri, final String method) {
+        if (("GET".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) && CONTROLLER_SERVICE_URI_PATTERN.matcher(uri.getPath()).matches()) {
+            return true;
+        } else if ("POST".equalsIgnoreCase(method) && CONTROLLER_SERVICES_URI.equals(uri.getPath())) {
+            return true;
+        }
+
+        return false;
+    }
+    
+    private static boolean isControllerServiceReferenceEndpoint(final URI uri, final String method) {
+        if (("GET".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) && CONTROLLER_SERVICE_REFERENCES_URI_PATTERN.matcher(uri.getPath()).matches()) {
+            return true;
+        }
+        
+        return false;
+    }
+    
+    private static boolean isReportingTasksEndpoint(final URI uri, final String method) {
+        return "GET".equalsIgnoreCase(method) && REPORTING_TASKS_URI.equals(uri.getPath());
+    }
+    
+    private static boolean isReportingTaskEndpoint(final URI uri, final String method) {
+        if (("GET".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) && REPORTING_TASK_URI_PATTERN.matcher(uri.getPath()).matches()) {
+            return true;
+        } else if ("POST".equalsIgnoreCase(method) && REPORTING_TASKS_URI.equals(uri.getPath())) {
+            return true;
+        }
+
+        return false;
+    }
 
     static boolean isResponseInterpreted(final URI uri, final String method) {
         return isProcessorsEndpoint(uri, method) || isProcessorEndpoint(uri, method)
                 || isRemoteProcessGroupsEndpoint(uri, method) || isRemoteProcessGroupEndpoint(uri, method)
                 || isProcessGroupEndpoint(uri, method)
                 || isTemplateEndpoint(uri, method) || isFlowSnippetEndpoint(uri, method)
-                || isProvenanceQueryEndpoint(uri, method) || isProvenanceEventEndpoint(uri, method);
+                || isProvenanceQueryEndpoint(uri, method) || isProvenanceEventEndpoint(uri, method)
+                || isControllerServicesEndpoint(uri, method) || isControllerServiceEndpoint(uri, method) || isControllerServiceReferenceEndpoint(uri, method)
+                || isReportingTasksEndpoint(uri, method) || isReportingTaskEndpoint(uri, method);
     }
 
     private void mergeProcessorValidationErrors(final ProcessorDTO processor, Map<NodeIdentifier, ProcessorDTO> processorMap) {
@@ -2095,37 +2488,12 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
             final NodeIdentifier nodeId = nodeEntry.getKey();
             final ProcessorDTO nodeProcessor = nodeEntry.getValue();
 
-            // get the processor's validation errors and put them into a map
-            // where the key is the validation error and the value is the set of all
-            // nodes that reported that validation error.
-            final Collection<String> nodeValidationErrors = nodeProcessor.getValidationErrors();
-            if (nodeValidationErrors != null) {
-                for (final String nodeValidationError : nodeValidationErrors) {
-                    Set<NodeIdentifier> nodeSet = validationErrorMap.get(nodeValidationError);
-                    if (nodeSet == null) {
-                        nodeSet = new HashSet<>();
-                        validationErrorMap.put(nodeValidationError, nodeSet);
-                    }
-                    nodeSet.add(nodeId);
-                }
-            }
-        }
-
-        final Set<String> normalizedValidationErrors = new HashSet<>();
-        for (final Map.Entry<String, Set<NodeIdentifier>> validationEntry : validationErrorMap.entrySet()) {
-            final String msg = validationEntry.getKey();
-            final Set<NodeIdentifier> nodeIds = validationEntry.getValue();
-
-            if (nodeIds.size() == processorMap.size()) {
-                normalizedValidationErrors.add(msg);
-            } else {
-                for (final NodeIdentifier nodeId : nodeIds) {
-                    normalizedValidationErrors.add(nodeId.getApiAddress() + ":" + nodeId.getApiPort() + " -- " + msg);
-                }
-            }
+            // merge the validation errors
+            mergeValidationErrors(validationErrorMap, nodeId, nodeProcessor.getValidationErrors());
         }
 
-        processor.setValidationErrors(normalizedValidationErrors);
+        // set the merged the validation errors
+        processor.setValidationErrors(normalizedMergedValidationErrors(validationErrorMap, processorMap.size()));
     }
 
     private void mergeProvenanceQueryResults(final ProvenanceDTO provenanceDto, final Map<NodeIdentifier, ProvenanceDTO> resultMap, final Set<NodeResponse> problematicResponses) {
@@ -2293,7 +2661,158 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
             remoteProcessGroup.setAuthorizationIssues(mergedAuthorizationIssues);
         }
     }
+    
+    private void mergeControllerServiceReferences(final Set<ControllerServiceReferencingComponentDTO> referencingComponents, final Map<NodeIdentifier, Set<ControllerServiceReferencingComponentDTO>> referencingComponentMap) {
+        final Map<String, Integer> activeThreadCounts = new HashMap<>();
+        final Map<String, String> states = new HashMap<>();
+        for (final Map.Entry<NodeIdentifier, Set<ControllerServiceReferencingComponentDTO>> nodeEntry : referencingComponentMap.entrySet()) {
+            final Set<ControllerServiceReferencingComponentDTO> nodeReferencingComponents = nodeEntry.getValue();
+
+            // go through all the nodes referencing components
+            if ( nodeReferencingComponents != null ) {
+                for (final ControllerServiceReferencingComponentDTO nodeReferencingComponent : nodeReferencingComponents) {
+                    // handle active thread counts
+                    if (nodeReferencingComponent.getActiveThreadCount() != null && nodeReferencingComponent.getActiveThreadCount() > 0) {
+                        final Integer current = activeThreadCounts.get(nodeReferencingComponent.getId());
+                        if (current == null) {
+                            activeThreadCounts.put(nodeReferencingComponent.getId(), nodeReferencingComponent.getActiveThreadCount());
+                        } else {
+                            activeThreadCounts.put(nodeReferencingComponent.getId(), nodeReferencingComponent.getActiveThreadCount() + current);
+                        }
+                    }
+                    
+                    // handle controller service state
+                    final String state = states.get(nodeReferencingComponent.getId());
+                    if (state == null) {
+                        if (ControllerServiceState.DISABLING.name().equals(nodeReferencingComponent.getState())) {
+                            states.put(nodeReferencingComponent.getId(), ControllerServiceState.DISABLING.name());
+                        } else if (ControllerServiceState.ENABLING.name().equals(nodeReferencingComponent.getState())) {
+                            states.put(nodeReferencingComponent.getId(), ControllerServiceState.ENABLING.name());
+                        }
+                    }
+                }
+            }
+        }            
+
+        // go through each referencing components
+        for (final ControllerServiceReferencingComponentDTO referencingComponent : referencingComponents) {
+            final Integer activeThreadCount = activeThreadCounts.get(referencingComponent.getId());
+            if (activeThreadCount != null) {
+                referencingComponent.setActiveThreadCount(activeThreadCount);
+            }
+            
+            final String state = states.get(referencingComponent.getId());
+            if (state != null) {
+                referencingComponent.setState(state);
+            }
+        }
+    }
+    
+    private void mergeControllerService(final ControllerServiceDTO controllerService, final Map<NodeIdentifier, ControllerServiceDTO> controllerServiceMap) {
+        final Map<String, Set<NodeIdentifier>> validationErrorMap = new HashMap<>();
+        final Set<ControllerServiceReferencingComponentDTO> referencingComponents = controllerService.getReferencingComponents();
+        final Map<NodeIdentifier, Set<ControllerServiceReferencingComponentDTO>> nodeReferencingComponentsMap = new HashMap<>();
+        
+        String state = null;
+        for (final Map.Entry<NodeIdentifier, ControllerServiceDTO> nodeEntry : controllerServiceMap.entrySet()) {
+            final NodeIdentifier nodeId = nodeEntry.getKey();
+            final ControllerServiceDTO nodeControllerService = nodeEntry.getValue();
+            
+            if (state == null) {
+                if (ControllerServiceState.DISABLING.name().equals(nodeControllerService.getState())) {
+                    state = ControllerServiceState.DISABLING.name();
+                } else if (ControllerServiceState.ENABLING.name().equals(nodeControllerService.getState())) {
+                    state = ControllerServiceState.ENABLING.name();
+                }
+            }
+            
+            for (final ControllerServiceReferencingComponentDTO nodeReferencingComponents : nodeControllerService.getReferencingComponents()) {
+                nodeReferencingComponentsMap.put(nodeId, nodeReferencingComponents.getReferencingComponents());
+            }
+            
+            // merge the validation errors
+            mergeValidationErrors(validationErrorMap, nodeId, nodeControllerService.getValidationErrors());
+        }
+        
+        // merge the referencing components
+        mergeControllerServiceReferences(referencingComponents, nodeReferencingComponentsMap);
+        
+        // store the 'transition' state is applicable
+        if (state != null) {
+            controllerService.setState(state);
+        }
+        
+        // set the merged the validation errors
+        controllerService.setValidationErrors(normalizedMergedValidationErrors(validationErrorMap, controllerServiceMap.size()));
+    }
+    
+    private void mergeReportingTask(final ReportingTaskDTO reportingTask, final Map<NodeIdentifier, ReportingTaskDTO> reportingTaskMap) {
+        final Map<String, Set<NodeIdentifier>> validationErrorMap = new HashMap<>();
+
+        int activeThreadCount = 0;
+        for (final Map.Entry<NodeIdentifier, ReportingTaskDTO> nodeEntry : reportingTaskMap.entrySet()) {
+            final NodeIdentifier nodeId = nodeEntry.getKey();
+            final ReportingTaskDTO nodeReportingTask = nodeEntry.getValue();
+
+            if (nodeReportingTask.getActiveThreadCount() != null) {
+                activeThreadCount += nodeReportingTask.getActiveThreadCount();
+            }
+            
+            // merge the validation errors
+            mergeValidationErrors(validationErrorMap, nodeId, nodeReportingTask.getValidationErrors());
+        }
+
+        // set the merged active thread counts
+        reportingTask.setActiveThreadCount(activeThreadCount);
+        
+        // set the merged the validation errors
+        reportingTask.setValidationErrors(normalizedMergedValidationErrors(validationErrorMap, reportingTaskMap.size()));
+    }
 
+    /**
+     * Merges the validation errors into the specified map, recording the corresponding node identifier.
+     * 
+     * @param validationErrorMap
+     * @param nodeId
+     * @param nodeValidationErrors 
+     */
+    public void mergeValidationErrors(final Map<String, Set<NodeIdentifier>> validationErrorMap, final NodeIdentifier nodeId, final Collection<String> nodeValidationErrors) {
+        if (nodeValidationErrors != null) {
+            for (final String nodeValidationError : nodeValidationErrors) {
+                Set<NodeIdentifier> nodeSet = validationErrorMap.get(nodeValidationError);
+                if (nodeSet == null) {
+                    nodeSet = new HashSet<>();
+                    validationErrorMap.put(nodeValidationError, nodeSet);
+                }
+                nodeSet.add(nodeId);
+            }
+        }
+    }
+    
+    /**
+     * Normalizes the validation errors by prepending the corresponding nodes when the error does not exist across all nodes.
+     * 
+     * @param validationErrorMap
+     * @param totalNodes
+     * @return 
+     */
+    public Set<String> normalizedMergedValidationErrors(final Map<String, Set<NodeIdentifier>> validationErrorMap, int totalNodes) {
+        final Set<String> normalizedValidationErrors = new HashSet<>();
+        for (final Map.Entry<String, Set<NodeIdentifier>> validationEntry : validationErrorMap.entrySet()) {
+            final String msg = validationEntry.getKey();
+            final Set<NodeIdentifier> nodeIds = validationEntry.getValue();
+
+            if (nodeIds.size() == totalNodes) {
+                normalizedValidationErrors.add(msg);
+            } else {
+                for (final NodeIdentifier nodeId : nodeIds) {
+                    normalizedValidationErrors.add(nodeId.getApiAddress() + ":" + nodeId.getApiPort() + " -- " + msg);
+                }
+            }
+        }
+        return normalizedValidationErrors;
+    }
+    
     // requires write lock to be already acquired unless request is not mutable
     private NodeResponse mergeResponses(final URI uri, final String method, final Set<NodeResponse> nodeResponses, final boolean mutableRequest) {
         // holds the one response of all the node responses to return to the client
@@ -2583,6 +3102,126 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
             event.setClusterNodeAddress(nodeId.getApiAddress() + ":" + nodeId.getApiPort());
 
             clientResponse = new NodeResponse(clientResponse, responseEntity);
+        } else if (hasSuccessfulClientResponse && isControllerServiceEndpoint(uri, method)) {
+            final ControllerServiceEntity responseEntity = clientResponse.getClientResponse().getEntity(ControllerServiceEntity.class);
+            final ControllerServiceDTO controllerService = responseEntity.getControllerService();
+            
+            final Map<NodeIdentifier, ControllerServiceDTO> resultsMap = new HashMap<>();
+            for (final NodeResponse nodeResponse : updatedNodesMap.values()) {
+                if (problematicNodeResponses.contains(nodeResponse)) {
+                    continue;
+                }
+
+                final ControllerServiceEntity nodeResponseEntity = (nodeResponse == clientResponse) ? responseEntity : nodeResponse.getClientResponse().getEntity(ControllerServiceEntity.class);
+                final ControllerServiceDTO nodeControllerService = nodeResponseEntity.getControllerService();
+
+                resultsMap.put(nodeResponse.getNodeId(), nodeControllerService);
+            }
+            mergeControllerService(controllerService, resultsMap);
+            
+            clientResponse = new NodeResponse(clientResponse, responseEntity);
+        } else if (hasSuccessfulClientResponse && isControllerServicesEndpoint(uri, method)) {
+            final ControllerServicesEntity responseEntity = clientResponse.getClientResponse().getEntity(ControllerServicesEntity.class);
+            final Set<ControllerServiceDTO> controllerServices = responseEntity.getControllerServices();
+            
+            final Map<String, Map<NodeIdentifier, ControllerServiceDTO>> controllerServiceMap = new HashMap<>();
+            for (final NodeResponse nodeResponse : updatedNodesMap.values()) {
+                if (problematicNodeResponses.contains(nodeResponse)) {
+                    continue;
+                }
+
+                final ControllerServicesEntity nodeResponseEntity = (nodeResponse == clientResponse) ? responseEntity : nodeResponse.getClientResponse().getEntity(ControllerServicesEntity.class);
+                final Set<ControllerServiceDTO> nodeControllerServices = nodeResponseEntity.getControllerServices();
+
+                for (final ControllerServiceDTO nodeControllerService : nodeControllerServices) {
+                    Map<NodeIdentifier, ControllerServiceDTO> innerMap = controllerServiceMap.get(nodeControllerService.getId());
+                    if (innerMap == null) {
+                        innerMap = new HashMap<>();
+                        controllerServiceMap.put(nodeControllerService.getId(), innerMap);
+                    }
+
+                    innerMap.put(nodeResponse.getNodeId(), nodeControllerService);
+                }
+            }
+
+            for (final ControllerServiceDTO controllerService : controllerServices) {
+                final String procId = controllerService.getId();
+                final Map<NodeIdentifier, ControllerServiceDTO> mergeMap = controllerServiceMap.get(procId);
+
+                mergeControllerService(controllerService, mergeMap);
+            }
+
+            // create a new client response
+            clientResponse = new NodeResponse(clientResponse, responseEntity);
+        } else if (hasSuccessfulClientResponse && isControllerServiceReferenceEndpoint(uri, method)) {
+            final ControllerServiceReferencingComponentsEntity responseEntity = clientResponse.getClientResponse().getEntity(ControllerServiceReferencingComponentsEntity.class);
+            final Set<ControllerServiceReferencingComponentDTO> referencingComponents = responseEntity.getControllerServiceReferencingComponents();
+            
+            final Map<NodeIdentifier, Set<ControllerServiceReferencingComponentDTO>> resultsMap = new HashMap<>();
+            for (final NodeResponse nodeResponse : updatedNodesMap.values()) {
+                if (problematicNodeResponses.contains(nodeResponse)) {
+                    continue;
+                }
+
+                final ControllerServiceReferencingComponentsEntity nodeResponseEntity = (nodeResponse == clientResponse) ? responseEntity : nodeResponse.getClientResponse().getEntity(ControllerServiceReferencingComponentsEntity.class);
+                final Set<ControllerServiceReferencingComponentDTO> nodeReferencingComponents = nodeResponseEntity.getControllerServiceReferencingComponents();
+
+                resultsMap.put(nodeResponse.getNodeId(), nodeReferencingComponents);
+            }
+            mergeControllerServiceReferences(referencingComponents, resultsMap);
+            
+            clientResponse = new NodeResponse(clientResponse, responseEntity);
+        } else if (hasSuccessfulClientResponse && isReportingTaskEndpoint(uri, method)) {
+            final ReportingTaskEntity responseEntity = clientResponse.getClientResponse().getEntity(ReportingTaskEntity.class);
+            final ReportingTaskDTO reportingTask = responseEntity.getReportingTask();
+            
+            final Map<NodeIdentifier, ReportingTaskDTO> resultsMap = new HashMap<>();
+            for (final NodeResponse nodeResponse : updatedNodesMap.values()) {
+                if (problematicNodeResponses.contains(nodeResponse)) {
+                    continue;
+                }
+
+                final ReportingTaskEntity nodeResponseEntity = (nodeResponse == clientResponse) ? responseEntity : nodeResponse.getClientResponse().getEntity(ReportingTaskEntity.class);
+                final ReportingTaskDTO nodeReportingTask = nodeResponseEntity.getReportingTask();
+
+                resultsMap.put(nodeResponse.getNodeId(), nodeReportingTask);
+            }
+            mergeReportingTask(reportingTask, resultsMap);
+            
+            clientResponse = new NodeResponse(clientResponse, responseEntity);
+        } else if (hasSuccessfulClientResponse && isReportingTasksEndpoint(uri, method)) {
+            final ReportingTasksEntity responseEntity = clientResponse.getClientResponse().getEntity(ReportingTasksEntity.class);
+            final Set<ReportingTaskDTO> reportingTaskSet = responseEntity.getReportingTasks();
+            
+            final Map<String, Map<NodeIdentifier, ReportingTaskDTO>> reportingTaskMap = new HashMap<>();
+            for (final NodeResponse nodeResponse : updatedNodesMap.values()) {
+                if (problematicNodeResponses.contains(nodeResponse)) {
+                    continue;
+                }
+
+                final ReportingTasksEntity nodeResponseEntity = (nodeResponse == clientResponse) ? responseEntity : nodeResponse.getClientResponse().getEntity(ReportingTasksEntity.class);
+                final Set<ReportingTaskDTO> nodeReportingTasks = nodeResponseEntity.getReportingTasks();
+
+                for (final ReportingTaskDTO nodeReportingTask : nodeReportingTasks) {
+                    Map<NodeIdentifier, ReportingTaskDTO> innerMap = reportingTaskMap.get(nodeReportingTask.getId());
+                    if (innerMap == null) {
+                        innerMap = new HashMap<>();
+                        reportingTaskMap.put(nodeReportingTask.getId(), innerMap);
+                    }
+
+                    innerMap.put(nodeResponse.getNodeId(), nodeReportingTask);
+                }
+            }
+
+            for (final ReportingTaskDTO reportingTask : reportingTaskSet) {
+                final String procId = reportingTask.getId();
+                final Map<NodeIdentifier, ReportingTaskDTO> mergeMap = reportingTaskMap.get(procId);
+
+                mergeReportingTask(reportingTask, mergeMap);
+            }
+
+            // create a new client response
+            clientResponse = new NodeResponse(clientResponse, responseEntity);
         } else {
             if (!nodeResponsesToDrain.isEmpty()) {
                 drainResponses(nodeResponsesToDrain);
@@ -2616,36 +3255,6 @@ public class WebClusterManager implements HttpClusterManager, ProtocolHandler, C
                     logger.warn("All nodes failed to process URI {}. As a result, no node will be disconnected from cluster", uri);
                 }
             }
-
-            // if at least one node satisfied the request, then audit the action 
-            if (hasClientResponse) {
-                try {
-                    // get the cluster context from the response header
-                    final String serializedClusterContext = clientResponse.getClientResponse().getHeaders().getFirst(CLUSTER_CONTEXT_HTTP_HEADER);
-                    if (StringUtils.isNotBlank(serializedClusterContext)) {
-                        // deserialize object
-                        final Serializable clusterContextObj = WebUtils.deserializeHexToObject(serializedClusterContext);
-
-                        // if we have a valid object, audit the actions
-                        if (clusterContextObj instanceof ClusterContext) {
-                            final ClusterContext clusterContext = (ClusterContext) clusterContextObj;
-                            if (auditService != null) {
-                                try {
-                                    auditService.addActions(clusterContext.getActions());
-                                } catch (Throwable t) {
-                                    logger.warn("Unable to record actions: " + t.getMessage());
-                                    if (logger.isDebugEnabled()) {
-                                        logger.warn(StringUtils.EMPTY, t);
-                                    }
-                                }
-                            }
-                            revision = clusterContext.getRevision();
-                        }
-                    }
-                } catch (final ClassNotFoundException cnfe) {
-                    logger.warn("Classpath issue detected because failed to deserialize cluster context from node response due to: " + cnfe, cnfe);
-                }
-            }
         }
 
         return clientResponse;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/spring/WebClusterManagerFactoryBean.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/spring/WebClusterManagerFactoryBean.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/spring/WebClusterManagerFactoryBean.java
index 7169730..d3cff3b 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/spring/WebClusterManagerFactoryBean.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/spring/WebClusterManagerFactoryBean.java
@@ -16,7 +16,6 @@
  */
 package org.apache.nifi.cluster.spring;
 
-import java.nio.file.Paths;
 import org.apache.nifi.admin.service.AuditService;
 import org.apache.nifi.cluster.event.EventManager;
 import org.apache.nifi.cluster.firewall.ClusterNodeFirewall;
@@ -26,11 +25,11 @@ import org.apache.nifi.cluster.manager.HttpResponseMapper;
 import org.apache.nifi.cluster.manager.impl.WebClusterManager;
 import org.apache.nifi.cluster.protocol.impl.ClusterManagerProtocolSenderListener;
 import org.apache.nifi.cluster.protocol.impl.ClusterServicesBroadcaster;
-import org.apache.nifi.controller.service.ControllerServiceLoader;
 import org.apache.nifi.encrypt.StringEncryptor;
 import org.apache.nifi.io.socket.multicast.DiscoverableService;
 import org.apache.nifi.io.socket.multicast.DiscoverableServiceImpl;
 import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.OptimisticLockingManager;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.FactoryBean;
 import org.springframework.context.ApplicationContext;
@@ -50,6 +49,8 @@ public class WebClusterManagerFactoryBean implements FactoryBean, ApplicationCon
     private NiFiProperties properties;
 
     private StringEncryptor encryptor;
+    
+    private OptimisticLockingManager optimisticLockingManager;
 
     @Override
     public Object getObject() throws Exception {
@@ -62,13 +63,6 @@ public class WebClusterManagerFactoryBean implements FactoryBean, ApplicationCon
              */
             return null;
         } else if (clusterManager == null) {
-
-            // get the service configuration path (fail early)
-            final String serviceConfigurationFile = properties.getProperty(NiFiProperties.SERVICE_CONFIGURATION_FILE);
-            if (serviceConfigurationFile == null) {
-                throw new NullPointerException("The service configuration file has not been specified.");
-            }
-
             final HttpRequestReplicator requestReplicator = applicationContext.getBean("httpRequestReplicator", HttpRequestReplicator.class);
             final HttpResponseMapper responseMapper = applicationContext.getBean("httpResponseMapper", HttpResponseMapper.class);
             final DataFlowManagementService dataFlowService = applicationContext.getBean("dataFlowManagementService", DataFlowManagementService.class);
@@ -81,7 +75,8 @@ public class WebClusterManagerFactoryBean implements FactoryBean, ApplicationCon
                     dataFlowService,
                     senderListener,
                     properties,
-                    encryptor
+                    encryptor,
+                    optimisticLockingManager
             );
 
             // set the service broadcaster
@@ -106,10 +101,6 @@ public class WebClusterManagerFactoryBean implements FactoryBean, ApplicationCon
 
             // set the audit service
             clusterManager.setAuditService(applicationContext.getBean("auditService", AuditService.class));
-
-            // load the controller services
-            final ControllerServiceLoader serviceLoader = new ControllerServiceLoader(Paths.get(serviceConfigurationFile));
-            serviceLoader.loadControllerServices(clusterManager);
         }
         return clusterManager;
     }
@@ -136,4 +127,8 @@ public class WebClusterManagerFactoryBean implements FactoryBean, ApplicationCon
     public void setEncryptor(final StringEncryptor encryptor) {
         this.encryptor = encryptor;
     }
+    
+    public void setOptimisticLockingManager(OptimisticLockingManager optimisticLockingManager) {
+        this.optimisticLockingManager = optimisticLockingManager;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/resources/nifi-cluster-manager-context.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/resources/nifi-cluster-manager-context.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/resources/nifi-cluster-manager-context.xml
index 68c29bc..72c7bff 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/resources/nifi-cluster-manager-context.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/resources/nifi-cluster-manager-context.xml
@@ -91,10 +91,14 @@
         <property name="properties" ref="nifiProperties"/>
     </bean>
 
+    <!-- cluster manager optimistic locking manager -->
+    <bean id="clusterManagerOptimisticLockingManager" class="org.apache.nifi.web.StandardOptimisticLockingManager"/>
+
     <!-- cluster manager -->
     <bean id="clusterManager" class="org.apache.nifi.cluster.spring.WebClusterManagerFactoryBean">
         <property name="properties" ref="nifiProperties"/>
         <property name="encryptor" ref="stringEncryptor"/>
+        <property name="optimisticLockingManager" ref="clusterManagerOptimisticLockingManager"/>
     </bean>
     
     <!-- discoverable services -->

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/.gitignore
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/.gitignore b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/.gitignore
index ea8c4bf..29546b5 100755
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/.gitignore
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/.gitignore
@@ -1 +1,2 @@
 /target
+/target/

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/AbstractConfiguredComponent.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/AbstractConfiguredComponent.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/AbstractConfiguredComponent.java
index ef4b72a..c44161f 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/AbstractConfiguredComponent.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/AbstractConfiguredComponent.java
@@ -23,6 +23,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicReference;
@@ -149,6 +150,16 @@ public abstract class AbstractConfiguredComponent implements ConfigurableCompone
                 final PropertyDescriptor descriptor = component.getPropertyDescriptor(name);
                 String value = null;
                 if (!descriptor.isRequired() && (value = properties.remove(descriptor)) != null) {
+                	
+                	if ( descriptor.getControllerServiceDefinition() != null ) {
+                		if (value != null) {
+                            final ControllerServiceNode oldNode = serviceProvider.getControllerServiceNode(value);
+                            if (oldNode != null) {
+                                oldNode.removeReference(this);
+                            }
+                        }
+                	}
+                	
                     component.onPropertyModified(descriptor, value, null);
                     return true;
                 }
@@ -250,12 +261,17 @@ public abstract class AbstractConfiguredComponent implements ConfigurableCompone
         return true;
     }
 
+    
     @Override
     public Collection<ValidationResult> getValidationErrors() {
+        return getValidationErrors(Collections.<String>emptySet());
+    }
+    
+    public Collection<ValidationResult> getValidationErrors(final Set<String> serviceIdentifiersNotToValidate) {
         final List<ValidationResult> results = new ArrayList<>();
         lock.lock();
         try {
-            final ValidationContext validationContext = validationContextFactory.newValidationContext(getProperties(), getAnnotationData());
+            final ValidationContext validationContext = validationContextFactory.newValidationContext(serviceIdentifiersNotToValidate, getProperties(), getAnnotationData());
 
             final Collection<ValidationResult> validationResults;
             try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/Availability.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/Availability.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/Availability.java
deleted file mode 100644
index 38df6f7..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/Availability.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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.controller;
-
-public enum Availability {
-
-    CLUSTER_MANAGER_ONLY,
-    NODE_ONLY,
-    BOTH;
-}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessScheduler.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessScheduler.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessScheduler.java
index 303f540..c3b6613 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessScheduler.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessScheduler.java
@@ -19,8 +19,7 @@ package org.apache.nifi.controller;
 import org.apache.nifi.connectable.Connectable;
 import org.apache.nifi.connectable.Funnel;
 import org.apache.nifi.connectable.Port;
-import org.apache.nifi.processor.annotation.OnScheduled;
-import org.apache.nifi.processor.annotation.OnUnscheduled;
+import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.scheduling.SchedulingStrategy;
 
 public interface ProcessScheduler {
@@ -143,4 +142,28 @@ public interface ProcessScheduler {
      * @param procNode
      */
     void yield(ProcessorNode procNode);
+    
+    /**
+     * Stops scheduling the given Reporting Task to run
+     * @param taskNode
+     */
+    void unschedule(ReportingTaskNode taskNode);
+    
+    /**
+     * Begins scheduling the given Reporting Task to run
+     * @param taskNode
+     */
+    void schedule(ReportingTaskNode taskNode);
+    
+    /**
+     * Enables the Controller Service so that it can be used by Reporting Tasks and Processors
+     * @param service
+     */
+    void enableControllerService(ControllerServiceNode service);
+    
+    /**
+     * Disables the Controller Service so that it can be updated
+     * @param service
+     */
+    void disableControllerService(ControllerServiceNode service);
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessorNode.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessorNode.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessorNode.java
index f6786fa..3189edd 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessorNode.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessorNode.java
@@ -21,6 +21,7 @@ import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.nifi.connectable.Connectable;
+import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.controller.service.ControllerServiceProvider;
 import org.apache.nifi.logging.LogLevel;
 import org.apache.nifi.processor.Processor;
@@ -77,4 +78,19 @@ public abstract class ProcessorNode extends AbstractConfiguredComponent implemen
 
     public abstract void setStyle(Map<String, String> style);
 
+    /**
+     * Returns the number of threads (concurrent tasks) currently being used by this Processor
+     * @return
+     */
+    public abstract int getActiveThreadCount();
+    
+    /**
+     * Verifies that this Processor can be started if the provided set of
+     * services are enabled. This is introduced because we need to verify that all components
+     * can be started before starting any of them. In order to do that, we need to know that this
+     * component can be started if the given services are enabled, as we will then enable the given 
+     * services before starting this component.
+     * @param ignoredReferences
+     */
+    public abstract void verifyCanStart(Set<ControllerServiceNode> ignoredReferences);
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ReportingTaskNode.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ReportingTaskNode.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ReportingTaskNode.java
index fa48cb3..c932f30 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ReportingTaskNode.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ReportingTaskNode.java
@@ -16,18 +16,16 @@
  */
 package org.apache.nifi.controller;
 
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.reporting.ReportingContext;
 import org.apache.nifi.reporting.ReportingTask;
 import org.apache.nifi.scheduling.SchedulingStrategy;
 
 public interface ReportingTaskNode extends ConfiguredComponent {
 
-    Availability getAvailability();
-
-    void setAvailability(Availability availability);
-
     void setSchedulingStrategy(SchedulingStrategy schedulingStrategy);
 
     SchedulingStrategy getSchedulingStrategy();
@@ -53,6 +51,12 @@ public interface ReportingTaskNode extends ConfiguredComponent {
     ConfigurationContext getConfigurationContext();
 
     boolean isRunning();
+
+    /**
+     * Returns the number of threads (concurrent tasks) currently being used by this ReportingTask
+     * @return
+     */
+    int getActiveThreadCount();
     
     /**
      * Indicates the {@link ScheduledState} of this <code>ReportingTask</code>. A
@@ -68,6 +72,20 @@ public interface ReportingTaskNode extends ConfiguredComponent {
     
     void setScheduledState(ScheduledState state);
     
+    String getComments();
+    
+    void setComments(String comment);
+    
+    /**
+     * Verifies that this Reporting Task can be enabled if the provided set of
+     * services are enabled. This is introduced because we need to verify that all components
+     * can be started before starting any of them. In order to do that, we need to know that this
+     * component can be started if the given services are enabled, as we will then enable the given 
+     * services before starting this component.
+     * @param ignoredReferences
+     */
+    void verifyCanStart(Set<ControllerServiceNode> ignoredReferences);
+    
     void verifyCanStart();
     void verifyCanStop();
     void verifyCanDisable();


[36/62] [abbrv] incubator-nifi git commit: Merge branch 'develop' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into develop

Posted by ma...@apache.org.
Merge branch 'develop' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into develop


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/4f015f5f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/4f015f5f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/4f015f5f

Branch: refs/heads/NIFI-25
Commit: 4f015f5f435c9cf4d27c522e2363c5b2d338525c
Parents: e79db82 3a2b12d
Author: Matt Gilman <ma...@gmail.com>
Authored: Tue Apr 7 10:14:09 2015 -0400
Committer: Matt Gilman <ma...@gmail.com>
Committed: Tue Apr 7 10:14:09 2015 -0400

----------------------------------------------------------------------
 .../nifi/controller/scheduling/StandardProcessScheduler.java     | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
----------------------------------------------------------------------



[07/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
index 2886f21..626ba34 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 $(document).ready(function () {
     if (nf.Canvas.SUPPORTS_SVG) {
         // initialize the NiFi
@@ -179,12 +182,31 @@ nf.Canvas = (function () {
                 // changes that need to be updated
                 if (revision.version > currentRevision.version && revision.clientId !== currentRevision.clientId) {
                     var refreshContainer = $('#refresh-required-container');
+                    var settingsRefreshIcon = $('#settings-refresh-required-icon');
 
-                    // insert the refresh needed text - if necessary
+                    // insert the refresh needed text in the canvas - if necessary
                     if (!refreshContainer.is(':visible')) {
                         $('#stats-last-refreshed').addClass('alert');
+                        var refreshMessage = "This flow has been modified by '" + revision.lastModifier + "'. Please refresh.";
+                        
+                        // update the tooltip
+                        var refreshRequiredIcon = $('#refresh-required-icon');
+                        if (refreshRequiredIcon.data('qtip')) {
+                            refreshRequiredIcon.qtip('option', 'content.text', refreshMessage);
+                        } else {
+                            refreshRequiredIcon.qtip($.extend({
+                                content: refreshMessage
+                            }, nf.CanvasUtils.config.systemTooltipConfig));
+                        }
+                    
                         refreshContainer.show();
                     }
+                    
+                    // insert the refresh needed text in the settings - if necessary
+                    if (!settingsRefreshIcon.is(':visible')) {
+                        $('#settings-last-refreshed').addClass('alert');
+                        settingsRefreshIcon.show();
+                    }
                 }
             }
         }).fail(nf.Common.handleAjaxError);
@@ -509,15 +531,25 @@ nf.Canvas = (function () {
         // listen for browser resize events to reset the graph size
         $(window).on('resize', function () {
             updateGraphSize();
+            nf.Settings.resetTableSize();
         }).on('keydown', function (evt) {
             var isCtrl = evt.ctrlKey || evt.metaKey;
             
             // consider escape, before checking dialogs
             if (!isCtrl && evt.keyCode === 27) {
                 // esc
-                nf.Actions.hideDialogs();
+                var target = $(evt.target);
+                if (target.length) {
+                    if (target.get(0) === $('#canvas-body').get(0)) {
+                        nf.Actions.hideDialogs();
+                    } else {
+                        target.closest('.cancellable.dialog:visible').modal('hide');
+                    }
+                    
+                    evt.stopPropagation();
+                    evt.preventDefault();
+                }
 
-                evt.preventDefault();
                 return;
             }
             
@@ -876,7 +908,8 @@ nf.Canvas = (function () {
                 // get the process group to refresh everything
                 var processGroupXhr = reloadProcessGroup(nf.Canvas.getGroupId());
                 var statusXhr = reloadFlowStatus();
-                $.when(processGroupXhr, statusXhr).done(function (processGroupResult) {
+                var settingsXhr = nf.Settings.loadSettings();
+                $.when(processGroupXhr, statusXhr, settingsXhr).done(function (processGroupResult) {
                     // adjust breadcrumbs if necessary
                     var title = $('#data-flow-title-container');
                     var titlePosition = title.position();
@@ -1011,6 +1044,8 @@ nf.Canvas = (function () {
 
                         // initialize components
                         nf.ConnectionConfiguration.init();
+                        nf.ControllerService.init();
+                        nf.ReportingTask.init();
                         nf.ProcessorConfiguration.init();
                         nf.ProcessGroupConfiguration.init();
                         nf.RemoteProcessGroupConfiguration.init();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-clipboard.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-clipboard.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-clipboard.js
index 4f22d7e..ce0ea1d 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-clipboard.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-clipboard.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 /**
  * Clipboard used for copying and pasting content.
  */

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connectable.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connectable.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connectable.js
index 24f0f45..154c14b 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connectable.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connectable.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.Connectable = (function () {
 
     var connect;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js
index fd70aa1..cc246cf 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection-configuration.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.ConnectionConfiguration = (function () {
 
     var CONNECTION_OFFSET_Y_INCREMENT = 75;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js
index 8da9f6a..c8a96c0 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.Connection = (function () {
 
     // the dimensions for the connection label

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js
index a60d714..2d0a41d 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 nf.ContextMenu = (function () {
 
     /**


[56/62] [abbrv] incubator-nifi git commit: NIFI-74, NIFI-345, NIFI-495: Fixed several site-to-site related bugs

Posted by ma...@apache.org.
NIFI-74, NIFI-345, NIFI-495: Fixed several site-to-site related bugs


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/b682b6fa
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/b682b6fa
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/b682b6fa

Branch: refs/heads/NIFI-25
Commit: b682b6fab543cabeb3a321d6e5cf22f7ce9968c1
Parents: e9cb3b3
Author: Mark Payne <ma...@hotmail.com>
Authored: Thu Apr 9 17:59:02 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Thu Apr 9 17:59:02 2015 -0400

----------------------------------------------------------------------
 .../client/socket/EndpointConnectionPool.java   | 43 ++++++++++++++++----
 .../nifi/remote/client/socket/SocketClient.java |  3 +-
 .../protocol/socket/SocketClientProtocol.java   |  8 +++-
 .../socket/SocketClientTransaction.java         | 25 ++++++++----
 .../nifi/remote/StandardRemoteGroupPort.java    |  1 +
 5 files changed, 61 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/b682b6fa/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java b/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java
index 1a6dfd5..1b5412c 100644
--- a/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java
+++ b/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java
@@ -88,6 +88,7 @@ import org.apache.nifi.web.api.dto.ControllerDTO;
 import org.apache.nifi.web.api.dto.PortDTO;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.slf4j.helpers.MessageFormatter;
 
 public class EndpointConnectionPool {
     public static final long PEER_REFRESH_PERIOD = 60000L;
@@ -202,6 +203,28 @@ public class EndpointConnectionPool {
     	}, 5, 5, TimeUnit.SECONDS);
     }
     
+    void warn(final String msg, final Object... args) {
+    	logger.warn(msg, args);
+    	if ( eventReporter != null ) {
+    		eventReporter.reportEvent(Severity.WARNING, "Site-to-Site", MessageFormatter.arrayFormat(msg, args).getMessage());
+    	}
+    }
+    
+    void warn(final String msg, final Throwable t) {
+    	logger.warn(msg, t);
+    	
+    	if ( eventReporter != null ) {
+    		eventReporter.reportEvent(Severity.WARNING, "Site-to-Site", msg + ": " + t.toString());
+    	}
+    }
+    
+    void error(final String msg, final Object... args) {
+    	logger.error(msg, args);
+    	if ( eventReporter != null ) {
+    		eventReporter.reportEvent(Severity.ERROR, "Site-to-Site", MessageFormatter.arrayFormat(msg, args).getMessage());
+    	}
+    }
+    
     private String getPortIdentifier(final TransferDirection transferDirection) throws IOException {
         if ( remoteDestination.getIdentifier() != null ) {
             return remoteDestination.getIdentifier();
@@ -271,6 +294,7 @@ public class EndpointConnectionPool {
                     logger.debug("{} No Connection available for Port {}; creating new Connection", this, portId);
                     protocol = new SocketClientProtocol();
                     protocol.setDestination(new IdEnrichedRemoteDestination(remoteDestination, portId));
+                    protocol.setEventReporter(eventReporter);
 
                     final long penalizationMillis = remoteDestination.getYieldPeriod(TimeUnit.MILLISECONDS);
                     try {
@@ -312,7 +336,9 @@ public class EndpointConnectionPool {
                         
                         // handle error cases
                         if ( protocol.isDestinationFull() ) {
-                            logger.warn("{} {} indicates that port's destination is full; penalizing peer", this, peer);
+                            logger.warn("{} {} indicates that port {}'s destination is full; penalizing peer", 
+                            		this, peer, config.getPortName() == null ? config.getPortIdentifier() : config.getPortName());
+                            
                             penalize(peer, penalizationMillis);
                             try {
                             	peer.close();
@@ -341,7 +367,7 @@ public class EndpointConnectionPool {
                         cleanup(protocol, peer);
                         
                         final String message = String.format("%s failed to communicate with %s due to %s", this, peer == null ? clusterUrl : peer, e.toString());
-                        logger.error(message);
+                        error(message);
                         if ( logger.isDebugEnabled() ) {
                             logger.error("", e);
                         }
@@ -463,7 +489,7 @@ public class EndpointConnectionPool {
                         peerList = createPeerStatusList(direction);
                     } catch (final Exception e) {
                         final String message = String.format("%s Failed to update list of peers due to %s", this, e.toString());
-                        logger.warn(message);
+                        warn(message);
                         if ( logger.isDebugEnabled() ) {
                             logger.warn("", e);
                         }
@@ -503,7 +529,7 @@ public class EndpointConnectionPool {
     }
     
     private boolean isPenalized(final PeerStatus peerStatus) {
-        final Long expirationEnd = peerTimeoutExpirations.get(peerStatus);
+        final Long expirationEnd = peerTimeoutExpirations.get(peerStatus.getPeerDescription());
         return (expirationEnd == null ? false : expirationEnd > System.currentTimeMillis() );
     }
     
@@ -587,7 +613,7 @@ public class EndpointConnectionPool {
             clientProtocol.shutdown(peer);
         } catch (final IOException e) {
             final String message = String.format("%s Failed to shutdown protocol when updating list of peers due to %s", this, e.toString());
-            logger.warn(message);
+            warn(message);
             if (logger.isDebugEnabled()) {
                 logger.warn("", e);
             }
@@ -597,7 +623,7 @@ public class EndpointConnectionPool {
             peer.close();
         } catch (final IOException e) {
             final String message = String.format("%s Failed to close resources when updating list of peers due to %s", this, e.toString());
-            logger.warn(message);
+            warn(message);
             if (logger.isDebugEnabled()) {
                 logger.warn("", e);
             }
@@ -622,7 +648,8 @@ public class EndpointConnectionPool {
             }
 
         } catch (final IOException e) {
-            logger.error("Failed to persist list of Peers due to {}; if restarted and peer's NCM is down, may be unable to transfer data until communications with NCM are restored", e.toString(), e);
+            error("Failed to persist list of Peers due to {}; if restarted and peer's NCM is down, may be unable to transfer data until communications with NCM are restored", e.toString());
+            logger.error("", e);
         }
     }
 
@@ -818,7 +845,7 @@ public class EndpointConnectionPool {
             peerStatusCache = new PeerStatusCache(statuses);
             logger.info("{} Successfully refreshed Peer Status; remote instance consists of {} peers", this, statuses.size());
         } catch (Exception e) {
-            logger.warn("{} Unable to refresh Remote Group's peers due to {}", this, e);
+            warn("{} Unable to refresh Remote Group's peers due to {}", this, e);
             if (logger.isDebugEnabled()) {
                 logger.warn("", e);
             }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/b682b6fa/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/SocketClient.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/SocketClient.java b/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/SocketClient.java
index ed54ccb..4aab3f7 100644
--- a/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/SocketClient.java
+++ b/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/SocketClient.java
@@ -84,6 +84,7 @@ public class SocketClient implements SiteToSiteClient {
 		    logger.debug("Unable to resolve port [{}] to an identifier", portName);
 		} else {
 		    logger.debug("Resolved port [{}] to identifier [{}]", portName, portId);
+		    this.portIdentifier = portId;
 		}
 		
 		return portId;
@@ -136,7 +137,7 @@ public class SocketClient implements SiteToSiteClient {
 				connectionState.getPeer(), connectionState.getCodec(), direction);
 		} catch (final Throwable t) {
 			pool.terminate(connectionState);
-			throw t;
+			throw new IOException("Unable to create Transaction to communicate with " + connectionState.getPeer(), t);
 		}
 		
 		// Wrap the transaction in a new one that will return the EndpointConnectionState back to the pool whenever

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/b682b6fa/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/protocol/socket/SocketClientProtocol.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/protocol/socket/SocketClientProtocol.java b/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/protocol/socket/SocketClientProtocol.java
index c3275ea..83c5305 100644
--- a/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/protocol/socket/SocketClientProtocol.java
+++ b/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/protocol/socket/SocketClientProtocol.java
@@ -27,6 +27,7 @@ import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.nifi.events.EventReporter;
 import org.apache.nifi.flowfile.FlowFile;
 import org.apache.nifi.flowfile.attributes.CoreAttributes;
 import org.apache.nifi.processor.ProcessContext;
@@ -75,6 +76,7 @@ public class SocketClientProtocol implements ClientProtocol {
     private int batchCount;
     private long batchSize;
     private long batchMillis;
+    private EventReporter eventReporter;
 
     private static final long BATCH_SEND_NANOS = TimeUnit.SECONDS.toNanos(5L); // send batches of up to 5 seconds
     
@@ -93,6 +95,10 @@ public class SocketClientProtocol implements ClientProtocol {
         this.batchMillis = millis;
     }
     
+    public void setEventReporter(final EventReporter eventReporter) {
+    	this.eventReporter = eventReporter;
+    }
+    
     public void setDestination(final RemoteDestination destination) {
         this.destination = destination;
         this.useCompression = destination.isUseCompression();
@@ -272,7 +278,7 @@ public class SocketClientProtocol implements ClientProtocol {
         }
         
         return new SocketClientTransaction(versionNegotiator.getVersion(), destination.getIdentifier(), peer, codec, 
-        		direction, useCompression, (int) destination.getYieldPeriod(TimeUnit.MILLISECONDS));
+        		direction, useCompression, (int) destination.getYieldPeriod(TimeUnit.MILLISECONDS), eventReporter);
     }
 
 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/b682b6fa/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/protocol/socket/SocketClientTransaction.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/protocol/socket/SocketClientTransaction.java b/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/protocol/socket/SocketClientTransaction.java
index a1ce07e..e69104f 100644
--- a/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/protocol/socket/SocketClientTransaction.java
+++ b/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/protocol/socket/SocketClientTransaction.java
@@ -27,6 +27,7 @@ import java.util.zip.CRC32;
 import java.util.zip.CheckedInputStream;
 import java.util.zip.CheckedOutputStream;
 
+import org.apache.nifi.events.EventReporter;
 import org.apache.nifi.remote.Communicant;
 import org.apache.nifi.remote.Peer;
 import org.apache.nifi.remote.Transaction;
@@ -39,6 +40,7 @@ import org.apache.nifi.remote.io.CompressionOutputStream;
 import org.apache.nifi.remote.protocol.DataPacket;
 import org.apache.nifi.remote.protocol.RequestType;
 import org.apache.nifi.remote.util.StandardDataPacket;
+import org.apache.nifi.reporting.Severity;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -56,6 +58,7 @@ public class SocketClientTransaction implements Transaction {
 	private final Peer peer;
 	private final int penaltyMillis;
 	private final String destinationId;
+	private final EventReporter eventReporter;
 	
 	private boolean dataAvailable = false;
 	private int transfers = 0;
@@ -63,7 +66,7 @@ public class SocketClientTransaction implements Transaction {
 	private TransactionState state;
 	
 	SocketClientTransaction(final int protocolVersion, final String destinationId, final Peer peer, final FlowFileCodec codec, 
-			final TransferDirection direction, final boolean useCompression, final int penaltyMillis) throws IOException {
+			final TransferDirection direction, final boolean useCompression, final int penaltyMillis, final EventReporter eventReporter) throws IOException {
 		this.protocolVersion = protocolVersion;
 		this.destinationId = destinationId;
 		this.peer = peer;
@@ -74,6 +77,7 @@ public class SocketClientTransaction implements Transaction {
 		this.compress = useCompression;
 		this.state = TransactionState.TRANSACTION_STARTED;
 		this.penaltyMillis = penaltyMillis;
+		this.eventReporter = eventReporter;
 		
 		initialize();
 	}
@@ -116,11 +120,11 @@ public class SocketClientTransaction implements Transaction {
 	    try {
 	        try {
         		if ( state != TransactionState.DATA_EXCHANGED && state != TransactionState.TRANSACTION_STARTED) {
-        			throw new IllegalStateException("Cannot receive data because Transaction State is " + state);
+        			throw new IllegalStateException("Cannot receive data from " + peer + " because Transaction State is " + state);
         		}
         		
             	if ( direction == TransferDirection.SEND ) {
-            	    throw new IllegalStateException("Attempting to receive data but started a SEND Transaction");
+            	    throw new IllegalStateException("Attempting to receive data from " + peer + " but started a SEND Transaction");
             	}
             	
             	// if we already know there's no data, just return null
@@ -142,7 +146,7 @@ public class SocketClientTransaction implements Transaction {
                             this.dataAvailable = false;
                             break;
                         default:
-                            throw new ProtocolException("Got unexpected response when asking for data: " + dataAvailableCode);
+                            throw new ProtocolException("Got unexpected response from " + peer + " when asking for data: " + dataAvailableCode);
                     }
                 }
             	
@@ -184,11 +188,11 @@ public class SocketClientTransaction implements Transaction {
 	    try {
 	        try {
         		if ( state != TransactionState.DATA_EXCHANGED && state != TransactionState.TRANSACTION_STARTED) {
-        			throw new IllegalStateException("Cannot send data because Transaction State is " + state);
+        			throw new IllegalStateException("Cannot send data to " + peer + " because Transaction State is " + state);
         		}
         
                 if ( direction == TransferDirection.RECEIVE ) {
-                    throw new IllegalStateException("Attempting to send data but started a RECEIVE Transaction");
+                    throw new IllegalStateException("Attempting to send data to " + peer + " but started a RECEIVE Transaction");
                 }
         
         		if ( transfers > 0 ) {
@@ -242,7 +246,7 @@ public class SocketClientTransaction implements Transaction {
 	    try {
 	        try {
         		if ( state != TransactionState.TRANSACTION_CONFIRMED ) {
-        			throw new IllegalStateException("Cannot complete transaction because state is " + state + 
+        			throw new IllegalStateException("Cannot complete transaction with " + peer + " because state is " + state + 
         					"; Transaction can only be completed when state is " + TransactionState.TRANSACTION_CONFIRMED);
         		}
         		
@@ -272,7 +276,7 @@ public class SocketClientTransaction implements Transaction {
                         peer.penalize(destinationId, penaltyMillis);
                         backoff = true;
                     } else if ( transactionResponse.getCode() != ResponseCode.TRANSACTION_FINISHED ) {
-                        throw new ProtocolException("After sending data, expected TRANSACTION_FINISHED response but got " + transactionResponse);
+                        throw new ProtocolException("After sending data to " + peer + ", expected TRANSACTION_FINISHED response but got " + transactionResponse);
                     }
                     
                     state = TransactionState.TRANSACTION_COMPLETED;
@@ -324,7 +328,10 @@ public class SocketClientTransaction implements Transaction {
                     try {
                         confirmTransactionResponse = Response.read(dis);
                     } catch (final IOException ioe) {
-                        logger.error("Failed to receive response code from {} when expected confirmation of transaction", peer);
+                        logger.error("Failed to receive response code from {} when expecting confirmation of transaction", peer);
+                        if ( eventReporter != null ) {
+                        	eventReporter.reportEvent(Severity.ERROR, "Site-to-Site", "Failed to receive response code from " + peer + " when expecting confirmation of transaction");
+                        }
                         throw ioe;
                     }
                     

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/b682b6fa/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/StandardRemoteGroupPort.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/StandardRemoteGroupPort.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/StandardRemoteGroupPort.java
index 69ba0fd..eec6ed5 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/StandardRemoteGroupPort.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/StandardRemoteGroupPort.java
@@ -183,6 +183,7 @@ public class StandardRemoteGroupPort extends RemoteGroupPort {
             remoteGroup.getEventReporter().reportEvent(Severity.ERROR, CATEGORY, message);
             return;
         } catch (final IOException e) {
+        	context.yield();
             final String message = String.format("%s failed to communicate with %s due to %s", this, url, e.toString());
             logger.error(message);
             if ( logger.isDebugEnabled() ) {


[44/62] [abbrv] incubator-nifi git commit: NIFI-475: - Adding support to create controller services inline when editing a components properties.

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2154b822/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
index 0e60b2c..c7b2b65 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
@@ -168,34 +168,6 @@ nf.Settings = (function () {
     };
 
     /**
-     * Determines if all of the ancestors of the specified item are expanded.
-     * 
-     * @param {type} item
-     * @returns {Boolean}
-     */
-    var areAncestorsExpanded = function (item) {
-        var documentedType = item;
-        while (documentedType.parent !== null) {
-            if (documentedType.parent.collapsed === true) {
-                return false;
-            }
-            documentedType = documentedType.parent;
-        }
-
-        return true;
-    };
-
-    /**
-     * Determines if the specified item is an ancestor.
-     * 
-     * @param {type} item
-     * @returns {Boolean}
-     */
-    var isAncestor = function (item) {
-        return item.children.length > 0;
-    };
-
-    /**
      * Hides the selected controller service.
      */
     var clearSelectedControllerService = function () {
@@ -232,27 +204,6 @@ nf.Settings = (function () {
      * @returns {Boolean}       Whether or not to include the item
      */
     var filterControllerServiceTypes = function (item, args) {
-        if (!areAncestorsExpanded(item)) {
-            // if this item is currently selected and its parent is not collapsed
-            if ($('#selected-controller-service-type').text() === item['type']) {
-                clearControllerServiceSelection();
-            }
-
-            // update visibility flag
-            item.visible = false;
-
-            return false;
-        }
-
-        // don't allow ancestors to be filtered out (unless any of their ancestors
-        // are collapsed)
-        if (isAncestor(item)) {
-            // update visibility flag
-            item.visible = false;
-
-            return true;
-        }
-
         // determine if the item matches the filter
         var matchesFilter = matchesRegex(item, args);
 
@@ -274,9 +225,6 @@ nf.Settings = (function () {
             clearControllerServiceSelection();
         }
 
-        // update visibility flag
-        item.visible = matches;
-
         return matches;
     };
 
@@ -331,64 +279,6 @@ nf.Settings = (function () {
     };
 
     /**
-     * Formats the type by introducing expand/collapse where appropriate.
-     * 
-     * @param {type} row
-     * @param {type} cell
-     * @param {type} value
-     * @param {type} columnDef
-     * @param {type} dataContext
-     */
-    var expandableTypeFormatter = function (row, cell, value, columnDef, dataContext) {
-        var markup = '';
-
-        var indent = 0;
-        var documentedType = dataContext;
-        while (documentedType.parent !== null) {
-            indent += 20;
-            documentedType = documentedType.parent;
-        }
-
-        var padding = 3;
-
-        // create the markup for the row
-        if (dataContext.children.length > 0) {
-            // determine how to render the expansion button
-            var expansionStyle = 'expanded';
-            if (dataContext.collapsed === true) {
-                expansionStyle = 'collapsed';
-            }
-
-            // calculate the number of visible/total children
-            var visibleChildren = 0;
-            var totalChildren = 0;
-            var countChildren = function (item) {
-                $.each(item.children, function (_, child) {
-                    if (child.children.length > 0) {
-                        countChildren(child);
-                    } else {
-                        if (child.visible) {
-                            visibleChildren++;
-                        }
-                        totalChildren++;
-                    }
-                });
-            };
-            countChildren(dataContext);
-
-            markup += ('<span style="margin-top: 5px; margin-left: ' + indent + 'px;" class="expansion-button ' + expansionStyle + '"></span><span class="ancestor-type" style="margin-left: ' + padding + 'px;">' + value + '</span><span class="ancestor-type-rollup">(' + visibleChildren + ' of ' + totalChildren + ')</span>');
-        } else {
-            if (dataContext.parent === null) {
-                padding = 0;
-            }
-
-            markup += ('<span style="margin-left: ' + (indent + padding) + 'px;">' + value + '</span>');
-        }
-
-        return markup;
-    };
-
-    /**
      * Adds a new controller service of the specified type.
      * 
      * @param {string} controllerServiceType
@@ -431,6 +321,7 @@ nf.Settings = (function () {
             // select the new controller service
             var row = controllerServicesData.getRowById(controllerService.id);
             controllerServicesGrid.setSelectedRows([row]);
+            controllerServicesGrid.scrollRowIntoView(row);
         }).fail(nf.Common.handleAjaxError);
 
         // hide the dialog
@@ -488,7 +379,7 @@ nf.Settings = (function () {
 
         // initialize the processor type table
         var controllerServiceTypesColumns = [
-            {id: 'type', name: 'Type', field: 'label', formatter: expandableTypeFormatter, sortable: false, resizable: true},
+            {id: 'type', name: 'Type', field: 'label', sortable: false, resizable: true},
             {id: 'tags', name: 'Tags', field: 'tags', sortable: false, resizable: true}
         ];
 
@@ -502,32 +393,6 @@ nf.Settings = (function () {
             property: $('#controller-service-type-filter-options').combo('getSelectedOption').value
         });
         controllerServiceTypesData.setFilter(filterControllerServiceTypes);
-        controllerServiceTypesData.getItemMetadata = function (index) {
-            var item = controllerServiceTypesData.getItem(index);
-            if (item && item.children.length > 0) {
-                return {
-                    selectable: false,
-                    columns: {
-                        0: {
-                            colspan: '*'
-                        }
-                    }
-                };
-            } else {
-                return {};
-            }
-        };
-
-        var getVisibleControllerServiceCount = function () {
-            var count = 0;
-            for (var i = 0; i < controllerServiceTypesData.getLength(); i++) {
-                var item = controllerServiceTypesData.getItem(i);
-                if (item.children.length === 0) {
-                    count++;
-                }
-            }
-            return count;
-        };
 
         // initialize the grid
         var controllerServiceTypesGrid = new Slick.Grid('#controller-service-types-table', controllerServiceTypesData, controllerServiceTypesColumns, gridOptions);
@@ -539,41 +404,20 @@ nf.Settings = (function () {
                 var controllerServiceTypeIndex = args.rows[0];
                 var controllerServiceType = controllerServiceTypesGrid.getDataItem(controllerServiceTypeIndex);
 
-                // only allow selection of service implementations
-                if (controllerServiceType.children.length === 0) {
-                    // set the controller service type description
-                    if (nf.Common.isBlank(controllerServiceType.description)) {
-                        $('#controller-service-type-description').attr('title', '').html('<span class="unset">No description specified</span>');
-                    } else {
-                        $('#controller-service-type-description').html(controllerServiceType.description).ellipsis();
-                    }
-
-                    // populate the dom
-                    $('#controller-service-type-name').text(controllerServiceType.label).ellipsis();
-                    $('#selected-controller-service-name').text(controllerServiceType.label);
-                    $('#selected-controller-service-type').text(controllerServiceType.type);
-
-                    // show the selected controller service
-                    $('#controller-service-description-container').show();
-                }
-            }
-        });
-        controllerServiceTypesGrid.onClick.subscribe(function (e, args) {
-            var item = controllerServiceTypesData.getItem(args.row);
-            if (item && item.children.length > 0) {
-                // update the grid
-                item.collapsed = !item.collapsed;
-                controllerServiceTypesData.updateItem(item.id, item);
-
-                // update any affected ancestors
-                var parent = item.parent;
-                while (parent !== null) {
-                    controllerServiceTypesData.updateItem(parent.id, parent);
-                    parent = parent.parent;
+                // set the controller service type description
+                if (nf.Common.isBlank(controllerServiceType.description)) {
+                    $('#controller-service-type-description').attr('title', '').html('<span class="unset">No description specified</span>');
+                } else {
+                    $('#controller-service-type-description').html(controllerServiceType.description).ellipsis();
                 }
 
-                // prevent selection within slickgrid
-                e.stopImmediatePropagation();
+                // populate the dom
+                $('#controller-service-type-name').text(controllerServiceType.label).ellipsis();
+                $('#selected-controller-service-name').text(controllerServiceType.label);
+                $('#selected-controller-service-type').text(controllerServiceType.type);
+
+                // show the selected controller service
+                $('#controller-service-description-container').show();
             }
         });
         controllerServiceTypesGrid.onDblClick.subscribe(function (e, args) {
@@ -587,7 +431,7 @@ nf.Settings = (function () {
             controllerServiceTypesGrid.render();
 
             // update the total number of displayed processors
-            $('#displayed-controller-service-types').text(getVisibleControllerServiceCount());
+            $('#displayed-controller-service-types').text(args.current);
         });
         controllerServiceTypesData.onRowsChanged.subscribe(function (e, args) {
             controllerServiceTypesGrid.invalidateRows(args.rows);
@@ -610,46 +454,28 @@ nf.Settings = (function () {
             // begin the update
             controllerServiceTypesData.beginUpdate();
 
-            var addType = function (parentItem, documentedType) {
-                var item = {
+            // go through each controller service type
+            $.each(response.controllerServiceTypes, function (i, documentedType) {
+                // add the documented type
+                controllerServiceTypesData.addItem({
                     id: id++,
                     label: nf.Common.substringAfterLast(documentedType.type, '.'),
                     type: documentedType.type,
                     description: nf.Common.escapeHtml(documentedType.description),
-                    tags: documentedType.tags.join(', '),
-                    parent: parentItem,
-                    children: [],
-                    collapsed: false,
-                    visible: true
-                };
-
-                // add the documented type
-                controllerServiceTypesData.addItem(item);
+                    tags: documentedType.tags.join(', ')
+                });
 
                 // count the frequency of each tag for this type
                 $.each(documentedType.tags, function (i, tag) {
                     tags.push(tag.toLowerCase());
                 });
-
-                // add each of its children
-                $.each(documentedType.childTypes, function (_, documentedChildType) {
-                    var childItem = addType(item, documentedChildType);
-                    item.children.push(childItem);
-                });
-
-                return item;
-            };
-
-            // go through each controller service type
-            $.each(response.controllerServiceTypes, function (i, documentedType) {
-                addType(null, documentedType);
             });
 
             // end the udpate
             controllerServiceTypesData.endUpdate();
             
             // set the total number of processors
-            $('#total-controller-service-types, #displayed-controller-service-types').text(getVisibleControllerServiceCount());
+            $('#total-controller-service-types, #displayed-controller-service-types').text(response.controllerServiceTypes.length);
 
             // create the tag cloud
             $('#controller-service-tag-cloud').tagcloud({
@@ -1098,9 +924,6 @@ nf.Settings = (function () {
             clearReportingTaskSelection();
         }
 
-        // update visibility flag
-        item.visible = matches;
-
         return matches;
     };
 
@@ -1147,6 +970,7 @@ nf.Settings = (function () {
             // select the new reporting task
             var row = reportingTaskData.getRowById(reportingTask.id);
             reportingTaskGrid.setSelectedRows([row]);
+            reportingTaskGrid.scrollRowIntoView(row);
         }).fail(nf.Common.handleAjaxError);
 
         // hide the dialog
@@ -1287,10 +1111,7 @@ nf.Settings = (function () {
                     label: nf.Common.substringAfterLast(documentedType.type, '.'),
                     type: documentedType.type,
                     description: nf.Common.escapeHtml(documentedType.description),
-                    tags: documentedType.tags.join(', '),
-                    children: [],
-                    collapsed: false,
-                    visible: true
+                    tags: documentedType.tags.join(', ')
                 });
 
                 // count the frequency of each tag for this type


[48/62] [abbrv] incubator-nifi git commit: NIFI-496: Only update the updatedAttributes map if a value is changed

Posted by ma...@apache.org.
NIFI-496: Only update the updatedAttributes map if a value is changed


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/e8fde859
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/e8fde859
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/e8fde859

Branch: refs/heads/NIFI-25
Commit: e8fde859141e55fd13a1eaae77f4705e61f6071d
Parents: abd279c
Author: Mark Payne <ma...@hotmail.com>
Authored: Thu Apr 9 09:31:20 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Thu Apr 9 09:31:20 2015 -0400

----------------------------------------------------------------------
 .../repository/StandardRepositoryRecord.java         | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e8fde859/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/repository/StandardRepositoryRecord.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/repository/StandardRepositoryRecord.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/repository/StandardRepositoryRecord.java
index 1fde9aa..6ecb991 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/repository/StandardRepositoryRecord.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/repository/StandardRepositoryRecord.java
@@ -116,12 +116,23 @@ public class StandardRepositoryRecord implements RepositoryRecord {
 
     public void setWorking(final FlowFileRecord flowFile, final String attributeKey, final String attributeValue) {
         workingFlowFileRecord = flowFile;
-        updatedAttributes.put(attributeKey, attributeValue);
+        
+        // If setting attribute to same value as original, don't add to updated attributes
+        final String currentValue = originalAttributes.get(attributeKey);
+        if ( currentValue == null || !currentValue.equals(attributeValue) ) {
+        	updatedAttributes.put(attributeKey, attributeValue);
+        }
     }
 
     public void setWorking(final FlowFileRecord flowFile, final Map<String, String> updatedAttribs) {
         workingFlowFileRecord = flowFile;
-        updatedAttributes.putAll(updatedAttribs);
+        
+        for ( final Map.Entry<String, String> entry : updatedAttribs.entrySet() ) {
+        	final String currentValue = originalAttributes.get(entry.getKey());
+        	if ( currentValue == null || !currentValue.equals(entry.getValue()) ) {
+        		updatedAttributes.put(entry.getKey(), entry.getValue());
+        	}
+        }
     }
 
     @Override


[37/62] [abbrv] incubator-nifi git commit: NIFI-489: - Using the underlying instance in the base class instead of the proxy.

Posted by ma...@apache.org.
NIFI-489:
- Using the underlying instance in the base class instead of the proxy.

Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/7369730c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/7369730c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/7369730c

Branch: refs/heads/NIFI-25
Commit: 7369730cea59da19203e50a31cfb760db3a99023
Parents: 4f015f5
Author: Matt Gilman <ma...@gmail.com>
Authored: Tue Apr 7 10:15:54 2015 -0400
Committer: Matt Gilman <ma...@gmail.com>
Committed: Tue Apr 7 10:15:54 2015 -0400

----------------------------------------------------------------------
 .../nifi/controller/service/StandardControllerServiceNode.java     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/7369730c/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java
index 8ad0bf5..e768b9a 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceNode.java
@@ -53,7 +53,7 @@ public class StandardControllerServiceNode extends AbstractConfiguredComponent i
 
     public StandardControllerServiceNode(final ControllerService proxiedControllerService, final ControllerService implementation, final String id,
             final ValidationContextFactory validationContextFactory, final ControllerServiceProvider serviceProvider) {
-        super(proxiedControllerService, id, validationContextFactory, serviceProvider);
+        super(implementation, id, validationContextFactory, serviceProvider);
         this.proxedControllerService = proxiedControllerService;
         this.implementation = implementation;
         this.serviceProvider = serviceProvider;


[05/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-funnel.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-funnel.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-funnel.js
index c21969e..8943ddf 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-funnel.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-funnel.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.Funnel = (function () {
 
     var dimensions = {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-go-to.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-go-to.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-go-to.js
index 18812c4..2c75b7e 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-go-to.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-go-to.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 /**
  * Handles the upstream/downstream dialogs.
  */

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-graph-control.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-graph-control.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-graph-control.js
index 6537344..b6f2a51 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-graph-control.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-graph-control.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 nf.GraphControl = (function () {
 
     var config = {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-graph.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-graph.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-graph.js
index a9070c7..99f978e 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-graph.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-graph.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 nf.Graph = (function () {
 
     var combinePorts = function (contents) {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label-configuration.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label-configuration.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label-configuration.js
index aef6c74..e367452 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label-configuration.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label-configuration.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 nf.LabelConfiguration = (function () {
 
     var labelUri = '';

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label.js
index c175418..69a4758 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-label.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.Label = (function () {
 
     var dimensions = {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port-configuration.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port-configuration.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port-configuration.js
index 49d2b01..0939b3a 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port-configuration.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port-configuration.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.PortConfiguration = (function () {
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port-details.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port-details.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port-details.js
index a2cba44..c520044 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port-details.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port-details.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 nf.PortDetails = (function () {
 
     return {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js
index ed584b8..e0ff75d 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-port.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.Port = (function () {
 
     var PREVIEW_NAME_LENGTH = 15;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-configuration.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-configuration.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-configuration.js
index 3603976..fb22411 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-configuration.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-configuration.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.ProcessGroupConfiguration = (function () {
 
     return {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-details.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-details.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-details.js
index 2f88c12..d1cec36 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-details.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group-details.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 nf.ProcessGroupDetails = (function () {
 
     return {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js
index ae42547..32ba3af 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-process-group.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf, d3 */
+
 nf.ProcessGroup = (function () {
 
     var PREVIEW_NAME_LENGTH = 30;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js
index 14ffa95..329afc1 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global nf */
+
 nf.ProcessorConfiguration = (function () {
 
     // possible values for a processor's run duration (in millis)
@@ -220,7 +223,7 @@ nf.ProcessorConfiguration = (function () {
         }
 
         // defer to the property and relationship grids
-        return nf.ProcessorPropertyTable.isSaveRequired();
+        return $('#processor-properties').propertytable('isSaveRequired');
     };
 
     /**
@@ -275,7 +278,7 @@ nf.ProcessorConfiguration = (function () {
         processorConfigDto['autoTerminatedRelationships'] = marshalRelationships();
 
         // properties
-        var properties = nf.ProcessorPropertyTable.marshalProperties();
+        var properties = $('#processor-properties').propertytable('marshalProperties');
 
         // set the properties
         if ($.isEmptyObject(properties) === false) {
@@ -389,25 +392,25 @@ nf.ProcessorConfiguration = (function () {
                 selectedTabStyle: 'selected-tab',
                 tabs: [{
                         name: 'Settings',
-                        tabContentId: 'configuration-standard-settings-tab-content'
+                        tabContentId: 'processor-standard-settings-tab-content'
                     }, {
                         name: 'Scheduling',
-                        tabContentId: 'configuration-scheduling-tab-content'
+                        tabContentId: 'processor-scheduling-tab-content'
                     }, {
                         name: 'Properties',
-                        tabContentId: 'configuration-processor-properties-tab-content'
+                        tabContentId: 'processor-properties-tab-content'
                     }, {
                         name: 'Comments',
-                        tabContentId: 'configuration-comments-tab-content'
+                        tabContentId: 'processor-comments-tab-content'
                     }],
                 select: function () {
                     // update the processor property table size in case this is the first time its rendered
                     if ($(this).text() === 'Properties') {
-                        nf.ProcessorPropertyTable.resetTableSize();
+                        $('#processor-properties').propertytable('resetTableSize');
                     }
 
                     // close all fields currently being edited
-                    nf.ProcessorPropertyTable.saveRow();
+                    $('#processor-properties').propertytable('saveRow');
 
                     // show the border around the processor relationships if necessary
                     var processorRelationships = $('#auto-terminate-relationship-names');
@@ -426,18 +429,11 @@ nf.ProcessorConfiguration = (function () {
                         // empty the relationship list
                         $('#auto-terminate-relationship-names').css('border-width', '0').empty();
 
-                        // close the new property dialog if necessary
-                        $('#processor-property-dialog').hide();
-
-                        // cancel any active edits
-                        nf.ProcessorPropertyTable.cancelEdit();
-
-                        // clear the tables
-                        nf.ProcessorPropertyTable.clear();
+                        // cancel any active edits and clear the table
+                        $('#processor-properties').propertytable('cancelEdit').propertytable('clear');
 
                         // removed the cached processor details
                         $('#processor-configuration').removeData('processorDetails');
-                        $('#processor-configuration').removeData('processorHistory');
                     }
                 }
             }).draggable({
@@ -469,7 +465,21 @@ nf.ProcessorConfiguration = (function () {
             });
 
             // initialize the property table
-            nf.ProcessorPropertyTable.init();
+            $('#processor-properties').propertytable({
+                readOnly: false,
+                newPropertyDialogContainer: '#new-processor-property-container',
+                descriptorDeferred: function(propertyName) {
+                    var processor = $('#processor-configuration').data('processorDetails');
+                    return $.ajax({
+                        type: 'GET',
+                        url: processor.uri + '/descriptors',
+                        data: {
+                            propertyName: propertyName
+                        },
+                        dataType: 'json'
+                    }).fail(nf.Common.handleAjaxError);
+                }
+            });
         },
         
         /**
@@ -484,237 +494,244 @@ nf.ProcessorConfiguration = (function () {
                 // get the processor details
                 var processor = selectionData.component;
 
-                // record the processor details
-                $('#processor-configuration').data('processorDetails', processor);
+                // reload the processor in case an property descriptors have updated
+                var reloadProcessor = nf.Processor.reload(processor);
+                
+                // get the processor history
+                var loadHistory = $.ajax({
+                    type: 'GET',
+                    url: '../nifi-api/controller/history/processors/' + encodeURIComponent(processor.id),
+                    dataType: 'json'
+                });
+                
+                // once everything is loaded, show the dialog
+                $.when(reloadProcessor, loadHistory).done(function (processorResponse, historyResponse) {
+                    // get the updated processor
+                    processor = processorResponse[0].processor;
+                    
+                    // get the processor history
+                    var processorHistory = historyResponse[0].componentHistory;
+                    
+                    // record the processor details
+                    $('#processor-configuration').data('processorDetails', processor);
+
+                    // determine if the enabled checkbox is checked or not
+                    var processorEnableStyle = 'checkbox-checked';
+                    if (processor['state'] === 'DISABLED') {
+                        processorEnableStyle = 'checkbox-unchecked';
+                    }
 
-                // determine if the enabled checkbox is checked or not
-                var processorEnableStyle = 'checkbox-checked';
-                if (processor['state'] === 'DISABLED') {
-                    processorEnableStyle = 'checkbox-unchecked';
-                }
+                    // populate the processor settings
+                    $('#processor-id').text(processor['id']);
+                    $('#processor-type').text(nf.Common.substringAfterLast(processor['type'], '.'));
+                    $('#processor-name').val(processor['name']);
+                    $('#processor-enabled').removeClass('checkbox-unchecked checkbox-checked').addClass(processorEnableStyle);
+                    $('#penalty-duration').val(processor.config['penaltyDuration']);
+                    $('#yield-duration').val(processor.config['yieldDuration']);
+                    $('#processor-comments').val(processor.config['comments']);
+
+                    // set the run duration
+                    var runDuration = RUN_DURATION_VALUES.indexOf(processor.config['runDurationMillis']);
+                    $('#run-duration-slider').slider('value', runDuration);
+
+                    // select the appropriate bulletin level
+                    $('#bulletin-level-combo').combo('setSelectedOption', {
+                        value: processor.config['bulletinLevel']
+                    });
 
-                // populate the processor settings
-                $('#processor-id').text(processor['id']);
-                $('#processor-type').text(nf.Common.substringAfterLast(processor['type'], '.'));
-                $('#processor-name').val(processor['name']);
-                $('#processor-enabled').removeClass('checkbox-unchecked checkbox-checked').addClass(processorEnableStyle);
-                $('#penalty-duration').val(processor.config['penaltyDuration']);
-                $('#yield-duration').val(processor.config['yieldDuration']);
-                $('#processor-comments').val(processor.config['comments']);
-
-                // set the run duration
-                var runDuration = RUN_DURATION_VALUES.indexOf(processor.config['runDurationMillis']);
-                $('#run-duration-slider').slider('value', runDuration);
-
-                // select the appropriate bulletin level
-                $('#bulletin-level-combo').combo('setSelectedOption', {
-                    value: processor.config['bulletinLevel']
-                });
+                    // initialize the scheduling strategy
+                    $('#scheduling-strategy-combo').combo({
+                        options: getSchedulingStrategies(processor),
+                        selectedOption: {
+                            value: processor.config['schedulingStrategy']
+                        },
+                        select: function (selectedOption) {
+                            // show the appropriate panel
+                            if (selectedOption.value === 'EVENT_DRIVEN') {
+                                $('#event-driven-warning').show();
 
-                // initialize the scheduling strategy
-                $('#scheduling-strategy-combo').combo({
-                    options: getSchedulingStrategies(processor),
-                    selectedOption: {
-                        value: processor.config['schedulingStrategy']
-                    },
-                    select: function (selectedOption) {
-                        // show the appropriate panel
-                        if (selectedOption.value === 'EVENT_DRIVEN') {
-                            $('#event-driven-warning').show();
-                            
-                            $('#timer-driven-options').hide();
-                            $('#event-driven-options').show();
-                            $('#cron-driven-options').hide();
-                        } else {
-                            $('#event-driven-warning').hide();
-                            
-                            if (selectedOption.value === 'CRON_DRIVEN') {
                                 $('#timer-driven-options').hide();
-                                $('#event-driven-options').hide();
-                                $('#cron-driven-options').show();
-                            } else {
-                                $('#timer-driven-options').show();
-                                $('#event-driven-options').hide();
+                                $('#event-driven-options').show();
                                 $('#cron-driven-options').hide();
+                            } else {
+                                $('#event-driven-warning').hide();
+
+                                if (selectedOption.value === 'CRON_DRIVEN') {
+                                    $('#timer-driven-options').hide();
+                                    $('#event-driven-options').hide();
+                                    $('#cron-driven-options').show();
+                                } else {
+                                    $('#timer-driven-options').show();
+                                    $('#event-driven-options').hide();
+                                    $('#cron-driven-options').hide();
+                                }
                             }
                         }
-                    }
-                });
-
-                // initialize the concurrentTasks
-                var defaultConcurrentTasks = processor.config['defaultConcurrentTasks'];
-                $('#timer-driven-concurrently-schedulable-tasks').val(defaultConcurrentTasks['TIMER_DRIVEN']);
-                $('#event-driven-concurrently-schedulable-tasks').val(defaultConcurrentTasks['EVENT_DRIVEN']);
-                $('#cron-driven-concurrently-schedulable-tasks').val(defaultConcurrentTasks['CRON_DRIVEN']);
-
-                // get the appropriate concurrent tasks field
-                var concurrentTasks;
-                if (processor.config['schedulingStrategy'] === 'EVENT_DRIVEN') {
-                    concurrentTasks = $('#event-driven-concurrently-schedulable-tasks').val(processor.config['concurrentlySchedulableTaskCount']);
-                } else if (processor.config['schedulingStrategy'] === 'CRON_DRIVEN') {
-                    concurrentTasks = $('#cron-driven-concurrently-schedulable-tasks').val(processor.config['concurrentlySchedulableTaskCount']);
-                } else {
-                    concurrentTasks = $('#timer-driven-concurrently-schedulable-tasks').val(processor.config['concurrentlySchedulableTaskCount']);
-                }
+                    });
 
-                // conditionally allow the user to specify the concurrent tasks
-                if (nf.Common.isDefinedAndNotNull(concurrentTasks)) {
-                    if (processor.supportsParallelProcessing === true) {
-                        concurrentTasks.prop('disabled', false);
+                    // initialize the concurrentTasks
+                    var defaultConcurrentTasks = processor.config['defaultConcurrentTasks'];
+                    $('#timer-driven-concurrently-schedulable-tasks').val(defaultConcurrentTasks['TIMER_DRIVEN']);
+                    $('#event-driven-concurrently-schedulable-tasks').val(defaultConcurrentTasks['EVENT_DRIVEN']);
+                    $('#cron-driven-concurrently-schedulable-tasks').val(defaultConcurrentTasks['CRON_DRIVEN']);
+
+                    // get the appropriate concurrent tasks field
+                    var concurrentTasks;
+                    if (processor.config['schedulingStrategy'] === 'EVENT_DRIVEN') {
+                        concurrentTasks = $('#event-driven-concurrently-schedulable-tasks').val(processor.config['concurrentlySchedulableTaskCount']);
+                    } else if (processor.config['schedulingStrategy'] === 'CRON_DRIVEN') {
+                        concurrentTasks = $('#cron-driven-concurrently-schedulable-tasks').val(processor.config['concurrentlySchedulableTaskCount']);
                     } else {
-                        concurrentTasks.prop('disabled', true);
+                        concurrentTasks = $('#timer-driven-concurrently-schedulable-tasks').val(processor.config['concurrentlySchedulableTaskCount']);
                     }
-                }
-
-                // initialize the schedulingStrategy
-                var defaultSchedulingPeriod = processor.config['defaultSchedulingPeriod'];
-                $('#cron-driven-scheduling-period').val(defaultSchedulingPeriod['CRON_DRIVEN']);
-                $('#timer-driven-scheduling-period').val(defaultSchedulingPeriod['TIMER_DRIVEN']);
 
-                // set the scheduling period as appropriate
-                if (processor.config['schedulingStrategy'] === 'CRON_DRIVEN') {
-                    $('#cron-driven-scheduling-period').val(processor.config['schedulingPeriod']);
-                } else if (processor.config['schedulingStrategy'] !== 'EVENT_DRIVEN') {
-                    $('#timer-driven-scheduling-period').val(processor.config['schedulingPeriod']);
-                }
+                    // conditionally allow the user to specify the concurrent tasks
+                    if (nf.Common.isDefinedAndNotNull(concurrentTasks)) {
+                        if (processor.supportsParallelProcessing === true) {
+                            concurrentTasks.prop('disabled', false);
+                        } else {
+                            concurrentTasks.prop('disabled', true);
+                        }
+                    }
 
-                // load the property table
-                nf.ProcessorPropertyTable.loadProperties(processor.config.properties, processor.config.descriptors);
+                    // initialize the schedulingStrategy
+                    var defaultSchedulingPeriod = processor.config['defaultSchedulingPeriod'];
+                    $('#cron-driven-scheduling-period').val(defaultSchedulingPeriod['CRON_DRIVEN']);
+                    $('#timer-driven-scheduling-period').val(defaultSchedulingPeriod['TIMER_DRIVEN']);
 
-                // load the relationship list
-                if (!nf.Common.isEmpty(processor.relationships)) {
-                    $.each(processor.relationships, function (i, relationship) {
-                        createRelationshipOption(relationship);
-                    });
-                } else {
-                    $('#auto-terminate-relationship-names').append('<div class="unset">This processor has no relationships.</div>');
-                }
+                    // set the scheduling period as appropriate
+                    if (processor.config['schedulingStrategy'] === 'CRON_DRIVEN') {
+                        $('#cron-driven-scheduling-period').val(processor.config['schedulingPeriod']);
+                    } else if (processor.config['schedulingStrategy'] !== 'EVENT_DRIVEN') {
+                        $('#timer-driven-scheduling-period').val(processor.config['schedulingPeriod']);
+                    }
 
-                var buttons = [{
-                        buttonText: 'Apply',
-                        handler: {
-                            click: function () {
-                                // close all fields currently being edited
-                                nf.ProcessorPropertyTable.saveRow();
-
-                                // marshal the settings and properties and update the processor
-                                var updatedProcessor = marshalDetails();
-
-                                // ensure details are valid as far as we can tell
-                                if (validateDetails(updatedProcessor)) {
-                                    // update the selected component
-                                    $.ajax({
-                                        type: 'PUT',
-                                        data: JSON.stringify(updatedProcessor),
-                                        url: processor.uri,
-                                        dataType: 'json',
-                                        processData: false,
-                                        contentType: 'application/json'
-                                    }).done(function (response) {
-                                        if (nf.Common.isDefinedAndNotNull(response.processor)) {
-                                            // update the revision
-                                            nf.Client.setRevision(response.revision);
-
-                                            // set the new processor state based on the response
-                                            nf.Processor.set(response.processor);
-                                            
-                                            // reload the processor's outgoing connections
-                                            reloadProcessorConnections(processor);
+                    // load the relationship list
+                    if (!nf.Common.isEmpty(processor.relationships)) {
+                        $.each(processor.relationships, function (i, relationship) {
+                            createRelationshipOption(relationship);
+                        });
+                    } else {
+                        $('#auto-terminate-relationship-names').append('<div class="unset">This processor has no relationships.</div>');
+                    }
 
-                                            // close the details panel
-                                            $('#processor-configuration').modal('hide');
-                                        }
-                                    }).fail(handleProcessorConfigurationError);
+                    var buttons = [{
+                            buttonText: 'Apply',
+                            handler: {
+                                click: function () {
+                                    // close all fields currently being edited
+                                    $('#processor-properties').propertytable('saveRow');
+
+                                    // marshal the settings and properties and update the processor
+                                    var updatedProcessor = marshalDetails();
+
+                                    // ensure details are valid as far as we can tell
+                                    if (validateDetails(updatedProcessor)) {
+                                        // update the selected component
+                                        $.ajax({
+                                            type: 'PUT',
+                                            data: JSON.stringify(updatedProcessor),
+                                            url: processor.uri,
+                                            dataType: 'json',
+                                            processData: false,
+                                            contentType: 'application/json'
+                                        }).done(function (response) {
+                                            if (nf.Common.isDefinedAndNotNull(response.processor)) {
+                                                // update the revision
+                                                nf.Client.setRevision(response.revision);
+
+                                                // set the new processor state based on the response
+                                                nf.Processor.set(response.processor);
+
+                                                // reload the processor's outgoing connections
+                                                reloadProcessorConnections(processor);
+
+                                                // close the details panel
+                                                $('#processor-configuration').modal('hide');
+                                            }
+                                        }).fail(handleProcessorConfigurationError);
+                                    }
                                 }
                             }
-                        }
-                    }, {
-                        buttonText: 'Cancel',
-                        handler: {
-                            click: function () {
-                                $('#processor-configuration').modal('hide');
-                            }
-                        }
-                    }];
-
-                // determine if we should show the advanced button
-                if (nf.Common.isDefinedAndNotNull(processor.config.customUiUrl) && processor.config.customUiUrl !== '') {
-                    buttons.push({
-                        buttonText: 'Advanced',
-                        handler: {
-                            click: function () {
-                                var openCustomUi = function () {
-                                    // reset state and close the dialog manually to avoid hiding the faded background
+                        }, {
+                            buttonText: 'Cancel',
+                            handler: {
+                                click: function () {
                                     $('#processor-configuration').modal('hide');
-
-                                    // show the custom ui
-                                    nf.CustomProcessorUi.showCustomUi($('#processor-id').text(), processor.config.customUiUrl, true).done(function () {
-                                        // once the custom ui is closed, reload the processor
-                                        nf.Processor.reload(processor);
-                                        
-                                        // and reload the processor's outgoing connections
-                                        reloadProcessorConnections(processor);
-                                    });
-                                };
-
-                                // close all fields currently being edited
-                                nf.ProcessorPropertyTable.saveRow();
-
-                                // determine if changes have been made
-                                if (isSaveRequired()) {
-                                    // see if those changes should be saved
-                                    nf.Dialog.showYesNoDialog({
-                                        dialogContent: 'Save changes before opening the advanced configuration?',
-                                        overlayBackground: false,
-                                        noHandler: openCustomUi,
-                                        yesHandler: function () {
-                                            // marshal the settings and properties and update the processor
-                                            var updatedProcessor = marshalDetails();
-
-                                            // ensure details are valid as far as we can tell
-                                            if (validateDetails(updatedProcessor)) {
-                                                // update the selected component
-                                                $.ajax({
-                                                    type: 'PUT',
-                                                    data: JSON.stringify(updatedProcessor),
-                                                    url: processor.uri,
-                                                    dataType: 'json',
-                                                    processData: false,
-                                                    contentType: 'application/json'
-                                                }).done(function (response) {
-                                                    if (nf.Common.isDefinedAndNotNull(response.processor)) {
-                                                        // update the revision
-                                                        nf.Client.setRevision(response.revision);
-
-                                                        // open the custom ui
-                                                        openCustomUi();
-                                                    }
-                                                }).fail(handleProcessorConfigurationError);
+                                }
+                            }
+                        }];
+
+                    // determine if we should show the advanced button
+                    if (nf.Common.isDefinedAndNotNull(processor.config.customUiUrl) && processor.config.customUiUrl !== '') {
+                        buttons.push({
+                            buttonText: 'Advanced',
+                            handler: {
+                                click: function () {
+                                    var openCustomUi = function () {
+                                        // reset state and close the dialog manually to avoid hiding the faded background
+                                        $('#processor-configuration').modal('hide');
+
+                                        // show the custom ui
+                                        nf.CustomUi.showCustomUi($('#processor-id').text(), processor.config.customUiUrl, true).done(function () {
+                                            // once the custom ui is closed, reload the processor
+                                            nf.Processor.reload(processor);
+
+                                            // and reload the processor's outgoing connections
+                                            reloadProcessorConnections(processor);
+                                        });
+                                    };
+
+                                    // close all fields currently being edited
+                                    $('#processor-properties').propertytable('saveRow');
+
+                                    // determine if changes have been made
+                                    if (isSaveRequired()) {
+                                        // see if those changes should be saved
+                                        nf.Dialog.showYesNoDialog({
+                                            dialogContent: 'Save changes before opening the advanced configuration?',
+                                            overlayBackground: false,
+                                            noHandler: openCustomUi,
+                                            yesHandler: function () {
+                                                // marshal the settings and properties and update the processor
+                                                var updatedProcessor = marshalDetails();
+
+                                                // ensure details are valid as far as we can tell
+                                                if (validateDetails(updatedProcessor)) {
+                                                    // update the selected component
+                                                    $.ajax({
+                                                        type: 'PUT',
+                                                        data: JSON.stringify(updatedProcessor),
+                                                        url: processor.uri,
+                                                        dataType: 'json',
+                                                        processData: false,
+                                                        contentType: 'application/json'
+                                                    }).done(function (response) {
+                                                        if (nf.Common.isDefinedAndNotNull(response.processor)) {
+                                                            // update the revision
+                                                            nf.Client.setRevision(response.revision);
+
+                                                            // open the custom ui
+                                                            openCustomUi();
+                                                        }
+                                                    }).fail(handleProcessorConfigurationError);
+                                                }
                                             }
-                                        }
-                                    });
-                                } else {
-                                    // if there were no changes, simply open the custom ui
-                                    openCustomUi();
+                                        });
+                                    } else {
+                                        // if there were no changes, simply open the custom ui
+                                        openCustomUi();
+                                    }
                                 }
                             }
-                        }
-                    });
-                }
-
-                // set the button model
-                $('#processor-configuration').modal('setButtonModel', buttons);
-
-                // get the processor history
-                $.ajax({
-                    type: 'GET',
-                    url: '../nifi-api/controller/history/processors/' + encodeURIComponent(processor.id),
-                    dataType: 'json'
-                }).done(function (response) {
-                    var processorHistory = response.processorHistory;
+                        });
+                    }
 
-                    // record the processor history
-                    $('#processor-configuration').data('processorHistory', processorHistory);
+                    // set the button model
+                    $('#processor-configuration').modal('setButtonModel', buttons);
+                    
+                    // load the property table
+                    $('#processor-properties').propertytable('loadProperties', processor.config.properties, processor.config.descriptors, processorHistory.propertyHistory);
 
                     // show the details
                     $('#processor-configuration').modal('show');

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-combo-editor.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-combo-editor.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-combo-editor.js
deleted file mode 100644
index e777293..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-combo-editor.js
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * 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.
- */
-nf.ProcessorPropertyComboEditor = function (args) {
-    var scope = this;
-    var initialValue = null;
-    var wrapper;
-    var combo;
-
-    this.init = function () {
-        var container = $('body');
-
-        // create the wrapper
-        wrapper = $('<div></div>').css({
-            'z-index': 1999,
-            'position': 'absolute',
-            'background': 'white',
-            'padding': '5px',
-            'overflow': 'hidden',
-            'border': '3px solid #365C6A',
-            'box-shadow': '4px 4px 6px rgba(0, 0, 0, 0.9)',
-            'cursor': 'move'
-        }).draggable({
-            cancel: '.button, .combo',
-            containment: 'parent'
-        }).appendTo(container);
-
-        // identify the property descriptor - property descriptor is never null/undefined here... in order
-        // to use this editor, the property descriptor would have had to indicate a set of allowable values
-        var processorDetails = $('#processor-configuration').data('processorDetails');
-        var propertyDescriptor = processorDetails.config.descriptors[args.item.property];
-
-        // check for allowable values which will drive which editor to use
-        var allowableValues = nf.ProcessorPropertyTable.getAllowableValues(propertyDescriptor);
-
-        // show the output port options
-        var options = [];
-        if (propertyDescriptor.required === false) {
-            options.push({
-                text: 'No value',
-                value: null,
-                optionClass: 'unset'
-            });
-        }
-        if ($.isArray(allowableValues)) {
-            $.each(allowableValues, function (i, allowableValue) {
-                options.push({
-                    text: allowableValue.displayName,
-                    value: allowableValue.value,
-                    description: nf.Common.escapeHtml(allowableValue.description)
-                });
-            });
-        }
-
-        // ensure the options there is at least one option
-        if (options.length === 0) {
-            options.push({
-                text: 'No value',
-                value: null,
-                optionClass: 'unset',
-                disabled: true
-            });
-        }
-
-        // determine the max height
-        var position = args.position;
-        var windowHeight = $(window).height();
-        var maxHeight = windowHeight - position.bottom - 16;
-
-        // build the combo field
-        combo = $('<div class="value-combo combo"></div>').combo({
-            options: options,
-            maxHeight: maxHeight
-        }).width(position.width - 16).appendTo(wrapper);
-
-        // add buttons for handling user input
-        $('<div class="button button-normal">Cancel</div>').css({
-            'margin': '0 0 0 5px',
-            'float': 'left'
-        }).on('click', scope.cancel).appendTo(wrapper);
-        $('<div class="button button-normal">Ok</div>').css({
-            'margin': '0 0 0 5px',
-            'float': 'left'
-        }).on('click', scope.save).appendTo(wrapper);
-
-        // position and focus
-        scope.position(position);
-    };
-
-    this.save = function () {
-        args.commitChanges();
-    };
-
-    this.cancel = function () {
-        args.cancelChanges();
-    };
-
-    this.hide = function () {
-        wrapper.hide();
-    };
-
-    this.show = function () {
-        wrapper.show();
-    };
-
-    this.position = function (position) {
-        wrapper.css({
-            'top': position.top - 5,
-            'left': position.left - 5
-        });
-    };
-
-    this.destroy = function () {
-        wrapper.remove();
-    };
-
-    this.focus = function () {
-    };
-
-    this.loadValue = function (item) {
-        // identify the property descriptor
-        var processorDetails = $('#processor-configuration').data('processorDetails');
-        var propertyDescriptor = processorDetails.config.descriptors[item.property];
-
-        // select as appropriate
-        if (!nf.Common.isUndefined(item.value)) {
-            initialValue = item.value;
-
-            combo.combo('setSelectedOption', {
-                value: item.value
-            });
-        } else if (nf.Common.isDefinedAndNotNull(propertyDescriptor.defaultValue)) {
-            initialValue = propertyDescriptor.defaultValue;
-
-            combo.combo('setSelectedOption', {
-                value: propertyDescriptor.defaultValue
-            });
-        }
-    };
-
-    this.serializeValue = function () {
-        var selectedOption = combo.combo('getSelectedOption');
-        return selectedOption.value;
-    };
-
-    this.applyValue = function (item, state) {
-        item[args.column.field] = state;
-    };
-
-    this.isValueChanged = function () {
-        var selectedOption = combo.combo('getSelectedOption');
-        return (!(selectedOption.value === "" && initialValue === null)) && (selectedOption.value !== initialValue);
-    };
-
-    this.validate = function () {
-        return {
-            valid: true,
-            msg: null
-        };
-    };
-
-    // initialize the custom long text editor
-    this.init();
-};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-nfel-editor.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-nfel-editor.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-nfel-editor.js
deleted file mode 100644
index dc369ac..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-nfel-editor.js
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * 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.
- */
-nf.ProcessorPropertyNfelEditor = function (args) {
-    var scope = this;
-    var initialValue = '';
-    var previousValue;
-    var propertyDescriptor;
-    var isEmpty;
-    var wrapper;
-    var editor;
-
-    this.init = function () {
-        var container = $('body');
-
-        // get the property descriptor for this property
-        var details = $('#processor-configuration').data('processorDetails');
-        propertyDescriptor = details.config.descriptors[args.item.property];
-
-        // determine if this is a sensitive property
-        var sensitive = nf.ProcessorPropertyTable.isSensitiveProperty(propertyDescriptor);
-
-        // record the previous value
-        previousValue = args.item[args.column.field];
-
-        var languageId = 'nfel';
-        var editorClass = languageId + '-editor';
-
-        // create the wrapper
-        wrapper = $('<div></div>').addClass('slickgrid-nfel-editor').css({
-            'z-index': 14000,
-            'position': 'absolute',
-            'background': 'white',
-            'padding': '5px',
-            'overflow': 'hidden',
-            'border': '3px solid #365C6A',
-            'box-shadow': '4px 4px 6px rgba(0, 0, 0, 0.9)',
-            'cursor': 'move'
-        }).draggable({
-            cancel: 'input, textarea, pre, .nf-checkbox, .button, .' + editorClass,
-            containment: 'parent'
-        }).appendTo(container);
-
-        // create the editor
-        editor = $('<div></div>').addClass(editorClass).appendTo(wrapper).nfeditor({
-            languageId: languageId,
-            width: args.position.width,
-            minWidth: 175,
-            minHeight: 100,
-            resizable: true,
-            sensitive: sensitive,
-            escape: function () {
-                scope.cancel();
-            },
-            enter: function () {
-                scope.save();
-            }
-        });
-
-        // create the button panel
-        var stringCheckPanel = $('<div class="string-check-container">');
-
-        // build the custom checkbox
-        isEmpty = $('<div class="nf-checkbox string-check"/>').appendTo(stringCheckPanel);
-        $('<span class="string-check-label">&nbsp;Empty</span>').appendTo(stringCheckPanel);
-
-        var ok = $('<div class="button button-normal">Ok</div>').on('click', scope.save);
-        var cancel = $('<div class="button button-normal">Cancel</div>').on('click', scope.cancel);
-        $('<div></div>').css({
-            'position': 'absolute',
-            'bottom': '0',
-            'left': '0',
-            'right': '0',
-            'padding': '0 3px 5px 1px'
-        }).append(stringCheckPanel).append(ok).append(cancel).append('<div class="clear"></div>').appendTo(wrapper);
-
-        // position and focus
-        scope.position(args.position);
-        editor.nfeditor('focus').nfeditor('selectAll');
-    };
-
-    this.save = function () {
-        args.commitChanges();
-    };
-
-    this.cancel = function () {
-        editor.nfeditor('setValue', initialValue);
-        args.cancelChanges();
-    };
-
-    this.hide = function () {
-        wrapper.hide();
-    };
-
-    this.show = function () {
-        wrapper.show();
-        editor.nfeditor('setSize', args.position.width, null).nfeditor('refresh');
-    };
-
-    this.position = function (position) {
-        wrapper.css({
-            'top': position.top - 5,
-            'left': position.left - 5
-        });
-    };
-
-    this.destroy = function () {
-        editor.nfeditor('destroy');
-        wrapper.remove();
-    };
-
-    this.focus = function () {
-        editor.nfeditor('focus');
-    };
-
-    this.loadValue = function (item) {
-        // determine if this is a sensitive property
-        var isEmptyChecked = false;
-        var sensitive = nf.ProcessorPropertyTable.isSensitiveProperty(propertyDescriptor);
-
-        // determine the value to use when populating the text field
-        if (nf.Common.isDefinedAndNotNull(item[args.column.field])) {
-            if (sensitive) {
-                initialValue = nf.ProcessorPropertyTable.config.sensitiveText;
-            } else {
-                initialValue = item[args.column.field];
-                isEmptyChecked = initialValue === '';
-            }
-        }
-
-        // determine if its an empty string
-        var checkboxStyle = isEmptyChecked ? 'checkbox-checked' : 'checkbox-unchecked';
-        isEmpty.addClass(checkboxStyle);
-
-        editor.nfeditor('setValue', initialValue).nfeditor('selectAll');
-    };
-
-    this.serializeValue = function () {
-        var value = editor.nfeditor('getValue');
-
-        // if the field has been cleared, set the value accordingly
-        if (value === '') {
-            // if the user has checked the empty string checkbox, use emtpy string
-            if (isEmpty.hasClass('checkbox-checked')) {
-                return '';
-            } else {
-                // otherwise if the property is required
-                if (nf.ProcessorPropertyTable.isRequiredProperty(propertyDescriptor)) {
-                    if (nf.Common.isBlank(propertyDescriptor.defaultValue)) {
-                        return previousValue;
-                    } else {
-                        return propertyDescriptor.defaultValue;
-                    }
-                } else {
-                    // if the property is not required, clear the value
-                    return null;
-                }
-            }
-        } else {
-            // if the field still has the sensitive class it means a property
-            // was edited but never modified so we should restore the previous
-            // value instead of setting it to the 'sensitive value set' string
-
-            // if the field hasn't been modified return the previous value... this
-            // is important because sensitive properties contain the text 'sensitive
-            // value set' which is cleared when the value is edited. we do not 
-            // want to actually use this value
-            if (editor.nfeditor('isModified') === false) {
-                return previousValue;
-            } else {
-                // if there is text specified, use that value
-                return value;
-            }
-        }
-    };
-
-    this.applyValue = function (item, state) {
-        item[args.column.field] = state;
-    };
-
-    this.isValueChanged = function () {
-        return scope.serializeValue() !== previousValue;
-    };
-
-    this.validate = function () {
-        return {
-            valid: true,
-            msg: null
-        };
-    };
-
-    // initialize the custom long nfel editor
-    this.init();
-};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-table.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-table.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-table.js
deleted file mode 100644
index 4c04702..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-property-table.js
+++ /dev/null
@@ -1,567 +0,0 @@
-/*
- * 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.
- */
-nf.ProcessorPropertyTable = (function () {
-
-    /**
-     * Initialize the new property dialog.
-     */
-    var initNewPropertyDialog = function () {
-        var languageId = 'nfel';
-        var editorClass = languageId + '-editor';
-
-        // add the over state for the new property button
-        nf.Common.addHoverEffect('#add-property-icon', 'add-icon-bg', 'add-icon-bg-hover');
-
-        var add = function () {
-            var propertyName = $.trim($('#new-property-name').val());
-            var propertyValue = $('#new-property-value').nfeditor('getValue');
-
-            // ensure the property name and value is specified
-            if (propertyName !== '') {
-                // add a row for the new property
-                var propertyGrid = $('#processor-properties').data('gridInstance');
-                var propertyData = propertyGrid.getData();
-                propertyData.addItem({
-                    id: propertyData.getLength(),
-                    hidden: false,
-                    property: propertyName,
-                    displayName: propertyName,
-                    previousValue: null,
-                    value: propertyValue,
-                    type: 'userDefined'
-                });
-            } else {
-                nf.Dialog.showOkDialog({
-                    dialogContent: 'Property name must be specified.',
-                    overlayBackground: false
-                });
-            }
-
-            // close the dialog
-            $('#processor-property-dialog').hide();
-        };
-
-        var cancel = function () {
-            $('#processor-property-dialog').hide();
-        };
-
-        // create the editor
-        $('#new-property-value').addClass(editorClass).nfeditor({
-            languageId: languageId,
-            width: 318,
-            minWidth: 318,
-            height: 106,
-            minHeight: 106,
-            resizable: true,
-            escape: cancel,
-            enter: add
-        });
-
-        // add a click listener to display the new property dialog
-        $('#add-property-icon').on('click', function () {
-            // close all fields currently being edited
-            nf.ProcessorPropertyTable.saveRow();
-
-            // clear the dialog
-            $('#new-property-name').val('');
-            $('#new-property-value').nfeditor('setValue', '');
-
-            // reset the add property dialog position/size
-            $('#new-property-value').nfeditor('setSize', 318, 106);
-
-            // open the new property dialog
-            $('#processor-property-dialog').center().show();
-
-            // give the property name focus
-            $('#new-property-value').nfeditor('refresh');
-            $('#new-property-name').focus();
-        });
-
-        // make the new property dialog draggable
-        $('#processor-property-dialog').draggable({
-            cancel: 'input, textarea, pre, .button, .' + editorClass,
-            containment: 'parent'
-        }).on('click', '#new-property-ok', add).on('click', '#new-property-cancel', cancel);
-
-        // enable tabs in the property value
-        $('#new-property-name').on('keydown', function (e) {
-            if (e.which === $.ui.keyCode.ENTER && !e.shiftKey) {
-                add();
-            } else if (e.which === $.ui.keyCode.ESCAPE) {
-                e.preventDefault();
-                cancel();
-            }
-        });
-    };
-
-    /**
-     * Initializes the processor property table.
-     */
-    var initProcessorPropertiesTable = function () {
-        // function for formatting the property name
-        var nameFormatter = function (row, cell, value, columnDef, dataContext) {
-            var nameWidthOffset = 10;
-            var cellContent = $('<div></div>');
-
-            // format the contents
-            var formattedValue = $('<span/>').addClass('table-cell').text(value).appendTo(cellContent);
-            if (dataContext.type === 'required') {
-                formattedValue.addClass('required');
-            }
-
-            // get the processor details to insert the description tooltip
-            var details = $('#processor-configuration').data('processorDetails');
-            var propertyDescriptor = details.config.descriptors[dataContext.property];
-
-            // show the property description if applicable
-            if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
-                if (!nf.Common.isBlank(propertyDescriptor.description) || !nf.Common.isBlank(propertyDescriptor.defaultValue) || !nf.Common.isBlank(propertyDescriptor.supportsEl)) {
-                    $('<img class="icon-info" src="images/iconInfo.png" alt="Info" title="" style="float: right; margin-right: 6px; margin-top: 4px;" />').appendTo(cellContent);
-                    $('<span class="hidden property-descriptor-name"></span>').text(dataContext.property).appendTo(cellContent);
-                    nameWidthOffset = 26; // 10 + icon width (10) + icon margin (6)
-                }
-            }
-
-            // adjust the width accordingly
-            formattedValue.width(columnDef.width - nameWidthOffset).ellipsis();
-
-            // return the cell content
-            return cellContent.html();
-        };
-
-        // function for formatting the property value
-        var valueFormatter = function (row, cell, value, columnDef, dataContext) {
-            var valueMarkup;
-            if (nf.Common.isDefinedAndNotNull(value)) {
-                // identify the property descriptor
-                var processorDetails = $('#processor-configuration').data('processorDetails');
-                var propertyDescriptor = processorDetails.config.descriptors[dataContext.property];
-
-                // determine if the property is sensitive
-                if (nf.ProcessorPropertyTable.isSensitiveProperty(propertyDescriptor)) {
-                    valueMarkup = '<span class="table-cell sensitive">Sensitive value set</span>';
-                } else {
-                    // if there are allowable values, attempt to swap out for the display name
-                    var allowableValues = nf.ProcessorPropertyTable.getAllowableValues(propertyDescriptor);
-                    if ($.isArray(allowableValues)) {
-                        $.each(allowableValues, function (_, allowableValue) {
-                            if (value === allowableValue.value) {
-                                value = allowableValue.displayName;
-                                return false;
-                            }
-                        });
-                    }
-
-                    if (value === '') {
-                        valueMarkup = '<span class="table-cell blank">Empty string set</span>';
-                    } else {
-                        valueMarkup = '<div class="table-cell value"><pre class="ellipsis">' + nf.Common.escapeHtml(value) + '</pre></div>';
-                    }
-                }
-            } else {
-                valueMarkup = '<span class="unset">No value set</span>';
-            }
-
-            // format the contents
-            var content = $(valueMarkup);
-            if (dataContext.type === 'required') {
-                content.addClass('required');
-            }
-            content.find('.ellipsis').width(columnDef.width - 10).ellipsis();
-
-            // return the appropriate markup
-            return $('<div/>').append(content).html();
-        };
-
-        // custom formatter for the actions column
-        var actionFormatter = function (row, cell, value, columnDef, dataContext) {
-            var markup = '';
-
-            // allow user defined properties to be removed
-            if (dataContext.type === 'userDefined') {
-                markup = '<img src="images/iconDelete.png" title="Delete" class="pointer" style="margin-top: 2px" onclick="javascript:nf.ProcessorPropertyTable.deleteProperty(\'' + row + '\');"/>';
-            }
-
-            return markup;
-        };
-
-        var processorConfigurationColumns = [
-            {id: 'property', field: 'displayName', name: 'Property', sortable: false, resizable: true, rerenderOnResize: true, formatter: nameFormatter},
-            {id: 'value', field: 'value', name: 'Value', sortable: false, resizable: true, cssClass: 'pointer', rerenderOnResize: true, formatter: valueFormatter},
-            {id: "actions", name: "&nbsp;", minWidth: 20, width: 20, formatter: actionFormatter}
-        ];
-        var processorConfigurationOptions = {
-            forceFitColumns: true,
-            enableTextSelectionOnCells: true,
-            enableCellNavigation: true,
-            enableColumnReorder: false,
-            editable: true,
-            enableAddRow: false,
-            autoEdit: false
-        };
-
-        // initialize the dataview
-        var processorConfigurationData = new Slick.Data.DataView({
-            inlineFilters: false
-        });
-        processorConfigurationData.setItems([]);
-        processorConfigurationData.setFilterArgs({
-            searchString: '',
-            property: 'hidden'
-        });
-        processorConfigurationData.setFilter(filter);
-        processorConfigurationData.getItemMetadata = function (index) {
-            var item = processorConfigurationData.getItem(index);
-
-            // identify the property descriptor
-            var processorDetails = $('#processor-configuration').data('processorDetails');
-            var propertyDescriptor = processorDetails.config.descriptors[item.property];
-
-            // support el if specified or unsure yet (likely a dynamic property)
-            if (nf.Common.isUndefinedOrNull(propertyDescriptor) || nf.ProcessorPropertyTable.supportsEl(propertyDescriptor)) {
-                return {
-                    columns: {
-                        value: {
-                            editor: nf.ProcessorPropertyNfelEditor
-                        }
-                    }
-                };
-            } else {
-                // check for allowable values which will drive which editor to use
-                var allowableValues = nf.ProcessorPropertyTable.getAllowableValues(propertyDescriptor);
-                if ($.isArray(allowableValues)) {
-                    return {
-                        columns: {
-                            value: {
-                                editor: nf.ProcessorPropertyComboEditor
-                            }
-                        }
-                    };
-                } else {
-                    return {
-                        columns: {
-                            value: {
-                                editor: nf.ProcessorPropertyTextEditor
-                            }
-                        }
-                    };
-                }
-            }
-        };
-
-        // initialize the grid
-        var processorConfigurationGrid = new Slick.Grid('#processor-properties', processorConfigurationData, processorConfigurationColumns, processorConfigurationOptions);
-        processorConfigurationGrid.setSelectionModel(new Slick.RowSelectionModel());
-        processorConfigurationGrid.onClick.subscribe(function (e, args) {
-            // edits the clicked cell
-            processorConfigurationGrid.gotoCell(args.row, args.cell, true);
-
-            // prevents standard edit logic
-            e.stopImmediatePropagation();
-        });
-
-        // wire up the dataview to the grid
-        processorConfigurationData.onRowCountChanged.subscribe(function (e, args) {
-            processorConfigurationGrid.updateRowCount();
-            processorConfigurationGrid.render();
-        });
-        processorConfigurationData.onRowsChanged.subscribe(function (e, args) {
-            processorConfigurationGrid.invalidateRows(args.rows);
-            processorConfigurationGrid.render();
-        });
-
-        // hold onto an instance of the grid and listen for mouse events to add tooltips where appropriate
-        $('#processor-properties').data('gridInstance', processorConfigurationGrid).on('mouseenter', 'div.slick-cell', function (e) {
-            var infoIcon = $(this).find('img.icon-info');
-            if (infoIcon.length && !infoIcon.data('qtip')) {
-                var property = $(this).find('span.property-descriptor-name').text();
-
-                // get the processor details to insert the description tooltip
-                var details = $('#processor-configuration').data('processorDetails');
-                var propertyDescriptor = details.config.descriptors[property];
-
-                // get the processor history
-                var processorHistory = $('#processor-configuration').data('processorHistory');
-                var propertyHistory = processorHistory.propertyHistory[property];
-
-                // format the tooltip
-                var tooltip = nf.Common.formatPropertyTooltip(propertyDescriptor, propertyHistory);
-
-                if (nf.Common.isDefinedAndNotNull(tooltip)) {
-                    infoIcon.qtip($.extend({
-                        content: tooltip
-                    }, nf.Common.config.tooltipConfig));
-                }
-            }
-        });
-    };
-
-    /**
-     * Performs the filtering.
-     * 
-     * @param {object} item     The item subject to filtering
-     * @param {object} args     Filter arguments
-     * @returns {Boolean}       Whether or not to include the item
-     */
-    var filter = function (item, args) {
-        return item.hidden === false;
-    };
-
-    return {
-        config: {
-            sensitiveText: 'Sensitive value set'
-        },
-        
-        /**
-         * Initializes the property table.
-         */
-        init: function () {
-            initNewPropertyDialog();
-            initProcessorPropertiesTable();
-        },
-        
-        /**
-         * Determines if the specified property is sensitive.
-         * 
-         * @argument {object} propertyDescriptor        The property descriptor
-         */
-        isSensitiveProperty: function (propertyDescriptor) {
-            if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
-                return propertyDescriptor.sensitive === true;
-            } else {
-                return false;
-            }
-        },
-        
-        /**
-         * Determines if the specified property is required.
-         * 
-         * @param {object} propertyDescriptor           The property descriptor
-         */
-        isRequiredProperty: function (propertyDescriptor) {
-            if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
-                return propertyDescriptor.required === true;
-            } else {
-                return false;
-            }
-        },
-        
-        /**
-         * Determines if the specified property is required.
-         * 
-         * @param {object} propertyDescriptor           The property descriptor
-         */
-        isDynamicProperty: function (propertyDescriptor) {
-            if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
-                return propertyDescriptor.dynamic === true;
-            } else {
-                return false;
-            }
-        },
-        
-        /**
-         * Gets the allowable values for the specified property.
-         * 
-         * @argument {object} propertyDescriptor        The property descriptor
-         */
-        getAllowableValues: function (propertyDescriptor) {
-            if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
-                return propertyDescriptor.allowableValues;
-            } else {
-                return null;
-            }
-        },
-        
-        /**
-         * Returns whether the specified property supports EL.
-         * 
-         * @param {object} propertyDescriptor           The property descriptor
-         */
-        supportsEl: function (propertyDescriptor) {
-            if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
-                return propertyDescriptor.supportsEl === true;
-            } else {
-                return false;
-            }
-        },
-        
-        /**
-         * Saves the last edited row in the specified grid.
-         */
-        saveRow: function () {
-            // get the property grid to commit the current edit
-            var propertyGrid = $('#processor-properties').data('gridInstance');
-            if (nf.Common.isDefinedAndNotNull(propertyGrid)) {
-                var editController = propertyGrid.getEditController();
-                editController.commitCurrentEdit();
-            }
-        },
-        
-        /**
-         * Cancels the edit in the specified row.
-         */
-        cancelEdit: function () {
-            // get the property grid to reset the current edit
-            var propertyGrid = $('#processor-properties').data('gridInstance');
-            if (nf.Common.isDefinedAndNotNull(propertyGrid)) {
-                var editController = propertyGrid.getEditController();
-                editController.cancelCurrentEdit();
-            }
-        },
-        
-        /**
-         * Deletes the property in the specified row.
-         * 
-         * @argument {string} row         The row
-         */
-        deleteProperty: function (row) {
-            var propertyGrid = $('#processor-properties').data('gridInstance');
-            if (nf.Common.isDefinedAndNotNull(propertyGrid)) {
-                var propertyData = propertyGrid.getData();
-
-                // update the property in question
-                var property = propertyData.getItem(row);
-                property.hidden = true;
-
-                // refresh the table
-                propertyData.updateItem(property.id, property);
-            }
-        },
-        
-        /**
-         * Update the size of the grid based on its container's current size.
-         */
-        resetTableSize: function () {
-            var propertyGrid = $('#processor-properties').data('gridInstance');
-            if (nf.Common.isDefinedAndNotNull(propertyGrid)) {
-                propertyGrid.resizeCanvas();
-            }
-        },
-        
-        /**
-         * Loads the specified properties.
-         * 
-         * @argument {object} properties        The properties
-         * @argument {map} descriptors          The property descriptors (property name -> property descriptor)
-         */
-        loadProperties: function (properties, descriptors) {
-            // get the property grid
-            var propertyGrid = $('#processor-properties').data('gridInstance');
-            var propertyData = propertyGrid.getData();
-
-            // generate the processor properties
-            if (nf.Common.isDefinedAndNotNull(properties)) {
-                propertyData.beginUpdate();
-
-                var i = 0;
-                $.each(properties, function (name, value) {
-                    // get the property descriptor
-                    var descriptor = descriptors[name];
-
-                    // determine the property type
-                    var type = 'userDefined';
-                    var displayName = name;
-                    if (nf.Common.isDefinedAndNotNull(descriptor)) {
-                        if (nf.ProcessorPropertyTable.isRequiredProperty(descriptor)) {
-                            type = 'required';
-                        } else if (nf.ProcessorPropertyTable.isDynamicProperty(descriptor)) {
-                            type = 'userDefined';
-                        } else {
-                            type = 'optional';
-                        }
-
-                        // use the display name if possible
-                        displayName = descriptor.displayName;
-                        
-                        // determine the value
-                        if (nf.Common.isNull(value) && nf.Common.isDefinedAndNotNull(descriptor.defaultValue)) {
-                            value = descriptor.defaultValue;
-                        }
-                    }
-
-                    // add the row
-                    propertyData.addItem({
-                        id: i++,
-                        hidden: false,
-                        property: name,
-                        displayName: displayName,
-                        previousValue: value,
-                        value: value,
-                        type: type
-                    });
-                });
-
-                propertyData.endUpdate();
-            }
-        },
-        
-        /**
-         * Determines if a save is required.
-         */
-        isSaveRequired: function () {
-            // get the property grid
-            var propertyGrid = $('#processor-properties').data('gridInstance');
-            var propertyData = propertyGrid.getData();
-
-            // determine if any of the processor properties have changed
-            var isSaveRequired = false;
-            $.each(propertyData.getItems(), function () {
-                if (this.value !== this.previousValue) {
-                    isSaveRequired = true;
-                    return false;
-                }
-            });
-            return isSaveRequired;
-        },
-        
-        /**
-         * Marshal's the properties to send to the server.
-         */
-        marshalProperties: function () {
-            // properties
-            var properties = {};
-
-            // get the property grid data
-            var propertyGrid = $('#processor-properties').data('gridInstance');
-            var propertyData = propertyGrid.getData();
-            $.each(propertyData.getItems(), function () {
-                if (this.hidden === true) {
-                    properties[this.property] = null;
-                } else if (this.value !== this.previousValue) {
-                    properties[this.property] = this.value;
-                }
-            });
-
-            return properties;
-        },
-        
-        /**
-         * Clears the property table.
-         */
-        clear: function () {
-            var propertyGridElement = $('#processor-properties');
-            
-            // clean up any tooltips that may have been generated
-            nf.Common.cleanUpTooltips(propertyGridElement, 'img.icon-info');
-            
-            // clear the data in the grid
-            var propertyGrid = propertyGridElement.data('gridInstance');
-            var propertyData = propertyGrid.getData();
-            propertyData.setItems([]);
-        }
-    };
-}());
\ No newline at end of file


[59/62] [abbrv] incubator-nifi git commit: NIFI-504: Removed erroneous copyright notice

Posted by ma...@apache.org.
NIFI-504: Removed erroneous copyright notice


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/512ac9c7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/512ac9c7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/512ac9c7

Branch: refs/heads/NIFI-25
Commit: 512ac9c704e155461dac0c0c905ecccaba645c3b
Parents: 10d002c
Author: Mark Payne <ma...@hotmail.com>
Authored: Fri Apr 10 08:42:43 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Fri Apr 10 08:42:43 2015 -0400

----------------------------------------------------------------------
 .../java/org/apache/nifi/processors/maxmind/DatabaseReader.java    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/512ac9c7/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/src/main/java/org/apache/nifi/processors/maxmind/DatabaseReader.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/src/main/java/org/apache/nifi/processors/maxmind/DatabaseReader.java b/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/src/main/java/org/apache/nifi/processors/maxmind/DatabaseReader.java
index 796a7af..734f093 100644
--- a/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/src/main/java/org/apache/nifi/processors/maxmind/DatabaseReader.java
+++ b/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/src/main/java/org/apache/nifi/processors/maxmind/DatabaseReader.java
@@ -46,7 +46,7 @@ import com.maxmind.geoip2.model.IspResponse;
  * This class was copied from 
  * https://raw.githubusercontent.com/maxmind/GeoIP2-java/master/src/main/java/com/maxmind/geoip2/DatabaseReader.java
  * It is written by Maxmind and it is available under Apache Software License V2
- * Copyright (c) 2013 by MaxMind, Inc.
+ * 
  * The modification we're making to the code below is to stop using exceptions for
  * mainline flow control.  Specifically we don't want to throw an exception
  * simply because an address was not found.


[52/62] [abbrv] incubator-nifi git commit: NIFI-506: Initial import of HL7 work

Posted by ma...@apache.org.
NIFI-506: Initial import of HL7 work


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/45416dc6
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/45416dc6
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/45416dc6

Branch: refs/heads/NIFI-25
Commit: 45416dc66bc212bacf5a8429e4ae2ae880c0672c
Parents: e8fde85
Author: Mark Payne <ma...@hotmail.com>
Authored: Thu Apr 9 17:54:33 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Thu Apr 9 17:54:33 2015 -0400

----------------------------------------------------------------------
 .../nifi-hl7-query-language/.gitignore          |   3 +
 .../nifi-hl7-query-language/pom.xml             | 115 ++++++
 .../apache/nifi/hl7/query/antlr/HL7QueryLexer.g | 156 +++++++
 .../nifi/hl7/query/antlr/HL7QueryParser.g       |  91 ++++
 .../org/apache/nifi/hl7/hapi/EmptyField.java    |  37 ++
 .../org/apache/nifi/hl7/hapi/HapiField.java     |  83 ++++
 .../org/apache/nifi/hl7/hapi/HapiMessage.java   |  94 +++++
 .../org/apache/nifi/hl7/hapi/HapiSegment.java   |  69 ++++
 .../apache/nifi/hl7/hapi/SingleValueField.java  |  42 ++
 .../java/org/apache/nifi/hl7/io/HL7Reader.java  |  27 ++
 .../hl7/io/exception/InvalidHL7Exception.java   |  40 ++
 .../org/apache/nifi/hl7/model/HL7Component.java |  24 ++
 .../org/apache/nifi/hl7/model/HL7Field.java     |  21 +
 .../org/apache/nifi/hl7/model/HL7Message.java   |  27 ++
 .../org/apache/nifi/hl7/model/HL7Segment.java   |  27 ++
 .../org/apache/nifi/hl7/query/Declaration.java  |  29 ++
 .../org/apache/nifi/hl7/query/HL7Query.java     | 412 +++++++++++++++++++
 .../org/apache/nifi/hl7/query/QueryResult.java  |  29 ++
 .../org/apache/nifi/hl7/query/ResultHit.java    |  25 ++
 .../org/apache/nifi/hl7/query/Selection.java    |  37 ++
 .../hl7/query/evaluator/BooleanEvaluator.java   |  24 ++
 .../nifi/hl7/query/evaluator/Evaluator.java     |  27 ++
 .../hl7/query/evaluator/IntegerEvaluator.java   |  26 ++
 .../hl7/query/evaluator/StringEvaluator.java    |  25 ++
 .../comparison/AbstractComparisonEvaluator.java | 106 +++++
 .../comparison/AbstractNumericComparison.java   |  67 +++
 .../evaluator/comparison/EqualsEvaluator.java   |  32 ++
 .../comparison/GreaterThanEvaluator.java        |  34 ++
 .../comparison/GreaterThanOrEqualEvaluator.java |  34 ++
 .../evaluator/comparison/IsNullEvaluator.java   |  69 ++++
 .../evaluator/comparison/LessThanEvaluator.java |  31 ++
 .../comparison/LessThanOrEqualEvaluator.java    |  31 ++
 .../comparison/NotEqualsEvaluator.java          |  32 ++
 .../evaluator/comparison/NotEvaluator.java      |  36 ++
 .../evaluator/comparison/NotNullEvaluator.java  |  65 +++
 .../literal/IntegerLiteralEvaluator.java        |  36 ++
 .../literal/StringLiteralEvaluator.java         |  35 ++
 .../hl7/query/evaluator/logic/AndEvaluator.java |  43 ++
 .../hl7/query/evaluator/logic/OrEvaluator.java  |  43 ++
 .../message/DeclaredReferenceEvaluator.java     |  42 ++
 .../query/evaluator/message/DotEvaluator.java   |  88 ++++
 .../query/evaluator/message/FieldEvaluator.java |  67 +++
 .../evaluator/message/MessageEvaluator.java     |  34 ++
 .../evaluator/message/SegmentEvaluator.java     |  51 +++
 .../exception/HL7QueryParsingException.java     |  37 ++
 .../nifi/hl7/query/result/MissedResult.java     |  56 +++
 .../hl7/query/result/StandardQueryResult.java   |  69 ++++
 .../hl7/query/result/StandardResultHit.java     |  41 ++
 .../org/apache/nifi/hl7/query/TestHL7Query.java | 352 ++++++++++++++++
 .../src/test/resources/hyperglycemia            |   5 +
 .../src/test/resources/hypoglycemia             |   5 +
 .../src/test/resources/metabolic-panel          |  23 ++
 .../resources/unsolicited-vaccine-update-long   |  16 +
 .../resources/unsolicited-vaccine-update-short  |   4 +
 .../src/test/resources/vaccine-query            |   3 +
 .../src/test/resources/vaers-message-long       |  60 +++
 .../nifi-hl7-bundle/nifi-hl7-nar/pom.xml        |  36 ++
 .../nifi-hl7-processors/.gitignore              |   1 +
 .../nifi-hl7-bundle/nifi-hl7-processors/pom.xml | 106 +++++
 .../processors/hl7/ExtractHL7Attributes.java    | 247 +++++++++++
 .../apache/nifi/processors/hl7/RouteHL7.java    | 217 ++++++++++
 .../org.apache.nifi.processor.Processor         |  16 +
 .../hl7/TestExtractHL7Attributes.java           |  48 +++
 .../src/test/resources/1.hl7                    |  16 +
 .../src/test/resources/hypoglycemia.hl7         |   5 +
 nifi/nifi-nar-bundles/nifi-hl7-bundle/pom.xml   |  33 ++
 66 files changed, 3862 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/.gitignore
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/.gitignore b/nifi/nifi-commons/nifi-hl7-query-language/.gitignore
new file mode 100644
index 0000000..e91d5c4
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/.gitignore
@@ -0,0 +1,3 @@
+/target/
+/target/
+/target/

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/pom.xml b/nifi/nifi-commons/nifi-hl7-query-language/pom.xml
new file mode 100644
index 0000000..447a88b
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/pom.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	
+	<parent>
+		<groupId>org.apache.nifi</groupId>
+		<artifactId>nifi-commons</artifactId>
+		<version>0.1.0-incubating-SNAPSHOT</version>
+	</parent>
+	
+	<artifactId>nifi-hl7-query-language</artifactId>
+	<packaging>jar</packaging>
+
+	<name>NiFi Health Level 7 (HL7) Query Language</name>
+
+	<build>
+		<plugins>
+			<plugin>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<configuration>
+					<source>1.7</source>
+					<target>1.7</target>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.antlr</groupId>
+				<artifactId>antlr3-maven-plugin</artifactId>
+				<executions>
+					<execution>
+						<goals>
+							<goal>antlr</goal>
+						</goals>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.antlr</groupId>
+			<artifactId>antlr-runtime</artifactId>
+			<version>3.5.2</version>
+		</dependency>
+		
+		<!-- HAPI to parse v2 messages -->
+		<dependency>
+			<groupId>ca.uhn.hapi</groupId>
+			<artifactId>hapi-base</artifactId>
+			<version>2.2</version>
+		</dependency>
+		<dependency>
+			<groupId>ca.uhn.hapi</groupId>
+			<artifactId>hapi-structures-v21</artifactId>
+			<version>2.2</version>
+		</dependency>
+		<dependency>
+			<groupId>ca.uhn.hapi</groupId>
+			<artifactId>hapi-structures-v22</artifactId>
+			<version>2.2</version>
+		</dependency>
+		<dependency>
+			<groupId>ca.uhn.hapi</groupId>
+			<artifactId>hapi-structures-v23</artifactId>
+			<version>2.2</version>
+		</dependency>
+		<dependency>
+			<groupId>ca.uhn.hapi</groupId>
+			<artifactId>hapi-structures-v231</artifactId>
+			<version>2.2</version>
+		</dependency>
+		<dependency>
+			<groupId>ca.uhn.hapi</groupId>
+			<artifactId>hapi-structures-v24</artifactId>
+			<version>2.2</version>
+		</dependency>
+		<dependency>
+			<groupId>ca.uhn.hapi</groupId>
+			<artifactId>hapi-structures-v25</artifactId>
+			<version>2.2</version>
+		</dependency>
+		<dependency>
+			<groupId>ca.uhn.hapi</groupId>
+			<artifactId>hapi-structures-v251</artifactId>
+			<version>2.2</version>
+		</dependency>
+		<dependency>
+			<groupId>ca.uhn.hapi</groupId>
+			<artifactId>hapi-structures-v26</artifactId>
+			<version>2.2</version>
+		</dependency>
+
+
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/antlr3/org/apache/nifi/hl7/query/antlr/HL7QueryLexer.g
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/antlr3/org/apache/nifi/hl7/query/antlr/HL7QueryLexer.g b/nifi/nifi-commons/nifi-hl7-query-language/src/main/antlr3/org/apache/nifi/hl7/query/antlr/HL7QueryLexer.g
new file mode 100644
index 0000000..7fe3386
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/antlr3/org/apache/nifi/hl7/query/antlr/HL7QueryLexer.g
@@ -0,0 +1,156 @@
+lexer grammar HL7QueryLexer;
+
+@header {
+	package org.apache.nifi.hl7.query.antlr;
+	import org.apache.nifi.hl7.query.exception.HL7QueryParsingException;
+}
+
+@rulecatch {
+  catch(final Exception e) {
+    throw new HL7QueryParsingException(e);
+  }
+}
+
+@members {
+  public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
+    final StringBuilder sb = new StringBuilder();
+    if ( e.token == null ) {
+    	sb.append("Unrecognized token ");
+    } else {
+    	sb.append("Unexpected token '").append(e.token.getText()).append("' ");
+    }
+    sb.append("at line ").append(e.line);
+    if ( e.approximateLineInfo ) {
+    	sb.append(" (approximately)");
+    }
+    sb.append(", column ").append(e.charPositionInLine);
+    sb.append(". Query: ").append(e.input.toString());
+    
+    throw new HL7QueryParsingException(sb.toString());
+  }
+
+  public void recover(RecognitionException e) {
+  	final StringBuilder sb = new StringBuilder();
+    if ( e.token == null ) {
+    	sb.append("Unrecognized token ");
+    } else {
+    	sb.append("Unexpected token '").append(e.token.getText()).append("' ");
+    }
+    sb.append("at line ").append(e.line);
+    if ( e.approximateLineInfo ) {
+    	sb.append(" (approximately)");
+    }
+    sb.append(", column ").append(e.charPositionInLine);
+    sb.append(". Query: ").append(e.input.toString());
+    
+    throw new HL7QueryParsingException(sb.toString());
+  } 
+}
+
+
+// PUNCTUATION & SPECIAL CHARACTERS
+WHITESPACE : (' '|'\t'|'\n'|'\r')+ { $channel = HIDDEN; };
+COMMENT : '#' ( ~('\n') )* '\n' { $channel = HIDDEN; };
+
+LPAREN	: '(';
+RPAREN	: ')';
+LBRACE  : '{';
+RBRACE  : '}';
+COLON	: ':';
+COMMA	: ',';
+DOT		: '.';
+SEMICOLON : ';';
+
+
+
+// OPERATORS
+EQUALS		: '=';
+NOT_EQUALS	: '!=';
+GT			: '>';
+GE			: '>=';
+LT			: '<';
+LE			: '<=';
+REGEX		: 'MATCHES REGEX';
+LIKE		: 'LIKE';
+IS_NULL		: 'IS NULL';
+NOT_NULL	: 'NOT NULL';
+
+
+// KEYWORDS
+AND			: 'AND';
+OR			: 'OR';
+NOT			: 'NOT';
+
+TRUE	: 'true';
+FALSE	: 'false';
+
+SELECT		: 'select' | 'SELECT';
+DECLARE		: 'declare' | 'DECLARE';
+OPTIONAL	: 'optional' | 'OPTIONAL';
+REQUIRED	: 'required' | 'REQUIRED';
+AS			: 'as' | 'AS';
+WHERE		: 'where' | 'WHERE';
+
+MESSAGE 	: 'MESSAGE' | 'message';
+SEGMENT 	: 'SEGMENT' | 'segment';
+
+
+SEGMENT_NAME : LETTER ALPHA_NUMERIC ALPHA_NUMERIC;
+
+
+NUMBER	: ('0'..'9')+;
+fragment LETTER : 'A'..'Z';
+fragment ALPHA_NUMERIC : 'A'..'Z' | '0'..'9';
+
+
+// STRINGS
+STRING_LITERAL
+@init{StringBuilder lBuf = new StringBuilder();}
+	:
+		(
+			'"'
+				(
+					escaped=ESC {lBuf.append(getText());} |
+				  	normal = ~( '"' | '\\' | '\n' | '\r' | '\t' ) { lBuf.appendCodePoint(normal);} 
+				)*
+			'"'
+		)
+		{
+			setText(lBuf.toString());
+		}
+		|
+		(
+			'\''
+				(
+					escaped=ESC {lBuf.append(getText());} |
+				  	normal = ~( '\'' | '\\' | '\n' | '\r' | '\t' ) { lBuf.appendCodePoint(normal);} 
+				)*
+			'\''
+		)
+		{
+			setText(lBuf.toString());
+		}
+		;
+
+
+fragment
+ESC
+	:	'\\'
+		(
+				'"'		{ setText("\""); }
+			|	'\''	{ setText("\'"); }
+			|	'r'		{ setText("\r"); }
+			|	'n'		{ setText("\n"); }
+			|	't'		{ setText("\t"); }
+			|	'\\'	{ setText("\\\\"); }
+			|	nextChar = ~('"' | '\'' | 'r' | 'n' | 't' | '\\')		
+				{
+					StringBuilder lBuf = new StringBuilder(); lBuf.append("\\\\").appendCodePoint(nextChar); setText(lBuf.toString());
+				}
+		)
+	;
+
+IDENTIFIER : (
+				  ~('$' | '{' | '}' | '(' | ')' | '[' | ']' | ',' | ':' | ';' | '/' | '*' | '\'' | ' ' | '\t' | '\r' | '\n' | '0'..'9' | '.')
+				  ~('$' | '{' | '}' | '(' | ')' | '[' | ']' | ',' | ':' | ';' | '/' | '*' | '\'' | ' ' | '\t' | '\r' | '\n' | '.')*
+				 );

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/antlr3/org/apache/nifi/hl7/query/antlr/HL7QueryParser.g
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/antlr3/org/apache/nifi/hl7/query/antlr/HL7QueryParser.g b/nifi/nifi-commons/nifi-hl7-query-language/src/main/antlr3/org/apache/nifi/hl7/query/antlr/HL7QueryParser.g
new file mode 100644
index 0000000..4d8d13c
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/antlr3/org/apache/nifi/hl7/query/antlr/HL7QueryParser.g
@@ -0,0 +1,91 @@
+parser grammar HL7QueryParser;
+
+options {
+	output=AST;
+	tokenVocab=HL7QueryLexer;
+}
+
+tokens {
+	QUERY;
+	DECLARATION;
+}
+
+@header {
+	package org.apache.nifi.hl7.query.antlr;
+	import org.apache.nifi.hl7.query.exception.HL7QueryParsingException;
+}
+
+@members {
+  public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
+  	final StringBuilder sb = new StringBuilder();
+    if ( e.token == null ) {
+    	sb.append("Unrecognized token ");
+    } else {
+    	sb.append("Unexpected token '").append(e.token.getText()).append("' ");
+    }
+    sb.append("at line ").append(e.line);
+    if ( e.approximateLineInfo ) {
+    	sb.append(" (approximately)");
+    }
+    sb.append(", column ").append(e.charPositionInLine);
+    sb.append(". Query: ").append(e.input.toString());
+    
+    throw new HL7QueryParsingException(sb.toString());
+  }
+
+  public void recover(final RecognitionException e) {
+  	final StringBuilder sb = new StringBuilder();
+    if ( e.token == null ) {
+    	sb.append("Unrecognized token ");
+    } else {
+    	sb.append("Unexpected token '").append(e.token.getText()).append("' ");
+    }
+    sb.append("at line ").append(e.line);
+    if ( e.approximateLineInfo ) {
+    	sb.append(" (approximately)");
+    }
+    sb.append(", column ").append(e.charPositionInLine);
+    sb.append(". Query: ").append(e.input.toString());
+    
+    throw new HL7QueryParsingException(sb.toString());
+  } 
+}
+
+
+declareClause : DECLARE^ declaration (COMMA! declaration)*;
+
+requiredOrOptional : REQUIRED | OPTIONAL;
+declaration : IDENTIFIER AS requiredOrOptional SEGMENT_NAME ->
+	^(DECLARATION IDENTIFIER requiredOrOptional SEGMENT_NAME);
+
+
+selectClause : SELECT^ selectableClause;
+selectableClause : selectable (COMMA! selectable)*;
+selectable : (MESSAGE | ref | field)^ (AS! IDENTIFIER^)?;
+
+
+whereClause : WHERE^ conditions;
+
+conditions : condition ((AND^ | OR^) condition)*;
+
+condition : NOT^ condition | LPAREN! conditions RPAREN! | evaluation;
+
+evaluation : expression
+			 (
+			 	unaryOperator^
+			 	| (binaryOperator^ expression)
+			 );
+
+expression : (LPAREN! expr RPAREN!) | expr;
+expr : ref | field | STRING_LITERAL | NUMBER;
+
+unaryOperator : IS_NULL | NOT_NULL;
+binaryOperator : EQUALS | NOT_EQUALS | LT | GT | LE | GE;
+
+ref : (SEGMENT_NAME | IDENTIFIER);
+field : ref DOT^ NUMBER 
+	(DOT^ NUMBER (DOT^ NUMBER (DOT^ NUMBER)?)?)?;
+
+
+query : declareClause? selectClause whereClause? EOF ->
+	^(QUERY declareClause? selectClause whereClause?);

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/EmptyField.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/EmptyField.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/EmptyField.java
new file mode 100644
index 0000000..be645e5
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/EmptyField.java
@@ -0,0 +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.hl7.hapi;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.nifi.hl7.model.HL7Component;
+import org.apache.nifi.hl7.model.HL7Field;
+
+public class EmptyField implements HL7Field {
+
+	@Override
+	public String getValue() {
+		return null;
+	}
+
+	@Override
+	public List<HL7Component> getComponents() {
+		return Collections.emptyList();
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiField.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiField.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiField.java
new file mode 100644
index 0000000..056b6b6
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiField.java
@@ -0,0 +1,83 @@
+/*
+ * 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.hl7.hapi;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.nifi.hl7.model.HL7Component;
+import org.apache.nifi.hl7.model.HL7Field;
+
+import ca.uhn.hl7v2.model.Composite;
+import ca.uhn.hl7v2.model.ExtraComponents;
+import ca.uhn.hl7v2.model.Primitive;
+import ca.uhn.hl7v2.model.Type;
+import ca.uhn.hl7v2.model.Varies;
+import ca.uhn.hl7v2.parser.EncodingCharacters;
+import ca.uhn.hl7v2.parser.PipeParser;
+
+public class HapiField implements HL7Field, HL7Component {
+	private final String value;
+	private final List<HL7Component> components;
+	
+	public HapiField(final Type type) {
+		this.value = PipeParser.encode(type, EncodingCharacters.defaultInstance());
+		
+		final List<HL7Component> componentList = new ArrayList<>();
+		if ( type instanceof Composite ) {
+			final Composite composite = (Composite) type;
+			
+			for ( final Type component : composite.getComponents() ) {
+				componentList.add(new HapiField(component));
+			}
+		}
+		
+		final ExtraComponents extra = type.getExtraComponents();
+		if ( extra != null && extra.numComponents() > 0 ) {
+			final String singleFieldValue;
+			if ( type instanceof Primitive ) {
+				singleFieldValue = ((Primitive) type).getValue();
+			} else {
+				singleFieldValue = this.value;
+			}
+			componentList.add(new SingleValueField(singleFieldValue));
+			
+			for (int i=0; i < extra.numComponents(); i++) {
+				final Varies varies = extra.getComponent(i);
+				componentList.add(new HapiField(varies));
+			}
+		}
+		
+		this.components = Collections.unmodifiableList(componentList);
+	}
+	
+	@Override
+	public String getValue() {
+		return value;
+	}
+
+	@Override
+	public List<HL7Component> getComponents() {
+		return components;
+	}
+	
+	@Override
+	public String toString() {
+		return value;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiMessage.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiMessage.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiMessage.java
new file mode 100644
index 0000000..ddd28b2
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiMessage.java
@@ -0,0 +1,94 @@
+/*
+ * 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.hl7.hapi;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.nifi.hl7.model.HL7Message;
+import org.apache.nifi.hl7.model.HL7Segment;
+
+import ca.uhn.hl7v2.HL7Exception;
+import ca.uhn.hl7v2.model.Group;
+import ca.uhn.hl7v2.model.Message;
+import ca.uhn.hl7v2.model.Segment;
+import ca.uhn.hl7v2.model.Structure;
+
+public class HapiMessage implements HL7Message {
+	private final Message message;
+	private final List<HL7Segment> allSegments;
+	private final Map<String, List<HL7Segment>> segmentMap;
+	
+	public HapiMessage(final Message message) throws HL7Exception {
+		this.message = message;
+		
+		allSegments = new ArrayList<>();
+		populateSegments(message, allSegments);
+		
+		segmentMap = new HashMap<>();
+		for ( final HL7Segment segment : allSegments ) {
+			final String segmentName = segment.getName();
+			List<HL7Segment> segmentList = segmentMap.get(segmentName);
+			if ( segmentList == null ) {
+				segmentList = new ArrayList<>();
+				segmentMap.put(segmentName, segmentList);
+			}
+			
+			segmentList.add(segment);
+		}
+	}
+	
+	private void populateSegments(final Group group, final List<HL7Segment> segments) throws HL7Exception {
+		for ( final String structureName : group.getNames() ) {
+			final Structure[] structures = group.getAll(structureName);
+			if ( group.isGroup(structureName) ) {
+				for ( final Structure structure : structures ) {
+					populateSegments((Group) structure, segments);
+				}
+			} else {
+				for ( final Structure structure : structures ) {
+					final Segment segment = (Segment) structure;
+					final HapiSegment hapiSegment = new HapiSegment(segment);
+					segments.add(hapiSegment);
+				}
+			}
+		}
+	}
+	
+	@Override
+	public List<HL7Segment> getSegments() {
+		return Collections.unmodifiableList(allSegments);
+	}
+
+	@Override
+	public List<HL7Segment> getSegments(final String segmentType) {
+		final List<HL7Segment> segments = segmentMap.get(segmentType);
+		if ( segments == null ) {
+			return Collections.emptyList();
+		}
+		
+		return Collections.unmodifiableList(segments);
+	}
+
+	@Override
+	public String toString() {
+		return message.toString();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiSegment.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiSegment.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiSegment.java
new file mode 100644
index 0000000..d50afdb
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/HapiSegment.java
@@ -0,0 +1,69 @@
+/*
+ * 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.hl7.hapi;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.nifi.hl7.model.HL7Field;
+import org.apache.nifi.hl7.model.HL7Segment;
+
+import ca.uhn.hl7v2.HL7Exception;
+import ca.uhn.hl7v2.model.Segment;
+import ca.uhn.hl7v2.model.Type;
+
+public class HapiSegment implements HL7Segment {
+	private final Segment segment;
+	private final List<HL7Field> fields;
+	
+	public HapiSegment(final Segment segment) throws HL7Exception {
+		this.segment = segment;
+		
+		final List<HL7Field> fieldList = new ArrayList<>();
+		for (int i=1; i <= segment.numFields(); i++) {
+			final Type[] types = segment.getField(i);
+
+			if ( types == null || types.length == 0 ) {
+				fieldList.add(new EmptyField());
+				continue;
+			}
+			
+			for ( final Type type : types ) {
+				fieldList.add(new HapiField(type));
+			}
+		}
+		
+		this.fields = Collections.unmodifiableList(fieldList);
+	}
+	
+	
+	@Override
+	public String getName() {
+		return segment.getName();
+	}
+
+	@Override
+	public List<HL7Field> getFields() {
+		return fields;
+	}
+
+	@Override
+	public String toString() {
+		return segment.toString();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/SingleValueField.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/SingleValueField.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/SingleValueField.java
new file mode 100644
index 0000000..ed99077
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/hapi/SingleValueField.java
@@ -0,0 +1,42 @@
+/*
+ * 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.hl7.hapi;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.nifi.hl7.model.HL7Component;
+import org.apache.nifi.hl7.model.HL7Field;
+
+public class SingleValueField implements HL7Field {
+	private final String value;
+	
+	public SingleValueField(final String value) {
+		this.value = value;
+	}
+	
+	@Override
+	public String getValue() {
+		return value;
+	}
+
+	@Override
+	public List<HL7Component> getComponents() {
+		return Collections.emptyList();
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/io/HL7Reader.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/io/HL7Reader.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/io/HL7Reader.java
new file mode 100644
index 0000000..e7b31a4
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/io/HL7Reader.java
@@ -0,0 +1,27 @@
+/*
+ * 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.hl7.io;
+
+import java.io.IOException;
+
+import org.apache.nifi.hl7.model.HL7Message;
+
+public interface HL7Reader {
+
+	HL7Message nextMessage() throws IOException;
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/io/exception/InvalidHL7Exception.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/io/exception/InvalidHL7Exception.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/io/exception/InvalidHL7Exception.java
new file mode 100644
index 0000000..669f40c
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/io/exception/InvalidHL7Exception.java
@@ -0,0 +1,40 @@
+/*
+ * 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.hl7.io.exception;
+
+import java.io.IOException;
+
+public class InvalidHL7Exception extends IOException {
+	private static final long serialVersionUID = -5675416667224562441L;
+
+	public InvalidHL7Exception() {
+		super();
+	}
+
+	public InvalidHL7Exception(String message, Throwable cause) {
+		super(message, cause);
+	}
+
+	public InvalidHL7Exception(String message) {
+		super(message);
+	}
+
+	public InvalidHL7Exception(Throwable cause) {
+		super(cause);
+	}
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Component.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Component.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Component.java
new file mode 100644
index 0000000..cf35504
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Component.java
@@ -0,0 +1,24 @@
+/*
+ * 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.hl7.model;
+
+import java.util.List;
+
+public interface HL7Component {
+	String getValue();
+	List<HL7Component> getComponents();
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Field.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Field.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Field.java
new file mode 100644
index 0000000..4086e58
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Field.java
@@ -0,0 +1,21 @@
+/*
+ * 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.hl7.model;
+
+
+public interface HL7Field extends HL7Component {
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Message.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Message.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Message.java
new file mode 100644
index 0000000..dd9c2a9
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Message.java
@@ -0,0 +1,27 @@
+/*
+ * 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.hl7.model;
+
+import java.util.List;
+
+public interface HL7Message {
+
+	List<HL7Segment> getSegments();
+	
+	List<HL7Segment> getSegments(String segmentType);
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Segment.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Segment.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Segment.java
new file mode 100644
index 0000000..de5aaa1
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/model/HL7Segment.java
@@ -0,0 +1,27 @@
+/*
+ * 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.hl7.model;
+
+import java.util.List;
+
+public interface HL7Segment {
+
+	String getName();
+	
+	List<HL7Field> getFields();
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/Declaration.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/Declaration.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/Declaration.java
new file mode 100644
index 0000000..0903cc8
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/Declaration.java
@@ -0,0 +1,29 @@
+/*
+ * 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.hl7.query;
+
+import org.apache.nifi.hl7.model.HL7Message;
+
+public interface Declaration {
+
+	String getAlias();
+	
+	boolean isRequired();
+	
+	Object getDeclaredValue(HL7Message message);
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/HL7Query.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/HL7Query.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/HL7Query.java
new file mode 100644
index 0000000..a036106
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/HL7Query.java
@@ -0,0 +1,412 @@
+/*
+ * 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.hl7.query;
+
+import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.antlr.runtime.ANTLRStringStream;
+import org.antlr.runtime.CharStream;
+import org.antlr.runtime.CommonTokenStream;
+import org.antlr.runtime.tree.Tree;
+import org.apache.nifi.hl7.model.HL7Message;
+import org.apache.nifi.hl7.query.evaluator.BooleanEvaluator;
+import org.apache.nifi.hl7.query.evaluator.Evaluator;
+import org.apache.nifi.hl7.query.evaluator.IntegerEvaluator;
+import org.apache.nifi.hl7.query.evaluator.comparison.EqualsEvaluator;
+import org.apache.nifi.hl7.query.evaluator.comparison.GreaterThanEvaluator;
+import org.apache.nifi.hl7.query.evaluator.comparison.GreaterThanOrEqualEvaluator;
+import org.apache.nifi.hl7.query.evaluator.comparison.IsNullEvaluator;
+import org.apache.nifi.hl7.query.evaluator.comparison.LessThanEvaluator;
+import org.apache.nifi.hl7.query.evaluator.comparison.LessThanOrEqualEvaluator;
+import org.apache.nifi.hl7.query.evaluator.comparison.NotEqualsEvaluator;
+import org.apache.nifi.hl7.query.evaluator.comparison.NotEvaluator;
+import org.apache.nifi.hl7.query.evaluator.comparison.NotNullEvaluator;
+import org.apache.nifi.hl7.query.evaluator.literal.IntegerLiteralEvaluator;
+import org.apache.nifi.hl7.query.evaluator.literal.StringLiteralEvaluator;
+import org.apache.nifi.hl7.query.evaluator.logic.AndEvaluator;
+import org.apache.nifi.hl7.query.evaluator.logic.OrEvaluator;
+import org.apache.nifi.hl7.query.evaluator.message.DeclaredReferenceEvaluator;
+import org.apache.nifi.hl7.query.evaluator.message.DotEvaluator;
+import org.apache.nifi.hl7.query.evaluator.message.MessageEvaluator;
+import org.apache.nifi.hl7.query.evaluator.message.SegmentEvaluator;
+import org.apache.nifi.hl7.query.exception.HL7QueryParsingException;
+import org.apache.nifi.hl7.query.result.MissedResult;
+import org.apache.nifi.hl7.query.result.StandardQueryResult;
+
+import org.apache.nifi.hl7.query.antlr.HL7QueryLexer;
+import org.apache.nifi.hl7.query.antlr.HL7QueryParser;
+
+
+public class HL7Query {
+	private final Tree tree;
+	private final String query;
+	private final Set<Declaration> declarations = new HashSet<>();
+	
+	private final List<Selection> selections;
+	private final BooleanEvaluator whereEvaluator;
+	
+	private HL7Query(final Tree tree, final String query) {
+		this.tree = tree;
+		this.query = query;
+
+		List<Selection> select = null;
+		BooleanEvaluator where = null;
+		for (int i=0; i < tree.getChildCount(); i++) {
+			final Tree child = tree.getChild(i);
+			
+			switch (child.getType()) {
+				case DECLARE:
+					processDeclare(child);
+					break;
+				case SELECT:
+					select = processSelect(child);
+					break;
+				case WHERE:
+					where = processWhere(child);
+					break;
+				default:
+					throw new HL7QueryParsingException("Found unexpected clause at root level: " + tree.getText());
+			}
+		}
+		
+		this.whereEvaluator = where;
+		this.selections = select;
+	}
+	
+	private void processDeclare(final Tree declare) {
+		for (int i=0; i < declare.getChildCount(); i++) {
+			final Tree declarationTree = declare.getChild(i);
+			
+			final String identifier = declarationTree.getChild(0).getText();
+			final Tree requiredOrOptionalTree = declarationTree.getChild(1);
+			final boolean required = requiredOrOptionalTree.getType() == REQUIRED;
+			
+			final String segmentName = declarationTree.getChild(2).getText();
+			
+			final Declaration declaration = new Declaration() {
+				@Override
+				public String getAlias() {
+					return identifier;
+				}
+
+				@Override
+				public boolean isRequired() {
+					return required;
+				}
+
+				@Override
+				public Object getDeclaredValue(final HL7Message message) {
+					if ( message == null ) {
+						return null;
+					}
+					
+					return message.getSegments(segmentName);
+				}
+			};
+			
+			declarations.add(declaration);
+		}
+	}
+
+	private List<Selection> processSelect(final Tree select) {
+		final List<Selection> selections = new ArrayList<>();
+
+		for (int i=0; i < select.getChildCount(); i++) {
+			final Tree selectable = select.getChild(i);
+			
+			final String alias = getSelectedName(selectable);
+			final Evaluator<?> selectionEvaluator = buildReferenceEvaluator(selectable);
+			final Selection selection = new Selection(selectionEvaluator, alias);
+			selections.add(selection);
+		}
+		
+		return selections;
+	}
+	
+	
+	private String getSelectedName(final Tree selectable) {
+		if ( selectable.getChildCount() == 0 ) {
+			return selectable.getText();
+		} else if (selectable.getType() == DOT ) {
+			return getSelectedName(selectable.getChild(0)) + "." + getSelectedName(selectable.getChild(1));
+		} else {
+			return selectable.getChild(selectable.getChildCount() - 1).getText();
+		}
+	}
+	
+
+	private BooleanEvaluator processWhere(final Tree where) {
+		return buildBooleanEvaluator(where.getChild(0));
+	}
+	
+
+	private Evaluator<?> buildReferenceEvaluator(final Tree tree) {
+		switch (tree.getType()) {
+			case MESSAGE:
+				return new MessageEvaluator();
+			case SEGMENT_NAME:
+				return new SegmentEvaluator(new StringLiteralEvaluator(tree.getText()));
+			case IDENTIFIER:
+				return new DeclaredReferenceEvaluator(new StringLiteralEvaluator(tree.getText()));
+			case DOT:
+				final Tree firstChild = tree.getChild(0);
+				final Tree secondChild = tree.getChild(1);
+				return new DotEvaluator(buildReferenceEvaluator(firstChild), buildIntegerEvaluator(secondChild));
+			case STRING_LITERAL:
+				return new StringLiteralEvaluator(tree.getText());
+			case NUMBER:
+				return new IntegerLiteralEvaluator(Integer.parseInt(tree.getText()));
+			default:
+				throw new HL7QueryParsingException("Failed to build evaluator for " + tree.getText());
+		}
+	}
+	
+	
+	private IntegerEvaluator buildIntegerEvaluator(final Tree tree) {
+		switch (tree.getType()) {
+			case NUMBER:
+				return new IntegerLiteralEvaluator(Integer.parseInt(tree.getText()));
+			default:
+				throw new HL7QueryParsingException("Failed to build Integer Evaluator for " + tree.getText());
+		}
+	}
+	
+	
+	private BooleanEvaluator buildBooleanEvaluator(final Tree tree) {
+		// TODO: add Date comparisons
+		// LT/GT/GE/GE should allow for dates based on Field's Type
+		// BETWEEN
+		// DATE('2015/01/01')
+		// DATE('2015/01/01 12:00:00')
+		// DATE('24 HOURS AGO')
+		// DATE('YESTERDAY')
+		
+		switch (tree.getType()) {
+			case EQUALS:
+				return new EqualsEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1)));
+			case NOT_EQUALS:
+				return new NotEqualsEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1)));
+			case GT:
+				return new GreaterThanEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1)));
+			case LT:
+				return new LessThanEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1)));
+			case GE:
+				return new GreaterThanOrEqualEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1)));
+			case LE:
+				return new LessThanOrEqualEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1)));
+			case NOT:
+				return new NotEvaluator(buildBooleanEvaluator(tree.getChild(0)));
+			case AND:
+				return new AndEvaluator(buildBooleanEvaluator(tree.getChild(0)), buildBooleanEvaluator(tree.getChild(1)));
+			case OR:
+				return new OrEvaluator(buildBooleanEvaluator(tree.getChild(0)), buildBooleanEvaluator(tree.getChild(1)));
+			case IS_NULL:
+				return new IsNullEvaluator(buildReferenceEvaluator(tree.getChild(0)));
+			case NOT_NULL:
+				return new NotNullEvaluator(buildReferenceEvaluator(tree.getChild(0)));
+			default:
+				throw new HL7QueryParsingException("Cannot build boolean evaluator for '" + tree.getText() + "'");
+		}
+	}
+	
+	
+	Tree getTree() {
+		return tree;
+	}
+	
+	public String getQuery() {
+		return query;
+	}
+	
+	@Override
+	public String toString() {
+		return "HL7Query[" + query + "]";
+	}
+	
+	public static HL7Query compile(final String query) {
+		try {
+            final CommonTokenStream lexerTokenStream = createTokenStream(query);
+            final HL7QueryParser parser = new HL7QueryParser(lexerTokenStream);
+            final Tree tree = (Tree) parser.query().getTree();
+
+            return new HL7Query(tree, query);
+        } catch (final HL7QueryParsingException e) {
+            throw e;
+        } catch (final Exception e) {
+            throw new HL7QueryParsingException(e);
+        }
+	}
+	
+	private static CommonTokenStream createTokenStream(final String expression) throws HL7QueryParsingException {
+        final CharStream input = new ANTLRStringStream(expression);
+        final HL7QueryLexer lexer = new HL7QueryLexer(input);
+        return new CommonTokenStream(lexer);
+    }
+	
+	public List<Class<?>> getReturnTypes() {
+		final List<Class<?>> returnTypes = new ArrayList<>();
+		
+		for ( final Selection selection : selections ) {
+			returnTypes.add( selection.getEvaluator().getType() );
+		}
+		
+		return returnTypes;
+	}
+	
+	@SuppressWarnings("unchecked")
+	public QueryResult evaluate(final HL7Message message) {
+		
+		int totalIterations = 1;
+		final LinkedHashMap<String, List<Object>> possibleValueMap = new LinkedHashMap<>();
+		for ( final Declaration declaration : declarations ) {
+			final Object value = declaration.getDeclaredValue(message);
+			if ( value == null && declaration.isRequired() ) {
+				return new MissedResult(selections);
+			}
+
+			final List<Object> possibleValues;
+			if ( value instanceof List ) {
+				possibleValues = (List<Object>) value;
+			} else if ( value instanceof Collection ) {
+				possibleValues = new ArrayList<Object>((Collection<Object>) value);
+			} else {
+				possibleValues = new ArrayList<>(1);
+				possibleValues.add(value);
+			}
+			
+			if ( possibleValues.isEmpty() ) {
+				return new MissedResult(selections);
+			}
+			
+			possibleValueMap.put(declaration.getAlias(), possibleValues);
+			totalIterations *= possibleValues.size();
+		}
+
+		final Set<Map<String, Object>> resultSet = new HashSet<>();
+		for (int i=0; i < totalIterations; i++) {
+			final Map<String, Object> aliasValues = assignAliases(possibleValueMap, i);
+
+			aliasValues.put(Evaluator.MESSAGE_KEY, message);
+			if (whereEvaluator == null || Boolean.TRUE.equals(whereEvaluator.evaluate(aliasValues))) {
+				final Map<String, Object> resultMap = new HashMap<>();
+
+				for ( final Selection selection : selections ) {
+					final Object value = selection.getEvaluator().evaluate(aliasValues);
+					resultMap.put(selection.getName(), value);
+				}
+				
+				resultSet.add(resultMap);
+			}
+		}
+		
+//		for ( final Declaration declaration : declarations ) {
+//			final Object value = declaration.getDeclaredValue(message);
+//			if ( value == null && declaration.isRequired() ) {
+//				return new MissedResult(selections);
+//			}
+//			objectMap.put(declaration.getAlias(), value);
+//		}
+//		
+//		if (whereEvaluator == null || Boolean.TRUE.equals(whereEvaluator.evaluate(objectMap))) {
+//			for ( final Selection selection : selections ) {
+//				final Object value = selection.getEvaluator().evaluate(objectMap);
+//				resultMap.put(selection.getName(), value);
+//			}
+//		} else {
+//			return new MissedResult(selections);
+//		}
+		
+		return new StandardQueryResult(selections, resultSet);
+	}
+	
+	
+	// assigns one of the possible values to each alias, based on which iteration this is.
+	// require LinkedHashMap just to be very clear and explicit that the order of the Map MUST be guaranteed
+	// between multiple invocations of this method.
+	// package protected for testing visibility
+//	static Map<String, Object> assignAliases(final LinkedHashMap<String, List<Object>> possibleValues, final int iteration) {
+//		final Map<String, Object> aliasMap = new HashMap<>();
+//		
+//		int aliasIndex = possibleValues.size() - 1;
+//		for ( final Map.Entry<String, List<Object>> entry : possibleValues.entrySet() ) {
+//			final String alias = entry.getKey();
+//			final List<Object> validValues = entry.getValue();
+//
+//			final int validValueIndex;
+//			if (aliasIndex > 0) {
+//				validValueIndex = iteration / aliasIndex;
+//			} else {
+//				validValueIndex = iteration;
+//			}
+//			
+//			final Object obj = validValues.get(validValueIndex % validValues.size());
+//			aliasMap.put(alias, obj);
+//			
+//			aliasIndex--;
+//		}
+//		
+//		return aliasMap;
+//	}
+//	
+	
+	static Map<String, Object> assignAliases(final LinkedHashMap<String, List<Object>> possibleValues, final int iteration) {
+		final Map<String, Object> aliasMap = new HashMap<>();
+		
+		int divisor = 1;
+		for ( final Map.Entry<String, List<Object>> entry : possibleValues.entrySet() ) {
+			final String alias = entry.getKey();
+			final List<Object> validValues = entry.getValue();
+
+			final int idx = (iteration / divisor) % validValues.size();
+			final Object obj = validValues.get(idx);
+			aliasMap.put(alias, obj);
+			
+			divisor *= validValues.size();
+		}
+		
+		return aliasMap;
+	}
+	
+	public String toTreeString() {
+		final StringBuilder sb = new StringBuilder();
+		toTreeString(tree, sb, 0);
+		return sb.toString();
+	}
+	
+	private void toTreeString(final Tree tree, final StringBuilder sb, final int indentLevel) {
+		final String nodeName = tree.getText();
+		for (int i=0; i < indentLevel; i++) {
+			sb.append(" ");
+		}
+		sb.append(nodeName);
+		sb.append("\n");
+		
+		for (int i=0; i < tree.getChildCount(); i++) {
+			final Tree child = tree.getChild(i);
+			toTreeString(child, sb, indentLevel + 2);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/QueryResult.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/QueryResult.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/QueryResult.java
new file mode 100644
index 0000000..b198bc7
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/QueryResult.java
@@ -0,0 +1,29 @@
+/*
+ * 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.hl7.query;
+
+import java.util.List;
+
+public interface QueryResult {
+	boolean isMatch();
+	
+	List<String> getLabels();
+	
+	int getHitCount();
+	
+	ResultHit nextHit();
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/ResultHit.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/ResultHit.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/ResultHit.java
new file mode 100644
index 0000000..ee97e5d
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/ResultHit.java
@@ -0,0 +1,25 @@
+/*
+ * 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.hl7.query;
+
+import java.util.Map;
+
+public interface ResultHit {
+	Object getValue(String label);
+	
+	Map<String, Object> getSelectedValues();
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/Selection.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/Selection.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/Selection.java
new file mode 100644
index 0000000..36a181f
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/Selection.java
@@ -0,0 +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.hl7.query;
+
+import org.apache.nifi.hl7.query.evaluator.Evaluator;
+
+public class Selection {
+	private final Evaluator<?> evaluator;
+	private final String name;
+	
+	public Selection(final Evaluator<?> evaluator, final String name) {
+		this.evaluator = evaluator;
+		this.name = name;
+	}
+	
+	public String getName() {
+		return name;
+	}
+	
+	public Evaluator<?> getEvaluator() {
+		return evaluator;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/BooleanEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/BooleanEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/BooleanEvaluator.java
new file mode 100644
index 0000000..fdd807e
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/BooleanEvaluator.java
@@ -0,0 +1,24 @@
+/*
+ * 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.hl7.query.evaluator;
+
+public abstract class BooleanEvaluator implements Evaluator<Boolean> {
+
+	public Class<? extends Boolean> getType() {
+		return Boolean.class;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/Evaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/Evaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/Evaluator.java
new file mode 100644
index 0000000..d86c30e
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/Evaluator.java
@@ -0,0 +1,27 @@
+/*
+ * 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.hl7.query.evaluator;
+
+import java.util.Map;
+
+public interface Evaluator<T> {
+	public static final String MESSAGE_KEY = "message";
+	
+	T evaluate(Map<String, Object> objectMap);
+	
+	Class<? extends T> getType();
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/IntegerEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/IntegerEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/IntegerEvaluator.java
new file mode 100644
index 0000000..6afa9ed
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/IntegerEvaluator.java
@@ -0,0 +1,26 @@
+/*
+ * 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.hl7.query.evaluator;
+
+
+public abstract class IntegerEvaluator implements Evaluator<Integer> {
+
+	public Class<? extends Integer> getType() {
+		return Integer.class;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/StringEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/StringEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/StringEvaluator.java
new file mode 100644
index 0000000..5f73649
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/StringEvaluator.java
@@ -0,0 +1,25 @@
+/*
+ * 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.hl7.query.evaluator;
+
+public abstract class StringEvaluator implements Evaluator<String> {
+
+	public Class<? extends String> getType() {
+		return String.class;
+	}
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/AbstractComparisonEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/AbstractComparisonEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/AbstractComparisonEvaluator.java
new file mode 100644
index 0000000..a7fa1b7
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/AbstractComparisonEvaluator.java
@@ -0,0 +1,106 @@
+/*
+ * 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.hl7.query.evaluator.comparison;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.nifi.hl7.model.HL7Field;
+import org.apache.nifi.hl7.query.evaluator.BooleanEvaluator;
+import org.apache.nifi.hl7.query.evaluator.Evaluator;
+
+public abstract class AbstractComparisonEvaluator extends BooleanEvaluator {
+	private final Evaluator<?> lhs;
+	private final Evaluator<?> rhs;
+	
+	public AbstractComparisonEvaluator(final Evaluator<?> lhs, final Evaluator<?> rhs) {
+		this.lhs = lhs;
+		this.rhs = rhs;
+	}
+	
+	public final Boolean evaluate(final Map<String, Object> objectMap) {
+		final Object lhsValue = lhs.evaluate(objectMap);
+		if ( lhsValue == null ) {
+			return false;
+		}
+		
+		final Object rhsValue = rhs.evaluate(objectMap);
+		if ( rhsValue == null ) {
+			return false;
+		}
+		
+		return compareRaw(lhsValue, rhsValue);
+	}
+	
+	
+	private Boolean compareRaw(Object lhsValue, Object rhsValue) {
+		if ( lhsValue == null || rhsValue == null ) {
+			return false;
+		}
+
+		if ( lhsValue instanceof HL7Field ) {
+			lhsValue = ((HL7Field) lhsValue).getValue();
+		}
+		
+		if ( rhsValue instanceof HL7Field ) {
+			rhsValue = ((HL7Field) rhsValue).getValue();
+		}
+
+		if ( lhsValue == null || rhsValue == null ) {
+			return false;
+		}
+		
+		// both are collections, and compare(lhsValue, rhsValue) is false.
+		// this would be the case, for instance, if we compared field 1 of one segment to 
+		// a field in another segment, and both fields had components.
+		if ( lhsValue instanceof Collection && rhsValue instanceof Collection ) {
+			return false;
+		}
+		
+		// if one side is a collection but the other is not, check if any element in that
+		// collection compares to the other element in a way that satisfies the condition.
+		// this would happen, for instance, if we check Segment1.Field5 = 'X' and field 5 repeats
+		// with a value "A~B~C~X~Y~Z"; in this case we do want to consider Field 5 = X as true.
+		if ( lhsValue instanceof Collection ) {
+			for ( final Object lhsObject : (Collection<?>) lhsValue ) {
+				if ( compareRaw(lhsObject, rhsValue) ) {
+					return true;
+				}
+			}
+			
+			return false;
+		}
+		
+		if ( rhsValue instanceof Collection ) {
+			for ( final Object rhsObject : (Collection<?>) rhsValue ) {
+				if ( compareRaw(rhsObject, lhsValue) ) {
+					return true;
+				}
+			}
+			
+			return false;
+		}
+		
+		if ( lhsValue != null && rhsValue != null && compare(lhsValue, rhsValue) ) {
+			return true;
+		}
+		
+		return false;
+	}
+	
+	protected abstract boolean compare(Object lhs, Object rhs);
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/AbstractNumericComparison.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/AbstractNumericComparison.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/AbstractNumericComparison.java
new file mode 100644
index 0000000..2529c49
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/AbstractNumericComparison.java
@@ -0,0 +1,67 @@
+/*
+ * 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.hl7.query.evaluator.comparison;
+
+import java.util.regex.Pattern;
+
+import org.apache.nifi.hl7.query.evaluator.Evaluator;
+
+public abstract class AbstractNumericComparison extends AbstractComparisonEvaluator {
+	private static final Pattern NUMERIC_PATTERN = Pattern.compile("\\d+(\\.\\d+)?");
+	
+	public AbstractNumericComparison(final Evaluator<?> lhs, final Evaluator<?> rhs) {
+		super(lhs, rhs);
+	}
+	
+	@Override
+	protected final boolean compare(final Object lhs, final Object rhs) {
+		final Double lhsDouble = toDouble(lhs);
+		if ( lhsDouble == null ) {
+			return false;
+		}
+		
+		final Double rhsDouble = toDouble(rhs);
+		if ( rhsDouble == null ) {
+			return false;
+		}
+		
+		return compareNumbers(lhsDouble, rhsDouble);
+	}
+
+	private Double toDouble(final Object value) {
+		if ( value == null ) {
+			return null;
+		}
+		
+		if ( value instanceof Double ) {
+			return (Double) value;
+		}
+		if ( value instanceof Number ) {
+			return ((Number) value).doubleValue();
+		}
+		
+		if ( value instanceof String ) {
+			if ( NUMERIC_PATTERN.matcher((String) value).matches() ) {
+				return Double.parseDouble((String) value);
+			}
+		}
+		
+		return null;
+	}
+	
+	protected abstract boolean compareNumbers(final Double lhs, final Double rhs);
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/EqualsEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/EqualsEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/EqualsEvaluator.java
new file mode 100644
index 0000000..7ee2f87
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/EqualsEvaluator.java
@@ -0,0 +1,32 @@
+/*
+ * 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.hl7.query.evaluator.comparison;
+
+import org.apache.nifi.hl7.query.evaluator.Evaluator;
+
+public class EqualsEvaluator extends AbstractComparisonEvaluator {
+
+	public EqualsEvaluator(final Evaluator<?> lhs, final Evaluator<?> rhs) {
+		super(lhs, rhs);
+	}
+	
+	@Override
+	protected boolean compare(final Object lhs, final Object rhs) {
+		return lhs != null && rhs != null && ((lhs == rhs) || (lhs.equals(rhs)) || lhs.toString().equals(rhs.toString()));
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/GreaterThanEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/GreaterThanEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/GreaterThanEvaluator.java
new file mode 100644
index 0000000..bf8596e
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/GreaterThanEvaluator.java
@@ -0,0 +1,34 @@
+/*
+ * 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.hl7.query.evaluator.comparison;
+
+import org.apache.nifi.hl7.query.evaluator.Evaluator;
+
+
+public class GreaterThanEvaluator extends AbstractNumericComparison {
+
+	public GreaterThanEvaluator(final Evaluator<?> lhs, final Evaluator<?> rhs) {
+		super(lhs, rhs);
+	}
+
+	@Override
+	protected boolean compareNumbers(final Double lhs, final Double rhs) {
+		return lhs > rhs;
+	}
+
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/GreaterThanOrEqualEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/GreaterThanOrEqualEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/GreaterThanOrEqualEvaluator.java
new file mode 100644
index 0000000..69115a3
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/GreaterThanOrEqualEvaluator.java
@@ -0,0 +1,34 @@
+/*
+ * 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.hl7.query.evaluator.comparison;
+
+import org.apache.nifi.hl7.query.evaluator.Evaluator;
+
+
+public class GreaterThanOrEqualEvaluator extends AbstractNumericComparison {
+
+	public GreaterThanOrEqualEvaluator(final Evaluator<?> lhs, final Evaluator<?> rhs) {
+		super(lhs, rhs);
+	}
+
+	@Override
+	protected boolean compareNumbers(final Double lhs, final Double rhs) {
+		return lhs >= rhs;
+	}
+
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/IsNullEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/IsNullEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/IsNullEvaluator.java
new file mode 100644
index 0000000..69d481e
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/IsNullEvaluator.java
@@ -0,0 +1,69 @@
+/*
+ * 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.hl7.query.evaluator.comparison;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.nifi.hl7.model.HL7Component;
+import org.apache.nifi.hl7.query.evaluator.BooleanEvaluator;
+import org.apache.nifi.hl7.query.evaluator.Evaluator;
+
+public class IsNullEvaluator extends BooleanEvaluator {
+	private final Evaluator<?> subjectEvaluator;
+	
+	public IsNullEvaluator(final Evaluator<?> subjectEvaluator) {
+		this.subjectEvaluator = subjectEvaluator;
+	}
+	
+	@Override
+	public Boolean evaluate(final Map<String, Object> objectMap) {
+		Object subjectValue = subjectEvaluator.evaluate(objectMap);
+		if ( subjectValue == null ) {
+			return true;
+		}
+		
+		return isNull(subjectValue);
+	}
+
+	private boolean isNull(Object subjectValue) {
+		if ( subjectValue == null ) {
+			return true;
+		}
+		
+		if ( subjectValue instanceof HL7Component ) {
+			subjectValue = ((HL7Component) subjectValue).getValue();
+		}
+		
+		if ( subjectValue instanceof Collection ) {
+			final Collection<?> collection = (Collection<?>) subjectValue;
+			if ( collection.isEmpty() ) {
+				return true;
+			}
+			
+			for ( final Object obj : collection ) {
+				if ( !isNull(obj) ) {
+					return false;
+				}
+			}
+			
+			return true;
+		}
+		
+		return subjectValue == null;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/LessThanEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/LessThanEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/LessThanEvaluator.java
new file mode 100644
index 0000000..891d5e4
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/LessThanEvaluator.java
@@ -0,0 +1,31 @@
+/*
+ * 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.hl7.query.evaluator.comparison;
+
+import org.apache.nifi.hl7.query.evaluator.Evaluator;
+
+public class LessThanEvaluator extends AbstractNumericComparison {
+	public LessThanEvaluator(final Evaluator<?> lhs, final Evaluator<?> rhs) {
+		super(lhs, rhs);
+	}
+
+	@Override
+	protected boolean compareNumbers(final Double lhs, final Double rhs) {
+		return lhs < rhs;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/45416dc6/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/LessThanOrEqualEvaluator.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/LessThanOrEqualEvaluator.java b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/LessThanOrEqualEvaluator.java
new file mode 100644
index 0000000..c6fb097
--- /dev/null
+++ b/nifi/nifi-commons/nifi-hl7-query-language/src/main/java/org/apache/nifi/hl7/query/evaluator/comparison/LessThanOrEqualEvaluator.java
@@ -0,0 +1,31 @@
+/*
+ * 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.hl7.query.evaluator.comparison;
+
+import org.apache.nifi.hl7.query.evaluator.Evaluator;
+
+public class LessThanOrEqualEvaluator extends AbstractNumericComparison {
+	public LessThanOrEqualEvaluator(final Evaluator<?> lhs, final Evaluator<?> rhs) {
+		super(lhs, rhs);
+	}
+
+	@Override
+	protected boolean compareNumbers(final Double lhs, final Double rhs) {
+		return lhs <= rhs;
+	}
+
+}


[47/62] [abbrv] incubator-nifi git commit: NIFI-488: Redirect nifi output streams and redirect bootstrap log messages to file

Posted by ma...@apache.org.
NIFI-488: Redirect nifi output streams and redirect bootstrap log messages to file


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/abd279c1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/abd279c1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/abd279c1

Branch: refs/heads/NIFI-25
Commit: abd279c1e01e3aea3332e6b378701554722317e6
Parents: 7819afb
Author: Mark Payne <ma...@hotmail.com>
Authored: Wed Apr 8 13:41:34 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Wed Apr 8 13:41:34 2015 -0400

----------------------------------------------------------------------
 .../java/org/apache/nifi/bootstrap/RunNiFi.java | 131 ++++++++++++++++---
 .../src/main/resources/conf/bootstrap.conf      |   5 +
 2 files changed, 119 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/abd279c1/nifi/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/RunNiFi.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/RunNiFi.java b/nifi/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/RunNiFi.java
index 28a9b71..d25df97 100644
--- a/nifi/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/RunNiFi.java
+++ b/nifi/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/RunNiFi.java
@@ -45,9 +45,10 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
-import java.util.logging.ConsoleHandler;
+import java.util.logging.FileHandler;
 import java.util.logging.Handler;
 import java.util.logging.Level;
+import java.util.logging.SimpleFormatter;
 
 
 /**
@@ -92,16 +93,82 @@ public class RunNiFi {
 
 	private final java.util.logging.Logger logger;
 	
-	public RunNiFi(final File bootstrapConfigFile, final boolean verbose) {
+	public RunNiFi(final File bootstrapConfigFile, final boolean verbose) throws IOException {
 		this.bootstrapConfigFile = bootstrapConfigFile;
 		logger = java.util.logging.Logger.getLogger("Bootstrap");
+		
+		final Properties bootstrapProps = new Properties();
+		try (final InputStream configIn = new FileInputStream(bootstrapConfigFile)) {
+			bootstrapProps.load(configIn);
+		}
+
+		String logFilename = bootstrapProps.getProperty("bootstrap.log.file");
+		if ( logFilename == null ) {
+			logFilename = "./logs/bootstrap.log";
+		}
+		
+		File logFile = new File(logFilename);
+		if ( !logFile.isAbsolute() ) {
+			final File workDir = getDefaultWorkingDirectory();
+			logFile = new File(workDir, logFilename);
+		}
+		
+		final File logFileDir = logFile.getParentFile();
+		final Handler fileHandler;
+		if ( logFileDir.exists() || logFileDir.mkdirs() ) {
+			final int maxSize = getIntProp(bootstrapProps, "bootstrap.log.max.bytes", 1024 * 1024 * 10);	// 10 MB
+			final int numFiles = getIntProp(bootstrapProps, "bootstrap.log.count", 10);
+			
+			fileHandler = new FileHandler(logFile.getAbsolutePath(), maxSize, numFiles, true);
+			fileHandler.setFormatter(new SimpleFormatter());
+			logger.addHandler(fileHandler);
+		} else {
+			fileHandler = null;
+			logger.severe("Could not create log file directory " + logFileDir + ". Will not log bootstrap info to file or redirect NiFi standard out to file");
+		}
+		
 		if ( verbose ) {
 		    logger.info("Enabling Verbose Output");
 		    
 		    logger.setLevel(Level.FINE);
-		    final Handler handler = new ConsoleHandler();
-		    handler.setLevel(Level.FINE);
-		    logger.addHandler(handler);
+		    
+		    for ( final Handler handler : logger.getHandlers() ) {
+		    	handler.setLevel(Level.FINE);
+		    }
+		}
+	}
+	
+	
+	private File getLogFile() throws IOException {
+		final Properties bootstrapProps = new Properties();
+		try (final InputStream configIn = new FileInputStream(bootstrapConfigFile)) {
+			bootstrapProps.load(configIn);
+		}
+
+		String logFilename = bootstrapProps.getProperty("bootstrap.log.file");
+		if ( logFilename == null ) {
+			logFilename = "./logs/bootstrap.log";
+		}
+		
+		File logFile = new File(logFilename);
+		if ( !logFile.isAbsolute() ) {
+			final File workDir = getDefaultWorkingDirectory();
+			logFile = new File(workDir, logFilename);
+		}
+
+		return logFile;
+	}
+	
+	private static int getIntProp(final Properties properties, final String name, final int defaultValue) {
+		String propVal = properties.getProperty(name);
+		if ( propVal == null || propVal.trim().isEmpty() ) {
+			return defaultValue;
+		}
+		
+		try {
+			return Integer.parseInt(propVal.trim());
+		} catch (final NumberFormatException nfe) {
+			throw new NumberFormatException("Expected bootstrap property '" + name + "' to be an integer but found value: " + propVal);
 		}
 	}
 	
@@ -581,6 +648,35 @@ public class RunNiFi {
 		}
 	}
 	
+	private void redirectOutput(final Process process) {
+		redirectStreamToLogs(process.getInputStream());
+		redirectStreamToLogs(process.getErrorStream());
+	}
+	
+	private void redirectStreamToLogs(final InputStream in) {
+		final Thread t = new Thread(new Runnable() {
+			@Override
+			public void run() {
+				try (final BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
+					String line;
+					while ((line = reader.readLine()) != null) {
+						logger.info(line);
+					}
+				} catch (IOException e) {
+					logger.warning("Failed to read output of NiFi console: " + e);
+				}
+			}
+		});
+		t.setDaemon(true);
+		t.start();
+	}
+	
+	private File getDefaultWorkingDirectory() {
+		final File bootstrapConfigAbsoluteFile = bootstrapConfigFile.getAbsoluteFile();
+		final File binDir = bootstrapConfigAbsoluteFile.getParentFile();
+		return binDir.getParentFile();
+	}
+	
 	@SuppressWarnings({ "rawtypes", "unchecked" })
 	public void start(final boolean monitor) throws IOException, InterruptedException {
 		final Integer port = getCurrentPort();
@@ -590,7 +686,6 @@ public class RunNiFi {
 		}
 		
 		final ProcessBuilder builder = new ProcessBuilder();
-
 		if ( !bootstrapConfigFile.exists() ) {
 			throw new FileNotFoundException(bootstrapConfigFile.getAbsolutePath());
 		}
@@ -604,18 +699,17 @@ public class RunNiFi {
 		props.putAll( (Map) properties );
 
 		final String specifiedWorkingDir = props.get("working.dir");
-		if ( specifiedWorkingDir != null ) {
-			builder.directory(new File(specifiedWorkingDir));
-		}
-
-		final File bootstrapConfigAbsoluteFile = bootstrapConfigFile.getAbsoluteFile();
-		final File binDir = bootstrapConfigAbsoluteFile.getParentFile();
-		final File workingDir = binDir.getParentFile();
-		
+		final File workingDir = getDefaultWorkingDirectory();
 		if ( specifiedWorkingDir == null ) {
 			builder.directory(workingDir);
+		} else {
+			builder.directory(new File(specifiedWorkingDir));
 		}
 		
+		final File logDir = getLogFile().getParentFile();
+		builder.redirectError(new File(logDir, "nifi.err"));
+		builder.redirectOutput(new File(logDir, "nifi.out"));
+		
 		final String libFilename = replaceNull(props.get("lib.dir"), "./lib").trim();
 		File libDir = getFile(libFilename, workingDir);
 		
@@ -738,14 +832,15 @@ public class RunNiFi {
 			try {
 				gracefulShutdownSeconds = Integer.parseInt(gracefulShutdown);
 			} catch (final NumberFormatException nfe) {
-				throw new NumberFormatException("The '" + GRACEFUL_SHUTDOWN_PROP + "' property in Bootstrap Config File " + bootstrapConfigAbsoluteFile.getAbsolutePath() + " has an invalid value. Must be a non-negative integer");
+				throw new NumberFormatException("The '" + GRACEFUL_SHUTDOWN_PROP + "' property in Bootstrap Config File " + bootstrapConfigFile.getAbsolutePath() + " has an invalid value. Must be a non-negative integer");
 			}
 			
 			if ( gracefulShutdownSeconds < 0 ) {
-				throw new NumberFormatException("The '" + GRACEFUL_SHUTDOWN_PROP + "' property in Bootstrap Config File " + bootstrapConfigAbsoluteFile.getAbsolutePath() + " has an invalid value. Must be a non-negative integer");
+				throw new NumberFormatException("The '" + GRACEFUL_SHUTDOWN_PROP + "' property in Bootstrap Config File " + bootstrapConfigFile.getAbsolutePath() + " has an invalid value. Must be a non-negative integer");
 			}
 			
 			Process process = builder.start();
+			redirectOutput(process);
 			Long pid = getPid(process);
 		    if ( pid != null ) {
                 nifiPid = pid;
@@ -776,7 +871,8 @@ public class RunNiFi {
 					if (autoRestartNiFi) {
 						logger.warning("Apache NiFi appears to have died. Restarting...");
 						process = builder.start();
-						
+						redirectOutput(process);
+
 						pid = getPid(process);
 						if ( pid != null ) {
 			                nifiPid = pid;
@@ -802,6 +898,7 @@ public class RunNiFi {
 			}
 		} else {
 			final Process process = builder.start();
+			redirectOutput(process);
 			final Long pid = getPid(process);
 			
 			if ( pid != null ) {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/abd279c1/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/bootstrap.conf
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/bootstrap.conf b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/bootstrap.conf
index c1536d8..e87bde2 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/bootstrap.conf
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/bootstrap.conf
@@ -21,6 +21,11 @@ java=java
 # Username to use when running NiFi. This value will be ignored on Windows.
 run.as=
 
+# Bootstrap logger info
+bootstrap.log.file=logs/nifi-bootstrap.log
+bootstrap.log.max.bytes=10485760
+bootstrap.log.count=10
+
 # Configure where NiFi's lib and conf directories live
 lib.dir=./lib
 conf.dir=./conf


[32/62] [abbrv] incubator-nifi git commit: NIFI-484 EvaluateJsonPath: always overwrites content

Posted by ma...@apache.org.
NIFI-484 EvaluateJsonPath: always overwrites content

Signed-off-by: Mark Payne <ma...@hotmail.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/4a168453
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/4a168453
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/4a168453

Branch: refs/heads/NIFI-25
Commit: 4a16845309fce1f2cdbad3b31926563dd27b2c3c
Parents: 2b21b46
Author: Jon Anderson <an...@gmail.com>
Authored: Sat Apr 4 21:18:47 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Sun Apr 5 14:22:46 2015 -0400

----------------------------------------------------------------------
 .../java/org/apache/nifi/processors/standard/EvaluateJsonPath.java  | 1 +
 1 file changed, 1 insertion(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/4a168453/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/EvaluateJsonPath.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/EvaluateJsonPath.java b/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/EvaluateJsonPath.java
index 2634da2..229b342 100644
--- a/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/EvaluateJsonPath.java
+++ b/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/EvaluateJsonPath.java
@@ -269,6 +269,7 @@ public class EvaluateJsonPath extends AbstractJsonPathProcessor {
             switch (destination) {
                 case DESTINATION_ATTRIBUTE:
                     jsonPathResults.put(jsonPathAttrKey, resultRepresentation);
+                    break;
                 case DESTINATION_CONTENT:
                     flowFile = processSession.write(flowFile, new OutputStreamCallback() {
                         @Override


[55/62] [abbrv] incubator-nifi git commit: NIFI-244: Initial import of GetTwitter processor

Posted by ma...@apache.org.
NIFI-244: Initial import of GetTwitter processor


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/e9cb3b30
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/e9cb3b30
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/e9cb3b30

Branch: refs/heads/NIFI-25
Commit: e9cb3b300c6b5e223744925cee1d0c59dd97d29a
Parents: 178c5cd
Author: Mark Payne <ma...@hotmail.com>
Authored: Thu Apr 9 17:56:52 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Thu Apr 9 17:56:52 2015 -0400

----------------------------------------------------------------------
 nifi/nifi-assembly/NOTICE                       |  49 +
 nifi/nifi-assembly/pom.xml                      | 925 ++++++++++---------
 nifi/nifi-commons/pom.xml                       |   1 +
 .../nifi-social-media-nar/pom.xml               |  36 +
 .../nifi-twitter-processors/.gitignore          |   1 +
 .../nifi-twitter-processors/pom.xml             |  60 ++
 .../nifi/processors/twitter/GetTwitter.java     | 360 ++++++++
 .../org.apache.nifi.processor.Processor         |  16 +
 .../nifi-social-media-bundle/pom.xml            |  33 +
 nifi/nifi-nar-bundles/pom.xml                   |   4 +
 nifi/pom.xml                                    |  24 +
 11 files changed, 1060 insertions(+), 449 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9cb3b30/nifi/nifi-assembly/NOTICE
----------------------------------------------------------------------
diff --git a/nifi/nifi-assembly/NOTICE b/nifi/nifi-assembly/NOTICE
index 8d7db8d..d95e2ff 100644
--- a/nifi/nifi-assembly/NOTICE
+++ b/nifi/nifi-assembly/NOTICE
@@ -501,6 +501,38 @@ The following binary components are provided under the Apache Software License v
       Apache License Version 2.0 http://www.apache.org/licenses/.
       (c) Daniel Lemire, http://lemire.me/en/
 
+  (ASLv2) Twitter4J
+    The following NOTICE information applies:
+      Copyright 2007 Yusuke Yamamoto
+      
+      Twitter4J includes software from JSON.org to parse JSON response from the Twitter API. You can see the license term at http://www.JSON.org/license.html
+  
+  (ASLv2) JOAuth
+    The following NOTICE information applies:
+      JOAuth
+      Copyright 2010-2013 Twitter, Inc
+
+      Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
+  
+  (ASLv2) Hosebird Client
+    The following NOTICE information applies:
+      Hosebird Client (hbc)
+      Copyright 2013 Twitter, Inc.
+
+      Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
+
+  (ASLv2) GeoIP2 Java API
+    The following NOTICE information applies:
+      GeoIP2 Java API
+      This software is Copyright (c) 2013 by MaxMind, Inc.
+      
+      This is free software, licensed under the Apache License, Version 2.0.
+      
+  (ASLv2) Google HTTP Client Library for Java
+    The following NOTICE information applies:
+      Google HTTP Client Library for Java
+      
+      This is free software, licensed under the Apache License, Version 2.0.
 
 ************************
 Common Development and Distribution License 1.1
@@ -541,6 +573,14 @@ The following binary components are provided under the Common Development and Di
     (CDDL 1.0) SR 250 Common Annotations For The JavaTM Platform (javax.annotation:jsr250-api:jar:1.0 - http://jcp.org/aboutJava/communityprocess/final/jsr250/index.html)
 
 ************************
+Creative Commons Attribution-ShareAlike 3.0
+************************
+
+The following binary components are provided under the Creative Commons Attribution-ShareAlike 3.0.  See project link for details.
+
+	(CCAS 3.0) MaxMind DB (https://github.com/maxmind/MaxMind-DB)
+
+************************
 Eclipse Public License 1.0
 ************************
 
@@ -560,6 +600,15 @@ The following binary components are provided under the Mozilla Public License v2
     (MPL 2.0) Saxon HE (net.sf.saxon:Saxon-HE:jar:9.6.0-4 - http://www.saxonica.com/)
 
 *****************
+Mozilla Public License v1.1
+*****************
+
+The following binary components are provided under the Mozilla Public License v1.1.  See project link for details.
+
+    (MPL 1.1) HAPI Base (ca.uhn.hapi:hapi-base:2.2 - http://http://hl7api.sourceforge.net/)
+    (MPL 1.1) HAPI Structures (ca.uhn.hapi:hapi-structures-v*:2.2 - http://http://hl7api.sourceforge.net/)
+
+*****************
 Public Domain
 *****************
 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9cb3b30/nifi/nifi-assembly/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-assembly/pom.xml b/nifi/nifi-assembly/pom.xml
index a26f214..13ffba8 100644
--- a/nifi/nifi-assembly/pom.xml
+++ b/nifi/nifi-assembly/pom.xml
@@ -1,457 +1,484 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!--
-  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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <parent>
-        <groupId>org.apache.nifi</groupId>
-        <artifactId>nifi</artifactId>
-        <version>0.1.0-incubating-SNAPSHOT</version>
-    </parent>
-    <artifactId>nifi-assembly</artifactId>
-    <packaging>pom</packaging>
-    <description>This is the assembly Apache NiFi (incubating)</description>
-    <build>
-        <plugins>
-            <plugin>
-                <artifactId>maven-assembly-plugin</artifactId>
-                <configuration>
-                    <finalName>nifi-${project.version}</finalName>
-                    <attach>false</attach>
-                </configuration>
-                <executions>
-                    <execution>
-                        <id>make shared resource</id>
-                        <goals>
-                            <goal>single</goal>
-                        </goals>
-                        <phase>package</phase>
-                        <configuration>
-                            <descriptors>
-                                <descriptor>src/main/assembly/dependencies.xml</descriptor>
-                            </descriptors>
-                            <tarLongFileMode>posix</tarLongFileMode>
-                        </configuration>
-                    </execution>
-                </executions>    
-            </plugin>
-        </plugins>
-    </build>
-    <dependencies>
-        <dependency>
-            <groupId>ch.qos.logback</groupId>
-            <artifactId>logback-classic</artifactId>
-            <scope>compile</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jcl-over-slf4j</artifactId>
-            <scope>compile</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>jul-to-slf4j</artifactId>
-            <scope>compile</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>log4j-over-slf4j</artifactId>
-            <scope>compile</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-            <scope>compile</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-runtime</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-bootstrap</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-resources</artifactId>
-            <classifier>resources</classifier>
-            <scope>runtime</scope>
-            <type>zip</type>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-docs</artifactId>
-            <classifier>resources</classifier>
-            <scope>runtime</scope>
-            <type>zip</type>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-framework-nar</artifactId>
-            <type>nar</type>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-provenance-repository-nar</artifactId>
-            <type>nar</type>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-standard-services-api-nar</artifactId>
-            <type>nar</type>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-ssl-context-service-nar</artifactId>
-            <type>nar</type>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-distributed-cache-services-nar</artifactId>
-            <type>nar</type>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-standard-nar</artifactId>
-            <type>nar</type>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-jetty-bundle</artifactId>
-            <type>nar</type>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-update-attribute-nar</artifactId>
-            <type>nar</type>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-hadoop-libraries-nar</artifactId>
-            <type>nar</type>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-hadoop-nar</artifactId>
-            <type>nar</type>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-kafka-nar</artifactId>
-            <type>nar</type>
-        </dependency>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.apache.nifi</groupId>
+		<artifactId>nifi</artifactId>
+		<version>0.1.0-incubating-SNAPSHOT</version>
+	</parent>
+	<artifactId>nifi-assembly</artifactId>
+	<packaging>pom</packaging>
+	<description>This is the assembly Apache NiFi (incubating)</description>
+	<build>
+		<plugins>
+			<plugin>
+				<artifactId>maven-assembly-plugin</artifactId>
+				<configuration>
+					<finalName>nifi-${project.version}</finalName>
+					<attach>false</attach>
+				</configuration>
+				<executions>
+					<execution>
+						<id>make shared resource</id>
+						<goals>
+							<goal>single</goal>
+						</goals>
+						<phase>package</phase>
+						<configuration>
+							<descriptors>
+								<descriptor>src/main/assembly/dependencies.xml</descriptor>
+							</descriptors>
+							<tarLongFileMode>posix</tarLongFileMode>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+	<dependencies>
 		<dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-http-context-map-nar</artifactId>
-            <type>nar</type>
-        </dependency>
+			<groupId>ch.qos.logback</groupId>
+			<artifactId>logback-classic</artifactId>
+			<scope>compile</scope>
+		</dependency>
 		<dependency>
-            <groupId>org.apache.nifi</groupId>
-            <artifactId>nifi-kite-nar</artifactId>
-            <type>nar</type>
-        </dependency>
-    </dependencies>
-    
-    <properties>        
-        <!--Wrapper Properties-->
-        <nifi.wrapper.jvm.heap.initial.mb>256</nifi.wrapper.jvm.heap.initial.mb>
-        <nifi.wrapper.jvm.heap.max.mb>512</nifi.wrapper.jvm.heap.max.mb>
-        <nifi.initial.permgen.size.mb>128</nifi.initial.permgen.size.mb>
-        <nifi.max.permgen.size.mb>128</nifi.max.permgen.size.mb>
-        <nifi.wrapper.logfile.maxsize>10m</nifi.wrapper.logfile.maxsize>
-        <nifi.wrapper.logfile.maxfiles>10</nifi.wrapper.logfile.maxfiles>
-        
-        <!-- nifi.properties: core properties -->
-        <nifi.version>${project.version}</nifi.version>
-        <nifi.flowcontroller.autoResumeState>true</nifi.flowcontroller.autoResumeState>
-        <nifi.flowcontroller.graceful.shutdown.period>10 sec</nifi.flowcontroller.graceful.shutdown.period>
-        <nifi.flowservice.writedelay.interval>500 ms</nifi.flowservice.writedelay.interval>
-        <nifi.administrative.yield.duration>30 sec</nifi.administrative.yield.duration>
-        <nifi.bored.yield.duration>10 millis</nifi.bored.yield.duration>
+			<groupId>org.slf4j</groupId>
+			<artifactId>jcl-over-slf4j</artifactId>
+			<scope>compile</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>jul-to-slf4j</artifactId>
+			<scope>compile</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>log4j-over-slf4j</artifactId>
+			<scope>compile</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+			<scope>compile</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-api</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-runtime</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-bootstrap</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-resources</artifactId>
+			<classifier>resources</classifier>
+			<scope>runtime</scope>
+			<type>zip</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-docs</artifactId>
+			<classifier>resources</classifier>
+			<scope>runtime</scope>
+			<type>zip</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-framework-nar</artifactId>
+			<type>nar</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-provenance-repository-nar</artifactId>
+			<type>nar</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-standard-services-api-nar</artifactId>
+			<type>nar</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-ssl-context-service-nar</artifactId>
+			<type>nar</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-distributed-cache-services-nar</artifactId>
+			<type>nar</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-standard-nar</artifactId>
+			<type>nar</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-jetty-bundle</artifactId>
+			<type>nar</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-update-attribute-nar</artifactId>
+			<type>nar</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-hadoop-libraries-nar</artifactId>
+			<type>nar</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-hadoop-nar</artifactId>
+			<type>nar</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-kafka-nar</artifactId>
+			<type>nar</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-http-context-map-nar</artifactId>
+			<type>nar</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-kite-nar</artifactId>
+			<type>nar</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-social-media-nar</artifactId>
+			<version>0.1.0-incubating-SNAPSHOT</version>
+			<type>nar</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-hl7-nar</artifactId>
+			<version>0.1.0-incubating-SNAPSHOT</version>
+			<type>nar</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-language-translation-nar</artifactId>
+			<version>0.1.0-incubating-SNAPSHOT</version>
+			<type>nar</type>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-geo-nar</artifactId>
+			<version>0.1.0-incubating-SNAPSHOT</version>
+			<type>nar</type>
+		</dependency>
+	</dependencies>
+
+	<properties>
+		<!--Wrapper Properties -->
+		<nifi.wrapper.jvm.heap.initial.mb>256</nifi.wrapper.jvm.heap.initial.mb>
+		<nifi.wrapper.jvm.heap.max.mb>512</nifi.wrapper.jvm.heap.max.mb>
+		<nifi.initial.permgen.size.mb>128</nifi.initial.permgen.size.mb>
+		<nifi.max.permgen.size.mb>128</nifi.max.permgen.size.mb>
+		<nifi.wrapper.logfile.maxsize>10m</nifi.wrapper.logfile.maxsize>
+		<nifi.wrapper.logfile.maxfiles>10</nifi.wrapper.logfile.maxfiles>
+
+		<!-- nifi.properties: core properties -->
+		<nifi.version>${project.version}</nifi.version>
+		<nifi.flowcontroller.autoResumeState>true</nifi.flowcontroller.autoResumeState>
+		<nifi.flowcontroller.graceful.shutdown.period>10 sec</nifi.flowcontroller.graceful.shutdown.period>
+		<nifi.flowservice.writedelay.interval>500 ms</nifi.flowservice.writedelay.interval>
+		<nifi.administrative.yield.duration>30 sec</nifi.administrative.yield.duration>
+		<nifi.bored.yield.duration>10 millis</nifi.bored.yield.duration>
+
+		<nifi.flow.configuration.file>./conf/flow.xml.gz</nifi.flow.configuration.file>
+		<nifi.flow.configuration.archive.dir>./conf/archive/</nifi.flow.configuration.archive.dir>
+		<nifi.authority.provider.configuration.file>./conf/authority-providers.xml</nifi.authority.provider.configuration.file>
+		<nifi.templates.directory>./conf/templates</nifi.templates.directory>
+		<nifi.database.directory>./database_repository</nifi.database.directory>
+
+		<nifi.flowfile.repository.implementation>org.apache.nifi.controller.repository.WriteAheadFlowFileRepository</nifi.flowfile.repository.implementation>
+		<nifi.flowfile.repository.directory>./flowfile_repository</nifi.flowfile.repository.directory>
+		<nifi.flowfile.repository.partitions>256</nifi.flowfile.repository.partitions>
+		<nifi.flowfile.repository.checkpoint.interval>2 mins</nifi.flowfile.repository.checkpoint.interval>
+		<nifi.flowfile.repository.always.sync>false</nifi.flowfile.repository.always.sync>
+		<nifi.swap.manager.implementation>org.apache.nifi.controller.FileSystemSwapManager</nifi.swap.manager.implementation>
+		<nifi.queue.swap.threshold>20000</nifi.queue.swap.threshold>
+		<nifi.swap.in.period>5 sec</nifi.swap.in.period>
+		<nifi.swap.in.threads>1</nifi.swap.in.threads>
+		<nifi.swap.out.period>5 sec</nifi.swap.out.period>
+		<nifi.swap.out.threads>4</nifi.swap.out.threads>
+
+		<nifi.content.repository.implementation>org.apache.nifi.controller.repository.FileSystemRepository</nifi.content.repository.implementation>
+		<nifi.content.claim.max.appendable.size>10 MB</nifi.content.claim.max.appendable.size>
+		<nifi.content.claim.max.flow.files>100</nifi.content.claim.max.flow.files>
+		<nifi.content.repository.directory.default>./content_repository</nifi.content.repository.directory.default>
+		<nifi.content.repository.archive.max.retention.period />
+		<nifi.content.repository.archive.max.usage.percentage />
+		<nifi.content.repository.archive.enabled>false</nifi.content.repository.archive.enabled>
+		<nifi.content.repository.always.sync>false</nifi.content.repository.always.sync>
+		<nifi.content.viewer.url />
+
+		<nifi.restore.directory />
+		<nifi.ui.banner.text />
+		<nifi.ui.autorefresh.interval>30 sec</nifi.ui.autorefresh.interval>
+		<nifi.nar.library.directory>./lib</nifi.nar.library.directory>
+		<nifi.nar.working.directory>./work/nar/</nifi.nar.working.directory>
+		<nifi.documentation.working.directory>./work/docs/components</nifi.documentation.working.directory>
+
+		<nifi.sensitive.props.algorithm>PBEWITHMD5AND256BITAES-CBC-OPENSSL</nifi.sensitive.props.algorithm>
+		<nifi.sensitive.props.provider>BC</nifi.sensitive.props.provider>
+		<nifi.h2.url.append>;LOCK_TIMEOUT=25000;WRITE_DELAY=0;AUTO_SERVER=FALSE</nifi.h2.url.append>
+
+		<nifi.remote.input.socket.port>9990</nifi.remote.input.socket.port>
+
+		<!-- persistent provenance repository properties -->
+		<nifi.provenance.repository.implementation>org.apache.nifi.provenance.PersistentProvenanceRepository</nifi.provenance.repository.implementation>
+		<nifi.provenance.repository.directory.default>./provenance_repository</nifi.provenance.repository.directory.default>
+		<nifi.provenance.repository.max.storage.time>24 hours</nifi.provenance.repository.max.storage.time>
+		<nifi.provenance.repository.max.storage.size>1 GB</nifi.provenance.repository.max.storage.size>
+		<nifi.provenance.repository.rollover.time>5 mins</nifi.provenance.repository.rollover.time>
+		<nifi.provenance.repository.rollover.size>100 MB</nifi.provenance.repository.rollover.size>
+		<nifi.provenance.repository.query.threads>2</nifi.provenance.repository.query.threads>
+		<nifi.provenance.repository.compress.on.rollover>true</nifi.provenance.repository.compress.on.rollover>
+		<nifi.provenance.repository.indexed.fields>EventType, FlowFileUUID,
+			Filename, ProcessorID</nifi.provenance.repository.indexed.fields>
+		<nifi.provenance.repository.indexed.attributes />
+		<nifi.provenance.repository.index.shard.size>500 MB</nifi.provenance.repository.index.shard.size>
+		<nifi.provenance.repository.always.sync>false</nifi.provenance.repository.always.sync>
+		<nifi.provenance.repository.journal.count>16</nifi.provenance.repository.journal.count>
+
+		<!-- volatile provenance repository properties -->
+		<nifi.provenance.repository.buffer.size>100000</nifi.provenance.repository.buffer.size>
+
+		<!-- Component status repository properties -->
+		<nifi.components.status.repository.implementation>org.apache.nifi.controller.status.history.VolatileComponentStatusRepository</nifi.components.status.repository.implementation>
+		<nifi.components.status.repository.buffer.size>288</nifi.components.status.repository.buffer.size>
+		<nifi.components.status.snapshot.frequency>5 mins</nifi.components.status.snapshot.frequency>
+
+		<!-- nifi.properties: web properties -->
+		<nifi.web.war.directory>./lib</nifi.web.war.directory>
+		<nifi.web.http.host />
+		<nifi.web.http.port>8080</nifi.web.http.port>
+		<nifi.web.https.host />
+		<nifi.web.https.port />
+		<nifi.jetty.work.dir>./work/jetty</nifi.jetty.work.dir>
+		<nifi.web.jetty.threads>200</nifi.web.jetty.threads>
 
-        <nifi.flow.configuration.file>./conf/flow.xml.gz</nifi.flow.configuration.file>
-        <nifi.flow.configuration.archive.dir>./conf/archive/</nifi.flow.configuration.archive.dir>
-        <nifi.authority.provider.configuration.file>./conf/authority-providers.xml</nifi.authority.provider.configuration.file>
-        <nifi.templates.directory>./conf/templates</nifi.templates.directory>
-        <nifi.database.directory>./database_repository</nifi.database.directory>
+		<!-- nifi.properties: security properties -->
+		<nifi.security.keystore />
+		<nifi.security.keystoreType />
+		<nifi.security.keystorePasswd />
+		<nifi.security.keyPasswd />
+		<nifi.security.truststore />
+		<nifi.security.truststoreType />
+		<nifi.security.truststorePasswd />
+		<nifi.security.needClientAuth />
+		<nifi.security.authorizedUsers.file>./conf/authorized-users.xml</nifi.security.authorizedUsers.file>
+		<nifi.security.user.credential.cache.duration>24 hours</nifi.security.user.credential.cache.duration>
+		<nifi.security.user.authority.provider>file-provider</nifi.security.user.authority.provider>
+		<nifi.security.x509.principal.extractor />
+		<nifi.security.support.new.account.requests />
+		<nifi.security.ocsp.responder.url />
+		<nifi.security.ocsp.responder.certificate />
 
-        <nifi.flowfile.repository.implementation>org.apache.nifi.controller.repository.WriteAheadFlowFileRepository</nifi.flowfile.repository.implementation>
-        <nifi.flowfile.repository.directory>./flowfile_repository</nifi.flowfile.repository.directory>
-        <nifi.flowfile.repository.partitions>256</nifi.flowfile.repository.partitions>
-        <nifi.flowfile.repository.checkpoint.interval>2 mins</nifi.flowfile.repository.checkpoint.interval>
-        <nifi.flowfile.repository.always.sync>false</nifi.flowfile.repository.always.sync>
-        <nifi.swap.manager.implementation>org.apache.nifi.controller.FileSystemSwapManager</nifi.swap.manager.implementation>
-        <nifi.queue.swap.threshold>20000</nifi.queue.swap.threshold>
-        <nifi.swap.in.period>5 sec</nifi.swap.in.period>
-        <nifi.swap.in.threads>1</nifi.swap.in.threads>
-        <nifi.swap.out.period>5 sec</nifi.swap.out.period>
-        <nifi.swap.out.threads>4</nifi.swap.out.threads>
-		
-        <nifi.content.repository.implementation>org.apache.nifi.controller.repository.FileSystemRepository</nifi.content.repository.implementation>
-        <nifi.content.claim.max.appendable.size>10 MB</nifi.content.claim.max.appendable.size>
-        <nifi.content.claim.max.flow.files>100</nifi.content.claim.max.flow.files>
-        <nifi.content.repository.directory.default>./content_repository</nifi.content.repository.directory.default>
-        <nifi.content.repository.archive.max.retention.period />
-        <nifi.content.repository.archive.max.usage.percentage />
-        <nifi.content.repository.archive.enabled>false</nifi.content.repository.archive.enabled>
-        <nifi.content.repository.always.sync>false</nifi.content.repository.always.sync>
-        <nifi.content.viewer.url />
-        
-        <nifi.restore.directory />
-        <nifi.ui.banner.text />
-        <nifi.ui.autorefresh.interval>30 sec</nifi.ui.autorefresh.interval>
-        <nifi.nar.library.directory>./lib</nifi.nar.library.directory>
-        <nifi.nar.working.directory>./work/nar/</nifi.nar.working.directory>
-        <nifi.documentation.working.directory>./work/docs/components</nifi.documentation.working.directory>
-        
-        <nifi.sensitive.props.algorithm>PBEWITHMD5AND256BITAES-CBC-OPENSSL</nifi.sensitive.props.algorithm>
-        <nifi.sensitive.props.provider>BC</nifi.sensitive.props.provider>
-        <nifi.h2.url.append>;LOCK_TIMEOUT=25000;WRITE_DELAY=0;AUTO_SERVER=FALSE</nifi.h2.url.append>
+		<!-- nifi.properties: cluster common properties (cluster manager and nodes 
+			must have same values) -->
+		<nifi.cluster.protocol.heartbeat.interval>5 sec</nifi.cluster.protocol.heartbeat.interval>
+		<nifi.cluster.protocol.is.secure>false</nifi.cluster.protocol.is.secure>
+		<nifi.cluster.protocol.socket.timeout>30 sec</nifi.cluster.protocol.socket.timeout>
+		<nifi.cluster.protocol.connection.handshake.timeout>45 sec</nifi.cluster.protocol.connection.handshake.timeout>
+		<nifi.cluster.protocol.use.multicast>false</nifi.cluster.protocol.use.multicast>
+		<nifi.cluster.protocol.multicast.address />
+		<nifi.cluster.protocol.multicast.port />
+		<nifi.cluster.protocol.multicast.service.broadcast.delay>500 ms</nifi.cluster.protocol.multicast.service.broadcast.delay>
+		<nifi.cluster.protocol.multicast.service.locator.attempts>3</nifi.cluster.protocol.multicast.service.locator.attempts>
+		<nifi.cluster.protocol.multicast.service.locator.attempts.delay>1 sec</nifi.cluster.protocol.multicast.service.locator.attempts.delay>
 
-        <nifi.remote.input.socket.port>9990</nifi.remote.input.socket.port>
-        
-        <!-- persistent provenance repository properties -->
-        <nifi.provenance.repository.implementation>org.apache.nifi.provenance.PersistentProvenanceRepository</nifi.provenance.repository.implementation>
-        <nifi.provenance.repository.directory.default>./provenance_repository</nifi.provenance.repository.directory.default>
-        <nifi.provenance.repository.max.storage.time>24 hours</nifi.provenance.repository.max.storage.time>
-        <nifi.provenance.repository.max.storage.size>1 GB</nifi.provenance.repository.max.storage.size>
-        <nifi.provenance.repository.rollover.time>5 mins</nifi.provenance.repository.rollover.time>
-        <nifi.provenance.repository.rollover.size>100 MB</nifi.provenance.repository.rollover.size>
-        <nifi.provenance.repository.query.threads>2</nifi.provenance.repository.query.threads>
-        <nifi.provenance.repository.compress.on.rollover>true</nifi.provenance.repository.compress.on.rollover>
-        <nifi.provenance.repository.indexed.fields>EventType, FlowFileUUID, Filename, ProcessorID</nifi.provenance.repository.indexed.fields>
-        <nifi.provenance.repository.indexed.attributes />
-        <nifi.provenance.repository.index.shard.size>500 MB</nifi.provenance.repository.index.shard.size>
-        <nifi.provenance.repository.always.sync>false</nifi.provenance.repository.always.sync>
-        <nifi.provenance.repository.journal.count>16</nifi.provenance.repository.journal.count>
-        
-        <!-- volatile provenance repository properties -->
-        <nifi.provenance.repository.buffer.size>100000</nifi.provenance.repository.buffer.size>
-        
-        <!-- Component status repository properties -->
-        <nifi.components.status.repository.implementation>org.apache.nifi.controller.status.history.VolatileComponentStatusRepository</nifi.components.status.repository.implementation>
-        <nifi.components.status.repository.buffer.size>288</nifi.components.status.repository.buffer.size>
-        <nifi.components.status.snapshot.frequency>5 mins</nifi.components.status.snapshot.frequency>
-        
-        <!-- nifi.properties: web properties -->
-        <nifi.web.war.directory>./lib</nifi.web.war.directory>
-        <nifi.web.http.host />
-        <nifi.web.http.port>8080</nifi.web.http.port>
-        <nifi.web.https.host />
-        <nifi.web.https.port />
-        <nifi.jetty.work.dir>./work/jetty</nifi.jetty.work.dir>
-        <nifi.web.jetty.threads>200</nifi.web.jetty.threads>
-        
-        <!-- nifi.properties: security properties -->
-        <nifi.security.keystore />
-        <nifi.security.keystoreType />
-        <nifi.security.keystorePasswd />
-        <nifi.security.keyPasswd />
-        <nifi.security.truststore />
-        <nifi.security.truststoreType />
-        <nifi.security.truststorePasswd />
-        <nifi.security.needClientAuth />
-        <nifi.security.authorizedUsers.file>./conf/authorized-users.xml</nifi.security.authorizedUsers.file>
-        <nifi.security.user.credential.cache.duration>24 hours</nifi.security.user.credential.cache.duration>
-        <nifi.security.user.authority.provider>file-provider</nifi.security.user.authority.provider>
-        <nifi.security.x509.principal.extractor />
-        <nifi.security.support.new.account.requests />
-        <nifi.security.ocsp.responder.url />
-        <nifi.security.ocsp.responder.certificate />
-        
-        <!-- nifi.properties: cluster common properties (cluster manager and nodes must have same values) -->
-        <nifi.cluster.protocol.heartbeat.interval>5 sec</nifi.cluster.protocol.heartbeat.interval>
-        <nifi.cluster.protocol.is.secure>false</nifi.cluster.protocol.is.secure>
-        <nifi.cluster.protocol.socket.timeout>30 sec</nifi.cluster.protocol.socket.timeout>
-        <nifi.cluster.protocol.connection.handshake.timeout>45 sec</nifi.cluster.protocol.connection.handshake.timeout> 
-        <nifi.cluster.protocol.use.multicast>false</nifi.cluster.protocol.use.multicast>
-        <nifi.cluster.protocol.multicast.address />
-        <nifi.cluster.protocol.multicast.port />
-        <nifi.cluster.protocol.multicast.service.broadcast.delay>500 ms</nifi.cluster.protocol.multicast.service.broadcast.delay>
-        <nifi.cluster.protocol.multicast.service.locator.attempts>3</nifi.cluster.protocol.multicast.service.locator.attempts>
-        <nifi.cluster.protocol.multicast.service.locator.attempts.delay>1 sec</nifi.cluster.protocol.multicast.service.locator.attempts.delay>
+		<!-- nifi.properties: cluster node properties (only configure for cluster 
+			nodes) -->
+		<nifi.cluster.is.node>false</nifi.cluster.is.node>
+		<nifi.cluster.node.address />
+		<nifi.cluster.node.protocol.port />
+		<nifi.cluster.node.protocol.threads>2</nifi.cluster.node.protocol.threads>
+		<nifi.cluster.node.unicast.manager.address />
+		<nifi.cluster.node.unicast.manager.protocol.port />
 
-        <!-- nifi.properties: cluster node properties (only configure for cluster nodes) -->
-        <nifi.cluster.is.node>false</nifi.cluster.is.node>
-        <nifi.cluster.node.address />
-        <nifi.cluster.node.protocol.port />
-        <nifi.cluster.node.protocol.threads>2</nifi.cluster.node.protocol.threads>
-        <nifi.cluster.node.unicast.manager.address />
-        <nifi.cluster.node.unicast.manager.protocol.port />
-        
-        <!-- nifi.properties: cluster manager properties (only configure for cluster manager) -->
-        <nifi.cluster.is.manager>false</nifi.cluster.is.manager>
-        <nifi.cluster.manager.address />
-        <nifi.cluster.manager.protocol.port />
-        <nifi.cluster.manager.node.firewall.file />
-        <nifi.cluster.manager.node.event.history.size>10</nifi.cluster.manager.node.event.history.size>
-        <nifi.cluster.manager.node.api.connection.timeout>30 sec</nifi.cluster.manager.node.api.connection.timeout>
-        <nifi.cluster.manager.node.api.read.timeout>30 sec</nifi.cluster.manager.node.api.read.timeout>
-        <nifi.cluster.manager.node.api.request.threads>10</nifi.cluster.manager.node.api.request.threads>
-        <nifi.cluster.manager.flow.retrieval.delay>5 sec</nifi.cluster.manager.flow.retrieval.delay>
-        <nifi.cluster.manager.protocol.threads>10</nifi.cluster.manager.protocol.threads>
-        <nifi.cluster.manager.safemode.duration>0 sec</nifi.cluster.manager.safemode.duration>
-    </properties>
-    <profiles>
-        <profile>
-            <id>rpm</id>
-            <activation>
-                <activeByDefault>false</activeByDefault>
-            </activation>
-            <build>
-                <plugins>
-                    <plugin>
-                        <artifactId>maven-dependency-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <id>unpack-shared-resources</id>
-                                <goals>
-                                    <goal>unpack-dependencies</goal>
-                                </goals>
-                                <phase>generate-resources</phase>
-                                <configuration>
-                                    <outputDirectory>${project.build.directory}/generated-resources</outputDirectory>
-                                    <includeArtifactIds>nifi-resources</includeArtifactIds>
-                                    <includeGroupIds>org.apache.nifi</includeGroupIds>
-                                    <excludeTransitive>false</excludeTransitive>
-                                </configuration>
-                            </execution>
-                            <execution>
-                                <id>unpack-docs</id>
-                                <goals>
-                                    <goal>unpack-dependencies</goal>
-                                </goals>
-                                <phase>generate-resources</phase>
-                                <configuration>
-                                    <outputDirectory>${project.build.directory}/generated-docs</outputDirectory>
-                                    <includeArtifactIds>nifi-docs</includeArtifactIds>
-                                    <includeGroupIds>org.apache.nifi</includeGroupIds>
-                                    <excludeTransitive>false</excludeTransitive>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>                    
-                    <plugin>
-                        <groupId>org.codehaus.mojo</groupId>
-                        <artifactId>rpm-maven-plugin</artifactId>
-                        <configuration>
-                            <summary>Apache NiFi (incubating)</summary>
-                            <description>Apache Nifi (incubating) is dataflow system based on the Flow-Based Programming concepts.</description>
-                            <license>Apache License, Version 2.0 and others (see included LICENSE file)</license>
-                            <url>http://nifi.incubator.apache.org</url>
-                            <group>Utilities</group>
-                            <prefix>/opt/nifi</prefix>
-                            <defineStatements>
-                                <defineStatement>_use_internal_dependency_generator 0</defineStatement>
-                            </defineStatements>
-                            <defaultDirmode>750</defaultDirmode>
-                            <defaultFilemode>640</defaultFilemode>
-                            <defaultUsername>root</defaultUsername>
-                            <defaultGroupname>root</defaultGroupname>
-                        </configuration>
-                        <executions>
-                            <execution>
-                                <id>build-bin-rpm</id>
-                                <goals>
-                                    <goal>attached-rpm</goal>
-                                </goals>
-                                <configuration>
-                                    <classifier>bin</classifier>
-                                    <provides>
-                                        <provide>nifi</provide>
-                                    </provides>
-                                    <mappings>
-                                        <mapping>
-                                            <directory>/opt/nifi/nifi-${project.version}</directory>
-                                        </mapping>
-                                        <mapping>
-                                            <directory>/opt/nifi/nifi-${project.version}</directory>
-                                            <sources>
-                                                <source>
-                                                    <location>./LICENSE</location>
-                                                </source>
-                                                <source>
-                                                    <location>./NOTICE</location>
-                                                </source>
-                                                <source>
-                                                    <location>../DISCLAIMER</location>
-                                                </source>
-                                                <source>
-                                                    <location>./README.md</location>
-                                                    <destination>README</destination>
-                                                </source>
-                                            </sources>
-                                        </mapping>
-                                        <mapping>
-                                            <directory>/opt/nifi/nifi-${project.version}/bin</directory>
-                                            <filemode>750</filemode>
-                                            <sources>
-                                                <source>
-                                                    <location>${project.build.directory}/generated-resources/bin/nifi.sh</location>
-                                                    <destination>nifi.sh</destination>
-                                                    <filter>true</filter>
-                                                </source>
-                                            </sources>
-                                        </mapping>
-                                        <mapping>
-                                            <directory>/opt/nifi/nifi-${project.version}/conf</directory>
-                                            <configuration>true</configuration>
-                                            <sources>
-                                                <source>
-                                                    <location>${project.build.directory}/generated-resources/conf</location>
-                                                    <filter>true</filter>
-                                                </source>
-                                            </sources>
-                                        </mapping>
-                                        <mapping>
-                                            <directory>/opt/nifi/nifi-${project.version}/lib</directory>
-                                            <dependency>
-                                                <excludes>
-                                                    <exclude>org.apache.nifi:nifi-bootstrap</exclude>
-                                                    <exclude>org.apache.nifi:nifi-resources</exclude>
-                                                    <exclude>org.apache.nifi:nifi-docs</exclude>
-                                                </excludes>
-                                            </dependency>
-                                        </mapping>
-                                        <mapping>
-                                            <directory>/opt/nifi/nifi-${project.version}/lib/bootstrap</directory>
-                                            <dependency>
-                                                <includes>
-                                                    <include>org.apache.nifi:nifi-bootstrap</include>
-                                                </includes>
-                                            </dependency>
-                                        </mapping>
-                                        <mapping>
-                                            <directory>/opt/nifi/nifi-${project.version}/docs</directory>
-                                            <sources>
-                                                <source>
-                                                    <location>${project.build.directory}/generated-docs</location>
-                                                </source>
-                                            </sources>
-                                        </mapping>
-                                    </mappings>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-    </profiles>
+		<!-- nifi.properties: cluster manager properties (only configure for cluster 
+			manager) -->
+		<nifi.cluster.is.manager>false</nifi.cluster.is.manager>
+		<nifi.cluster.manager.address />
+		<nifi.cluster.manager.protocol.port />
+		<nifi.cluster.manager.node.firewall.file />
+		<nifi.cluster.manager.node.event.history.size>10</nifi.cluster.manager.node.event.history.size>
+		<nifi.cluster.manager.node.api.connection.timeout>30 sec</nifi.cluster.manager.node.api.connection.timeout>
+		<nifi.cluster.manager.node.api.read.timeout>30 sec</nifi.cluster.manager.node.api.read.timeout>
+		<nifi.cluster.manager.node.api.request.threads>10</nifi.cluster.manager.node.api.request.threads>
+		<nifi.cluster.manager.flow.retrieval.delay>5 sec</nifi.cluster.manager.flow.retrieval.delay>
+		<nifi.cluster.manager.protocol.threads>10</nifi.cluster.manager.protocol.threads>
+		<nifi.cluster.manager.safemode.duration>0 sec</nifi.cluster.manager.safemode.duration>
+	</properties>
+	<profiles>
+		<profile>
+			<id>rpm</id>
+			<activation>
+				<activeByDefault>false</activeByDefault>
+			</activation>
+			<build>
+				<plugins>
+					<plugin>
+						<artifactId>maven-dependency-plugin</artifactId>
+						<executions>
+							<execution>
+								<id>unpack-shared-resources</id>
+								<goals>
+									<goal>unpack-dependencies</goal>
+								</goals>
+								<phase>generate-resources</phase>
+								<configuration>
+									<outputDirectory>${project.build.directory}/generated-resources</outputDirectory>
+									<includeArtifactIds>nifi-resources</includeArtifactIds>
+									<includeGroupIds>org.apache.nifi</includeGroupIds>
+									<excludeTransitive>false</excludeTransitive>
+								</configuration>
+							</execution>
+							<execution>
+								<id>unpack-docs</id>
+								<goals>
+									<goal>unpack-dependencies</goal>
+								</goals>
+								<phase>generate-resources</phase>
+								<configuration>
+									<outputDirectory>${project.build.directory}/generated-docs</outputDirectory>
+									<includeArtifactIds>nifi-docs</includeArtifactIds>
+									<includeGroupIds>org.apache.nifi</includeGroupIds>
+									<excludeTransitive>false</excludeTransitive>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+					<plugin>
+						<groupId>org.codehaus.mojo</groupId>
+						<artifactId>rpm-maven-plugin</artifactId>
+						<configuration>
+							<summary>Apache NiFi (incubating)</summary>
+							<description>Apache Nifi (incubating) is dataflow system based on
+								the Flow-Based Programming concepts.</description>
+							<license>Apache License, Version 2.0 and others (see included
+								LICENSE file)</license>
+							<url>http://nifi.incubator.apache.org</url>
+							<group>Utilities</group>
+							<prefix>/opt/nifi</prefix>
+							<defineStatements>
+								<defineStatement>_use_internal_dependency_generator 0</defineStatement>
+							</defineStatements>
+							<defaultDirmode>750</defaultDirmode>
+							<defaultFilemode>640</defaultFilemode>
+							<defaultUsername>root</defaultUsername>
+							<defaultGroupname>root</defaultGroupname>
+						</configuration>
+						<executions>
+							<execution>
+								<id>build-bin-rpm</id>
+								<goals>
+									<goal>attached-rpm</goal>
+								</goals>
+								<configuration>
+									<classifier>bin</classifier>
+									<provides>
+										<provide>nifi</provide>
+									</provides>
+									<mappings>
+										<mapping>
+											<directory>/opt/nifi/nifi-${project.version}</directory>
+										</mapping>
+										<mapping>
+											<directory>/opt/nifi/nifi-${project.version}</directory>
+											<sources>
+												<source>
+													<location>./LICENSE</location>
+												</source>
+												<source>
+													<location>./NOTICE</location>
+												</source>
+												<source>
+													<location>../DISCLAIMER</location>
+												</source>
+												<source>
+													<location>./README.md</location>
+													<destination>README</destination>
+												</source>
+											</sources>
+										</mapping>
+										<mapping>
+											<directory>/opt/nifi/nifi-${project.version}/bin</directory>
+											<filemode>750</filemode>
+											<sources>
+												<source>
+													<location>${project.build.directory}/generated-resources/bin/nifi.sh</location>
+													<destination>nifi.sh</destination>
+													<filter>true</filter>
+												</source>
+											</sources>
+										</mapping>
+										<mapping>
+											<directory>/opt/nifi/nifi-${project.version}/conf</directory>
+											<configuration>true</configuration>
+											<sources>
+												<source>
+													<location>${project.build.directory}/generated-resources/conf</location>
+													<filter>true</filter>
+												</source>
+											</sources>
+										</mapping>
+										<mapping>
+											<directory>/opt/nifi/nifi-${project.version}/lib</directory>
+											<dependency>
+												<excludes>
+													<exclude>org.apache.nifi:nifi-bootstrap</exclude>
+													<exclude>org.apache.nifi:nifi-resources</exclude>
+													<exclude>org.apache.nifi:nifi-docs</exclude>
+												</excludes>
+											</dependency>
+										</mapping>
+										<mapping>
+											<directory>/opt/nifi/nifi-${project.version}/lib/bootstrap</directory>
+											<dependency>
+												<includes>
+													<include>org.apache.nifi:nifi-bootstrap</include>
+												</includes>
+											</dependency>
+										</mapping>
+										<mapping>
+											<directory>/opt/nifi/nifi-${project.version}/docs</directory>
+											<sources>
+												<source>
+													<location>${project.build.directory}/generated-docs</location>
+												</source>
+											</sources>
+										</mapping>
+									</mappings>
+								</configuration>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+	</profiles>
 </project>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9cb3b30/nifi/nifi-commons/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/pom.xml b/nifi/nifi-commons/pom.xml
index 5358054..1d2ce46 100644
--- a/nifi/nifi-commons/pom.xml
+++ b/nifi/nifi-commons/pom.xml
@@ -36,5 +36,6 @@
         <module>nifi-processor-utilities</module>
         <module>nifi-write-ahead-log</module>
 		<module>nifi-site-to-site-client</module>
+		<module>nifi-hl7-query-language</module>
     </modules>
 </project>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9cb3b30/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-social-media-nar/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-social-media-nar/pom.xml b/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-social-media-nar/pom.xml
new file mode 100644
index 0000000..6da74dd
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-social-media-nar/pom.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-social-media-bundle</artifactId>
+        <version>0.1.0-incubating-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>nifi-social-media-nar</artifactId>
+    <packaging>nar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-twitter-processors</artifactId>
+            <version>0.1.0-incubating-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9cb3b30/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/.gitignore
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/.gitignore b/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/.gitignore
@@ -0,0 +1 @@
+/target/

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9cb3b30/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/pom.xml b/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/pom.xml
new file mode 100644
index 0000000..45af0ce
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/pom.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-social-media-bundle</artifactId>
+        <version>0.1.0-incubating-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>nifi-twitter-processors</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-processor-utils</artifactId>
+        </dependency>
+	
+		<dependency>
+			<groupId>com.twitter</groupId>
+			<artifactId>hbc-twitter4j</artifactId>
+			<version>2.2.0</version>
+		</dependency>
+        
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-mock</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9cb3b30/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/src/main/java/org/apache/nifi/processors/twitter/GetTwitter.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/src/main/java/org/apache/nifi/processors/twitter/GetTwitter.java b/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/src/main/java/org/apache/nifi/processors/twitter/GetTwitter.java
new file mode 100644
index 0000000..45b1ae1
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/src/main/java/org/apache/nifi/processors/twitter/GetTwitter.java
@@ -0,0 +1,360 @@
+/*
+ * 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.processors.twitter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.regex.Pattern;
+
+import org.apache.nifi.annotation.behavior.SupportsBatching;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.annotation.lifecycle.OnStopped;
+import org.apache.nifi.components.AllowableValue;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.ValidationContext;
+import org.apache.nifi.components.ValidationResult;
+import org.apache.nifi.components.Validator;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.flowfile.attributes.CoreAttributes;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.apache.nifi.processor.util.StandardValidators;
+
+import com.twitter.hbc.ClientBuilder;
+import com.twitter.hbc.core.Client;
+import com.twitter.hbc.core.Constants;
+import com.twitter.hbc.core.endpoint.StatusesFilterEndpoint;
+import com.twitter.hbc.core.endpoint.StatusesFirehoseEndpoint;
+import com.twitter.hbc.core.endpoint.StatusesSampleEndpoint;
+import com.twitter.hbc.core.endpoint.StreamingEndpoint;
+import com.twitter.hbc.core.event.Event;
+import com.twitter.hbc.core.processor.StringDelimitedProcessor;
+import com.twitter.hbc.httpclient.auth.Authentication;
+import com.twitter.hbc.httpclient.auth.OAuth1;
+
+@SupportsBatching
+@Tags({"twitter", "tweets", "social media", "status", "json"})
+@CapabilityDescription("Pulls status changes from Twitter's streaming API")
+@WritesAttribute(attribute="mime.type", description="Sets mime type to application/json")
+public class GetTwitter extends AbstractProcessor {
+
+	static final AllowableValue ENDPOINT_SAMPLE = new AllowableValue("Sample Endpoint", "Sample Endpoint", "The endpoint that provides public data, aka a 'garden hose'");
+	static final AllowableValue ENDPOINT_FIREHOSE = new AllowableValue("Firehose Endpoint", "Firehose Endpoint", "The endpoint that provides access to all tweets");
+	static final AllowableValue ENDPOINT_FILTER = new AllowableValue("Filter Endpoint", "Filter Endpoint", "Endpoint that allows the stream to be filtered by specific terms or User IDs");
+	
+	public static final PropertyDescriptor ENDPOINT = new PropertyDescriptor.Builder()
+		.name("Twitter Endpoint")
+		.description("Specifies which endpoint data should be pulled from")
+		.required(true)
+		.allowableValues(ENDPOINT_SAMPLE, ENDPOINT_FIREHOSE, ENDPOINT_FILTER)
+		.defaultValue(ENDPOINT_SAMPLE.getValue())
+		.build();
+    public static final PropertyDescriptor CONSUMER_KEY = new PropertyDescriptor.Builder()
+    	.name("Consumer Key")
+    	.description("The Consumer Key provided by Twitter")
+    	.required(true)
+    	.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+    	.build();
+    public static final PropertyDescriptor CONSUMER_SECRET = new PropertyDescriptor.Builder()
+		.name("Consumer Secret")
+		.description("The Consumer Secret provided by Twitter")
+		.required(true)
+		.sensitive(true)
+		.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+		.build();
+    public static final PropertyDescriptor ACCESS_TOKEN = new PropertyDescriptor.Builder()
+		.name("Access Token")
+		.description("The Acces Token provided by Twitter")
+		.required(true)
+		.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+		.build();
+    public static final PropertyDescriptor ACCESS_TOKEN_SECRET = new PropertyDescriptor.Builder()
+		.name("Access Token Secret")
+		.description("The Access Token Secret provided by Twitter")
+		.required(true)
+		.sensitive(true)
+		.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+		.build();
+    public static final PropertyDescriptor LANGUAGES = new PropertyDescriptor.Builder()
+    	.name("Languages")
+    	.description("A comma-separated list of languages for which tweets should be fetched")
+    	.required(false)
+    	.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+    	.build();
+    public static final PropertyDescriptor FOLLOWING = new PropertyDescriptor.Builder()
+		.name("IDs to Follow")
+		.description("A comma-separated list of Twitter User ID's to follow. Ignored unless Endpoint is set to 'Filter Endpoint'.")
+		.required(false)
+		.addValidator(new FollowingValidator())
+		.build();
+    public static final PropertyDescriptor TERMS = new PropertyDescriptor.Builder()
+		.name("Terms to Filter On")
+		.description("A comma-separated list of terms to filter on. Ignored unless Endpoint is set to 'Filter Endpoint'. The filter works such that if any term matches, the status update will be retrieved; multiple terms separated by a space function as an 'AND'. I.e., 'it was, hello' will retrieve status updates that have either 'hello' or both 'it' AND 'was'")
+		.required(false)
+		.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+		.build();
+    
+    
+    public static final Relationship REL_SUCCESS = new Relationship.Builder()
+        .name("success")
+        .description("All status updates will be routed to this relationship")
+        .build();
+
+    private List<PropertyDescriptor> descriptors;
+    private Set<Relationship> relationships;
+
+    private final BlockingQueue<Event> eventQueue = new LinkedBlockingQueue<Event>(1000);
+    
+    private volatile Client client;
+    private volatile BlockingQueue<String> messageQueue;
+
+    @Override
+    protected void init(final ProcessorInitializationContext context) {
+        final List<PropertyDescriptor> descriptors = new ArrayList<PropertyDescriptor>();
+        descriptors.add(ENDPOINT);
+        descriptors.add(CONSUMER_KEY);
+        descriptors.add(CONSUMER_SECRET);
+        descriptors.add(ACCESS_TOKEN);
+        descriptors.add(ACCESS_TOKEN_SECRET);
+        descriptors.add(LANGUAGES);
+        descriptors.add(TERMS);
+        descriptors.add(FOLLOWING);
+        this.descriptors = Collections.unmodifiableList(descriptors);
+
+        final Set<Relationship> relationships = new HashSet<Relationship>();
+        relationships.add(REL_SUCCESS);
+        this.relationships = Collections.unmodifiableSet(relationships);
+    }
+
+    @Override
+    public Set<Relationship> getRelationships() {
+        return this.relationships;
+    }
+
+    @Override
+    public final List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        return descriptors;
+    }
+    
+    @Override
+    protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(final String propertyDescriptorName) {
+    	return new PropertyDescriptor.Builder()
+    		.name(propertyDescriptorName)
+    		.description("Adds a query parameter with name '" + propertyDescriptorName + "' to the Twitter query")
+    		.required(false)
+    		.dynamic(true)
+    		.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+    		.build();
+    }
+    
+    @Override
+    protected Collection<ValidationResult> customValidate(final ValidationContext validationContext) {
+    	final List<ValidationResult> results = new ArrayList<>();
+    	final String endpointName = validationContext.getProperty(ENDPOINT).getValue();
+    	
+    	if ( ENDPOINT_FILTER.getValue().equals(endpointName) ) {
+    		if ( !validationContext.getProperty(TERMS).isSet() && !validationContext.getProperty(FOLLOWING).isSet() ) {
+    			results.add(new ValidationResult.Builder().input("").subject(FOLLOWING.getName()).valid(false).explanation("When using the 'Filter Endpoint', at least one of '" + TERMS.getName() + "' or '" + FOLLOWING.getName() + "' must be set").build());
+    		}
+    	}
+    	
+    	return results;
+    }
+    
+    @Override
+    public void onPropertyModified(final PropertyDescriptor descriptor, final String oldValue, final String newValue) {
+    	// if any property is modified, the results are no longer valid. Destroy all messages in teh queue.
+    	messageQueue.clear();
+    }
+
+    @OnScheduled
+    public void onScheduled(final ProcessContext context) throws MalformedURLException {
+    	messageQueue = new LinkedBlockingQueue<>(100000);
+    	
+    	final String endpointName = context.getProperty(ENDPOINT).getValue();
+    	final Authentication oauth = new OAuth1(context.getProperty(CONSUMER_KEY).getValue(), 
+    			context.getProperty(CONSUMER_SECRET).getValue(), 
+    			context.getProperty(ACCESS_TOKEN).getValue(),
+    			context.getProperty(ACCESS_TOKEN_SECRET).getValue());
+
+    	final ClientBuilder clientBuilder = new ClientBuilder();
+    	clientBuilder.name("GetTwitter[id=" + getIdentifier() + "]")
+    		.authentication(oauth)
+    		.eventMessageQueue(eventQueue)
+    		.processor(new StringDelimitedProcessor(messageQueue));
+
+    	final String languageString = context.getProperty(LANGUAGES).getValue();
+    	final List<String> languages;
+    	if ( languageString == null ) {
+    		languages = null;
+    	} else {
+    		languages = new ArrayList<>();
+	    	for ( final String language : context.getProperty(LANGUAGES).getValue().split(",") ) {
+	    		languages.add(language.trim());
+	    	}
+    	}
+    	
+    	final String host;
+    	final StreamingEndpoint streamingEndpoint;
+    	if ( ENDPOINT_SAMPLE.getValue().equals(endpointName) ) {
+    		host = Constants.STREAM_HOST;
+    		final StatusesSampleEndpoint sse = new StatusesSampleEndpoint();
+    		streamingEndpoint = sse;
+    		if ( languages != null ) {
+    			sse.languages(languages);
+    		}
+    	} else if ( ENDPOINT_FIREHOSE.getValue().equals(endpointName) ) {
+    		host = Constants.STREAM_HOST;
+        	final StatusesFirehoseEndpoint firehoseEndpoint = new StatusesFirehoseEndpoint();
+        	streamingEndpoint = firehoseEndpoint;
+        	if ( languages != null ) {
+        		firehoseEndpoint.languages(languages);
+        	}
+    	} else if ( ENDPOINT_FILTER.getValue().equals(endpointName) ) {
+    		host = Constants.STREAM_HOST;
+    		final StatusesFilterEndpoint filterEndpoint = new StatusesFilterEndpoint();
+    		
+    		final String followingString = context.getProperty(FOLLOWING).getValue();
+    		final List<Long> followingIds;
+    		if ( followingString == null ) {
+    			followingIds = Collections.emptyList();
+    		} else {
+    			followingIds = new ArrayList<>();
+    			
+    			for ( final String split : followingString.split(",") ) {
+    				final Long id = Long.parseLong(split.trim());
+    				followingIds.add(id);
+    			}
+    		}
+    		
+    		final String termString = context.getProperty(TERMS).getValue();
+    		final List<String> terms;
+    		if ( termString == null ) {
+    			terms = Collections.emptyList();
+    		} else {
+    			terms = new ArrayList<>();
+    			for ( final String split : termString.split(",") ) {
+    				terms.add(split.trim());
+    			}
+    		}
+    		
+    		if ( !terms.isEmpty() ) {
+    			filterEndpoint.trackTerms(terms);
+    		}
+    		
+    		if ( !followingIds.isEmpty() ) {
+    			filterEndpoint.followings(followingIds);
+    		}
+    		
+    		if ( languages != null ) {
+    			filterEndpoint.languages(languages);
+    		}
+    		streamingEndpoint = filterEndpoint;
+    	} else {
+    		throw new AssertionError("Endpoint was invalid value: " + endpointName);
+    	}
+
+    	clientBuilder.hosts(host).endpoint(streamingEndpoint);
+    	client = clientBuilder.build();
+    	client.connect();
+    }
+
+    @OnStopped
+    public void shutdownClient() {
+    	if ( client != null ) {
+    		client.stop();
+    	}
+    }
+    
+    @Override
+    public void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException {
+    	final Event event = eventQueue.poll();
+    	if ( event != null ) {
+    		switch (event.getEventType()) {
+	    		case STOPPED_BY_ERROR:
+    				getLogger().error("Received error {}: {} due to {}. Will not attempt to reconnect", new Object[] {event.getEventType(), event.getMessage(), event.getUnderlyingException()});
+    				break;
+	    		case CONNECTION_ERROR:
+    			case HTTP_ERROR:
+    				getLogger().error("Received error {}: {}. Will attempt to reconnect", new Object[] {event.getEventType(), event.getMessage()});
+    				client.reconnect();
+    				break;
+    			default:
+    				break;
+    		}
+    	}
+    	
+    	final String tweet = messageQueue.poll();
+    	if ( tweet == null ) {
+    		context.yield();
+    		return;
+    	}
+    	
+    	FlowFile flowFile = session.create();
+    	flowFile = session.write(flowFile, new OutputStreamCallback() {
+			@Override
+			public void process(final OutputStream out) throws IOException {
+				out.write(tweet.getBytes(StandardCharsets.UTF_8));
+			}
+    	});
+    	
+    	final Map<String, String> attributes = new HashMap<>();
+    	attributes.put(CoreAttributes.MIME_TYPE.key(), "application/json");
+    	attributes.put(CoreAttributes.FILENAME.key(), flowFile.getAttribute(CoreAttributes.FILENAME.key()) + ".json");
+    	flowFile = session.putAllAttributes(flowFile, attributes);
+    	
+    	session.transfer(flowFile, REL_SUCCESS);
+    	session.getProvenanceReporter().receive(flowFile, Constants.STREAM_HOST + client.getEndpoint().getURI().toString());
+    }
+
+    private static class FollowingValidator implements Validator {
+    	private static final Pattern NUMBER_PATTERN = Pattern.compile("\\d+");
+    	
+		@Override
+		public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
+			final String[] splits = input.split(",");
+			for ( final String split : splits ) {
+				if ( !NUMBER_PATTERN.matcher(split.trim()).matches() ) {
+					return new ValidationResult.Builder().input(input).subject(subject).valid(false).explanation("Must be comma-separted list of User ID's").build();
+				}
+			}
+			
+			return new ValidationResult.Builder().subject(subject).input(input).valid(true).build();
+		}
+    	
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9cb3b30/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor b/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
new file mode 100644
index 0000000..9504a11
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
@@ -0,0 +1,16 @@
+# 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.
+
+org.apache.nifi.processors.twitter.GetTwitter
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9cb3b30/nifi/nifi-nar-bundles/nifi-social-media-bundle/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-social-media-bundle/pom.xml b/nifi/nifi-nar-bundles/nifi-social-media-bundle/pom.xml
new file mode 100644
index 0000000..5aadbce
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-social-media-bundle/pom.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-nar-bundles</artifactId>
+        <version>0.1.0-incubating-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>nifi-social-media-bundle</artifactId>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>nifi-twitter-processors</module>
+        <module>nifi-social-media-nar</module>
+    </modules>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9cb3b30/nifi/nifi-nar-bundles/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/pom.xml b/nifi/nifi-nar-bundles/pom.xml
index e7c122d..50a9407 100644
--- a/nifi/nifi-nar-bundles/pom.xml
+++ b/nifi/nifi-nar-bundles/pom.xml
@@ -35,6 +35,10 @@
         <module>nifi-update-attribute-bundle</module>
         <module>nifi-kafka-bundle</module>
 		<module>nifi-kite-bundle</module>
+		<module>nifi-social-media-bundle</module>
+		<module>nifi-geo-bundle</module>
+		<module>nifi-hl7-bundle</module>
+		<module>nifi-language-translation-bundle</module>
     </modules>
     <dependencyManagement>
         <dependencies>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9cb3b30/nifi/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/pom.xml b/nifi/pom.xml
index 2e2346a..9b8bfb4 100644
--- a/nifi/pom.xml
+++ b/nifi/pom.xml
@@ -800,6 +800,30 @@
             </dependency>
             <dependency>
                 <groupId>org.apache.nifi</groupId>
+                <artifactId>nifi-social-media-nar</artifactId>
+                <version>0.1.0-incubating-SNAPSHOT</version>
+                <type>nar</type>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.nifi</groupId>
+                <artifactId>nifi-hl7-nar</artifactId>
+                <version>0.1.0-incubating-SNAPSHOT</version>
+                <type>nar</type>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.nifi</groupId>
+                <artifactId>nifi-language-translation-nar</artifactId>
+                <version>0.1.0-incubating-SNAPSHOT</version>
+                <type>nar</type>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.nifi</groupId>
+                <artifactId>nifi-geo-nar</artifactId>
+                <version>0.1.0-incubating-SNAPSHOT</version>
+                <type>nar</type>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.nifi</groupId>
                 <artifactId>nifi-properties</artifactId>
                 <version>0.1.0-incubating-SNAPSHOT</version>
             </dependency>


[43/62] [abbrv] incubator-nifi git commit: Merge branch 'develop' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into develop

Posted by ma...@apache.org.
Merge branch 'develop' of https://git-wip-us.apache.org/repos/asf/incubator-nifi into develop


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/e456ea37
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/e456ea37
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/e456ea37

Branch: refs/heads/NIFI-25
Commit: e456ea37f4232e9072631af986f54336904318bd
Parents: d769b50 39735c3
Author: Matt Gilman <ma...@gmail.com>
Authored: Wed Apr 8 11:28:44 2015 -0400
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Apr 8 11:28:44 2015 -0400

----------------------------------------------------------------------
 .../org/apache/nifi/remote/client/socket/SocketClient.java   | 8 +++++++-
 .../nifi/controller/scheduling/StandardProcessScheduler.java | 4 ++--
 .../java/org/apache/nifi/processor/SimpleProcessLogger.java  | 2 +-
 3 files changed, 10 insertions(+), 4 deletions(-)
----------------------------------------------------------------------



[18/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceReference.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceReference.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceReference.java
index a1c4984..c470b99 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceReference.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceReference.java
@@ -58,23 +58,28 @@ public class StandardControllerServiceReference implements ControllerServiceRefe
     }
 
     @Override
-    public Set<ConfiguredComponent> getRunningReferences() {
-        final Set<ConfiguredComponent> runningReferences = new HashSet<>();
+    public Set<ConfiguredComponent> getActiveReferences() {
+        final Set<ConfiguredComponent> activeReferences = new HashSet<>();
         final Set<ControllerServiceNode> serviceNodes = new HashSet<>();
 
         for (final ConfiguredComponent component : components) {
             if (component instanceof ControllerServiceNode) {
                 serviceNodes.add((ControllerServiceNode) component);
+                
+                final ControllerServiceState state = ((ControllerServiceNode) component).getState();
+                if ( state != ControllerServiceState.DISABLED ) {
+                    activeReferences.add(component);
+                }
             } else if (isRunning(component)) {
-                runningReferences.add(component);
+                activeReferences.add(component);
             }
         }
 
-        runningReferences.addAll(getRunningIndirectReferences(serviceNodes));
-        return runningReferences;
+        activeReferences.addAll(getActiveIndirectReferences(serviceNodes));
+        return activeReferences;
     }
 
-    private Set<ConfiguredComponent> getRunningIndirectReferences(final Set<ControllerServiceNode> referencingServices) {
+    private Set<ConfiguredComponent> getActiveIndirectReferences(final Set<ControllerServiceNode> referencingServices) {
         if (referencingServices.isEmpty()) {
             return Collections.emptySet();
         }
@@ -92,7 +97,7 @@ public class StandardControllerServiceReference implements ControllerServiceRefe
                 }
             }
 
-            references.addAll(getRunningIndirectReferences(serviceNodes));
+            references.addAll(getActiveIndirectReferences(serviceNodes));
         }
 
         return references;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/tasks/ReportingTaskWrapper.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/tasks/ReportingTaskWrapper.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/tasks/ReportingTaskWrapper.java
index 9b70581..0c472c8 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/tasks/ReportingTaskWrapper.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/tasks/ReportingTaskWrapper.java
@@ -19,15 +19,13 @@ package org.apache.nifi.controller.tasks;
 import org.apache.nifi.annotation.lifecycle.OnStopped;
 import org.apache.nifi.controller.ReportingTaskNode;
 import org.apache.nifi.controller.scheduling.ScheduleState;
+import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.nar.NarCloseable;
+import org.apache.nifi.processor.SimpleProcessLogger;
 import org.apache.nifi.util.ReflectionUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class ReportingTaskWrapper implements Runnable {
 
-    private static final Logger logger = LoggerFactory.getLogger(ReportingTaskWrapper.class);
-
     private final ReportingTaskNode taskNode;
     private final ScheduleState scheduleState;
 
@@ -43,20 +41,23 @@ public class ReportingTaskWrapper implements Runnable {
         try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
             taskNode.getReportingTask().onTrigger(taskNode.getReportingContext());
         } catch (final Throwable t) {
-            logger.error("Error running task {} due to {}", taskNode.getReportingTask(), t.toString());
-            if (logger.isDebugEnabled()) {
-                logger.error("", t);
+            final ComponentLog componentLog = new SimpleProcessLogger(taskNode.getIdentifier(), taskNode.getReportingTask());
+            componentLog.error("Error running task {} due to {}", new Object[] {taskNode.getReportingTask(), t.toString()});
+            if (componentLog.isDebugEnabled()) {
+                componentLog.error("", t);
             }
         } finally {
-            // if the processor is no longer scheduled to run and this is the last thread,
-            // invoke the OnStopped methods
-            if (!scheduleState.isScheduled() && scheduleState.getActiveThreadCount() == 1 && scheduleState.mustCallOnStoppedMethods()) {
-                try (final NarCloseable x = NarCloseable.withNarLoader()) {
-                    ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnStopped.class, org.apache.nifi.processor.annotation.OnStopped.class, taskNode.getReportingTask(), taskNode.getReportingContext());
+            try {
+                // if the reporting task is no longer scheduled to run and this is the last thread,
+                // invoke the OnStopped methods
+                if (!scheduleState.isScheduled() && scheduleState.getActiveThreadCount() == 1 && scheduleState.mustCallOnStoppedMethods()) {
+                    try (final NarCloseable x = NarCloseable.withNarLoader()) {
+                        ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnStopped.class, org.apache.nifi.processor.annotation.OnStopped.class, taskNode.getReportingTask(), taskNode.getConfigurationContext());
+                    }
                 }
+            } finally {
+                scheduleState.decrementActiveThreadCount();
             }
-
-            scheduleState.decrementActiveThreadCount();
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java
index 8575569..9a8ad28 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java
@@ -41,12 +41,14 @@ import javax.xml.validation.SchemaFactory;
 
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.controller.FlowController;
+import org.apache.nifi.controller.FlowFromDOMFactory;
 import org.apache.nifi.controller.Template;
 import org.apache.nifi.controller.exception.ProcessorInstantiationException;
 import org.apache.nifi.encrypt.StringEncryptor;
 import org.apache.nifi.processor.Processor;
 import org.apache.nifi.util.DomUtils;
 import org.apache.nifi.web.api.dto.ConnectionDTO;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
 import org.apache.nifi.web.api.dto.FlowSnippetDTO;
 import org.apache.nifi.web.api.dto.FunnelDTO;
 import org.apache.nifi.web.api.dto.LabelDTO;
@@ -58,6 +60,7 @@ import org.apache.nifi.web.api.dto.ProcessorDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupContentsDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupPortDTO;
+import org.apache.nifi.web.api.dto.ReportingTaskDTO;
 import org.apache.nifi.web.api.dto.TemplateDTO;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
@@ -250,6 +253,22 @@ public final class FingerprintFactory {
         // root group
         final Element rootGroupElem = (Element) DomUtils.getChildNodesByTagName(flowControllerElem, "rootGroup").item(0);
         addProcessGroupFingerprint(builder, rootGroupElem, controller);
+        
+        final Element controllerServicesElem = DomUtils.getChild(flowControllerElem, "controllerServices");
+        if ( controllerServicesElem != null ) {
+        	for ( final Element serviceElem : DomUtils.getChildElementsByTagName(controllerServicesElem, "controllerService") ) {
+        		addControllerServiceFingerprint(builder, serviceElem);
+        	}
+        }
+        
+        final Element reportingTasksElem = DomUtils.getChild(flowControllerElem, "reportingTasks");
+        if ( reportingTasksElem != null ) {
+        	for ( final Element taskElem : DomUtils.getChildElementsByTagName(reportingTasksElem, "reportingTask") ) {
+        		addReportingTaskFingerprint(builder, taskElem);
+        	}
+        }
+        
+        
         return builder;
     }
 
@@ -832,6 +851,66 @@ public final class FingerprintFactory {
         builder.append(funnel.getId());
         return builder;
     }
+    
+    private void addControllerServiceFingerprint(final StringBuilder builder, final Element controllerServiceElem) {
+    	final ControllerServiceDTO dto = FlowFromDOMFactory.getControllerService(controllerServiceElem, encryptor);
+    	addControllerServiceFingerprint(builder, dto);
+    }
+    
+    private void addControllerServiceFingerprint(final StringBuilder builder, final ControllerServiceDTO dto) {
+    	builder.append(dto.getId());
+    	builder.append(dto.getType());
+    	builder.append(dto.getName());
+    	builder.append(dto.getComments());
+    	builder.append(dto.getAnnotationData());
+    	
+    	final Map<String, String> properties = dto.getProperties();
+    	if (properties == null) {
+            builder.append("NO_PROPERTIES");
+        } else {
+            final SortedMap<String, String> sortedProps = new TreeMap<>(properties);
+            for (final Map.Entry<String, String> entry : sortedProps.entrySet()) {
+                final String propName = entry.getKey();
+                final String propValue = entry.getValue();
+                if (propValue == null) {
+                    continue;
+                }
+
+                builder.append(propName).append("=").append(propValue);
+            }
+        }
+    }
+    
+    private void addReportingTaskFingerprint(final StringBuilder builder, final Element element) {
+    	final ReportingTaskDTO dto = FlowFromDOMFactory.getReportingTask(element, encryptor);
+    	addReportingTaskFingerprint(builder, dto);
+    }
+    
+    private void addReportingTaskFingerprint(final StringBuilder builder, final ReportingTaskDTO dto) {
+    	builder.append(dto.getId());
+    	builder.append(dto.getType());
+    	builder.append(dto.getName());
+    	builder.append(dto.getComments());
+    	builder.append(dto.getSchedulingPeriod());
+    	builder.append(dto.getSchedulingStrategy());
+    	builder.append(dto.getAnnotationData());
+    	
+    	final Map<String, String> properties = dto.getProperties();
+    	if (properties == null) {
+            builder.append("NO_PROPERTIES");
+        } else {
+            final SortedMap<String, String> sortedProps = new TreeMap<>(properties);
+            for (final Map.Entry<String, String> entry : sortedProps.entrySet()) {
+                final String propName = entry.getKey();
+                final String propValue = entry.getValue();
+                if (propValue == null) {
+                    continue;
+                }
+
+                builder.append(propName).append("=").append(propValue);
+            }
+        }
+    }
 
     private Comparator<Element> getIdsComparator() {
         return new Comparator<Element>() {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
index 3cd5853..216d015 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
@@ -30,8 +30,13 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
 import org.apache.nifi.annotation.lifecycle.OnRemoved;
 import org.apache.nifi.annotation.lifecycle.OnShutdown;
+import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.connectable.Connectable;
 import org.apache.nifi.connectable.ConnectableType;
 import org.apache.nifi.connectable.Connection;
@@ -45,7 +50,9 @@ import org.apache.nifi.controller.ScheduledState;
 import org.apache.nifi.controller.Snippet;
 import org.apache.nifi.controller.exception.ProcessorLifeCycleException;
 import org.apache.nifi.controller.label.Label;
+import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.controller.service.ControllerServiceProvider;
+import org.apache.nifi.encrypt.StringEncryptor;
 import org.apache.nifi.logging.LogRepositoryFactory;
 import org.apache.nifi.nar.NarCloseable;
 import org.apache.nifi.processor.StandardProcessContext;
@@ -53,11 +60,6 @@ import org.apache.nifi.remote.RemoteGroupPort;
 import org.apache.nifi.remote.RootGroupPort;
 import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.util.ReflectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.builder.HashCodeBuilder;
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
-import org.apache.nifi.encrypt.StringEncryptor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -329,7 +331,8 @@ public final class StandardProcessGroup implements ProcessGroup {
     private void shutdown(final ProcessGroup procGroup) {
         for (final ProcessorNode node : procGroup.getProcessors()) {
             try (final NarCloseable x = NarCloseable.withNarLoader()) {
-                ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnShutdown.class, org.apache.nifi.processor.annotation.OnShutdown.class, node.getProcessor());
+                final StandardProcessContext processContext = new StandardProcessContext(node, controllerServiceProvider, encryptor);
+                ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnShutdown.class, org.apache.nifi.processor.annotation.OnShutdown.class, node.getProcessor(), processContext);
             }
         }
 
@@ -674,6 +677,19 @@ public final class StandardProcessGroup implements ProcessGroup {
                 throw new ProcessorLifeCycleException("Failed to invoke 'OnRemoved' methods of " + processor, e);
             }
 
+            for ( final Map.Entry<PropertyDescriptor, String> entry : processor.getProperties().entrySet() ) {
+                final PropertyDescriptor descriptor = entry.getKey();
+                if (descriptor.getControllerServiceDefinition() != null ) {
+                    final String value = entry.getValue() == null ? descriptor.getDefaultValue() : entry.getValue();
+                    if ( value != null ) {
+                        final ControllerServiceNode serviceNode = controllerServiceProvider.getControllerServiceNode(value);
+                        if ( serviceNode != null ) {
+                            serviceNode.removeReference(processor);
+                        }
+                    }
+                }
+            }
+            
             processors.remove(id);
             LogRepositoryFactory.getRepository(processor.getIdentifier()).removeAllObservers();
 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/FlowConfigurationDAO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/FlowConfigurationDAO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/FlowConfigurationDAO.java
index 4f3afaf..8957314 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/FlowConfigurationDAO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/FlowConfigurationDAO.java
@@ -19,15 +19,12 @@ package org.apache.nifi.persistence;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.List;
 
 import org.apache.nifi.cluster.protocol.DataFlow;
 import org.apache.nifi.controller.FlowController;
 import org.apache.nifi.controller.FlowSerializationException;
 import org.apache.nifi.controller.FlowSynchronizationException;
-import org.apache.nifi.controller.ReportingTaskNode;
 import org.apache.nifi.controller.UninheritableFlowException;
-import org.apache.nifi.controller.service.ControllerServiceNode;
 
 /**
  * Interface to define service methods for FlowController configuration.
@@ -110,26 +107,4 @@ public interface FlowConfigurationDAO {
      */
     void save(FlowController flow, boolean archive) throws IOException;
 
-    /**
-     * Instantiates and schedules all controller tasks from the file used in the
-     * constructor
-     *
-     * @param controller
-     * @return 
-     * @throws java.io.IOException
-     * @returns all of the ReportingTasks that were instantiated & scheduled
-     */
-    List<ReportingTaskNode> loadReportingTasks(FlowController controller) throws IOException;
-
-    /**
-     * Instantiates all controller services from the file used in the
-     * constructor
-     *
-     * @param controller
-     * @return 
-     * @throws java.io.IOException
-     * @returns all of the ReportingTasks that were instantiated & scheduled
-     */
-    List<ControllerServiceNode> loadControllerServices(FlowController controller) throws IOException;
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/StandardXMLFlowConfigurationDAO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/StandardXMLFlowConfigurationDAO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/StandardXMLFlowConfigurationDAO.java
index c11aa72..b93ae8a 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/StandardXMLFlowConfigurationDAO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/persistence/StandardXMLFlowConfigurationDAO.java
@@ -21,72 +21,36 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.net.URL;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardOpenOption;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
 import java.util.zip.GZIPInputStream;
 import java.util.zip.GZIPOutputStream;
 
-import javax.xml.XMLConstants;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.validation.Schema;
-import javax.xml.validation.SchemaFactory;
-import javax.xml.validation.Validator;
-
 import org.apache.nifi.cluster.protocol.DataFlow;
-import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.controller.FlowController;
 import org.apache.nifi.controller.FlowSerializationException;
 import org.apache.nifi.controller.FlowSynchronizationException;
 import org.apache.nifi.controller.FlowSynchronizer;
-import org.apache.nifi.controller.ReportingTaskNode;
 import org.apache.nifi.controller.StandardFlowSerializer;
 import org.apache.nifi.controller.StandardFlowSynchronizer;
 import org.apache.nifi.controller.UninheritableFlowException;
-import org.apache.nifi.controller.reporting.ReportingTaskInstantiationException;
-import org.apache.nifi.controller.reporting.StandardReportingInitializationContext;
-import org.apache.nifi.controller.service.ControllerServiceLoader;
-import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.encrypt.StringEncryptor;
-import org.apache.nifi.util.file.FileUtils;
-import org.apache.nifi.nar.NarCloseable;
-import org.apache.nifi.reporting.InitializationException;
-import org.apache.nifi.reporting.ReportingInitializationContext;
-import org.apache.nifi.reporting.ReportingTask;
-import org.apache.nifi.scheduling.SchedulingStrategy;
-import org.apache.nifi.util.DomUtils;
 import org.apache.nifi.util.NiFiProperties;
-
+import org.apache.nifi.util.file.FileUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.w3c.dom.DOMException;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
 
 public final class StandardXMLFlowConfigurationDAO implements FlowConfigurationDAO {
 
     public static final String CONFIGURATION_ARCHIVE_DIR_KEY = "nifi.flow.configuration.archive.dir";
 
     private final Path flowXmlPath;
-    private final Path taskConfigXmlPath;
-    private final ControllerServiceLoader servicerLoader;
     private final StringEncryptor encryptor;
 
     private static final Logger LOG = LoggerFactory.getLogger(StandardXMLFlowConfigurationDAO.class);
 
-    public StandardXMLFlowConfigurationDAO(final Path flowXml, final Path taskConfigXml, final Path serviceConfigXml, final StringEncryptor encryptor) throws IOException {
+    public StandardXMLFlowConfigurationDAO(final Path flowXml, final StringEncryptor encryptor) throws IOException {
         final File flowXmlFile = flowXml.toFile();
         if (!flowXmlFile.exists()) {
             Files.createDirectories(flowXml.getParent());
@@ -96,14 +60,7 @@ public final class StandardXMLFlowConfigurationDAO implements FlowConfigurationD
             throw new IOException(flowXml + " exists but you have insufficient read/write privileges");
         }
 
-        final File taskConfigXmlFile = Objects.requireNonNull(taskConfigXml).toFile();
-        if ((!taskConfigXmlFile.exists() || !taskConfigXmlFile.canRead())) {
-            throw new IOException(taskConfigXml + " does not appear to exist or cannot be read. Cannot load configuration.");
-        }
-
         this.flowXmlPath = flowXml;
-        this.taskConfigXmlPath = taskConfigXml;
-        this.servicerLoader = new ControllerServiceLoader(serviceConfigXml);
         this.encryptor = encryptor;
     }
 
@@ -198,148 +155,4 @@ public final class StandardXMLFlowConfigurationDAO implements FlowConfigurationD
         }
     }
 
-    @Override
-    public List<ReportingTaskNode> loadReportingTasks(final FlowController controller) {
-        final List<ReportingTaskNode> tasks = new ArrayList<>();
-        if (taskConfigXmlPath == null) {
-            LOG.info("No reporting tasks to start");
-            return tasks;
-        }
-
-        try {
-            final URL schemaUrl = getClass().getResource("/ReportingTaskConfiguration.xsd");
-            final Document document = parse(taskConfigXmlPath.toFile(), schemaUrl);
-
-            final NodeList tasksNodes = document.getElementsByTagName("tasks");
-            final Element tasksElement = (Element) tasksNodes.item(0);
-
-            for (final Element taskElement : DomUtils.getChildElementsByTagName(tasksElement, "task")) {
-                //add global properties common to all tasks
-                Map<String, String> properties = new HashMap<>();
-
-                //get properties for the specific reporting task - id, name, class,
-                //and schedulingPeriod must be set
-                final String taskId = DomUtils.getChild(taskElement, "id").getTextContent().trim();
-                final String taskName = DomUtils.getChild(taskElement, "name").getTextContent().trim();
-
-                final List<Element> schedulingStrategyNodeList = DomUtils.getChildElementsByTagName(taskElement, "schedulingStrategy");
-                String schedulingStrategyValue = SchedulingStrategy.TIMER_DRIVEN.name();
-                if (schedulingStrategyNodeList.size() == 1) {
-                    final String specifiedValue = schedulingStrategyNodeList.get(0).getTextContent();
-
-                    try {
-                        schedulingStrategyValue = SchedulingStrategy.valueOf(specifiedValue).name();
-                    } catch (final Exception e) {
-                        throw new RuntimeException("Cannot start Reporting Task with id " + taskId + " because its Scheduling Strategy does not have a valid value", e);
-                    }
-                }
-
-                final SchedulingStrategy schedulingStrategy = SchedulingStrategy.valueOf(schedulingStrategyValue);
-                final String taskSchedulingPeriod = DomUtils.getChild(taskElement, "schedulingPeriod").getTextContent().trim();
-                final String taskClass = DomUtils.getChild(taskElement, "class").getTextContent().trim();
-
-                //optional task-specific properties
-                for (final Element optionalProperty : DomUtils.getChildElementsByTagName(taskElement, "property")) {
-                    final String name = optionalProperty.getAttribute("name");
-                    final String value = optionalProperty.getTextContent().trim();
-                    properties.put(name, value);
-                }
-
-                //set the class to be used for the configured reporting task
-                final ReportingTaskNode reportingTaskNode;
-                try {
-                    reportingTaskNode = controller.createReportingTask(taskClass, taskId);
-                } catch (final ReportingTaskInstantiationException e) {
-                    LOG.error("Unable to load reporting task {} due to {}", new Object[]{taskId, e});
-                    if (LOG.isDebugEnabled()) {
-                        LOG.error("", e);
-                    }
-                    continue;
-                }
-
-                reportingTaskNode.setName(taskName);
-                reportingTaskNode.setScheduldingPeriod(taskSchedulingPeriod);
-                reportingTaskNode.setSchedulingStrategy(schedulingStrategy);
-
-                final ReportingTask reportingTask = reportingTaskNode.getReportingTask();
-
-                final ReportingInitializationContext config = new StandardReportingInitializationContext(taskId, taskName, schedulingStrategy, taskSchedulingPeriod, controller);
-                reportingTask.initialize(config);
-
-                final Map<PropertyDescriptor, String> resolvedProps;
-                try (final NarCloseable narCloseable = NarCloseable.withNarLoader()) {
-                    resolvedProps = new HashMap<>();
-                    for (final Map.Entry<String, String> entry : properties.entrySet()) {
-                        final PropertyDescriptor descriptor = reportingTask.getPropertyDescriptor(entry.getKey());
-                        resolvedProps.put(descriptor, entry.getValue());
-                    }
-                }
-
-                for (final Map.Entry<PropertyDescriptor, String> entry : resolvedProps.entrySet()) {
-                    reportingTaskNode.setProperty(entry.getKey().getName(), entry.getValue());
-                }
-
-                tasks.add(reportingTaskNode);
-                controller.startReportingTask(reportingTaskNode);
-            }
-        } catch (final SAXException | ParserConfigurationException | IOException | DOMException | NumberFormatException | InitializationException t) {
-            LOG.error("Unable to load reporting tasks from {} due to {}", new Object[]{taskConfigXmlPath, t});
-            if (LOG.isDebugEnabled()) {
-                LOG.error("", t);
-            }
-        }
-
-        return tasks;
-    }
-
-    @Override
-    public List<ControllerServiceNode> loadControllerServices(final FlowController controller) throws IOException {
-        return servicerLoader.loadControllerServices(controller);
-    }
-
-    private Document parse(final File xmlFile, final URL schemaUrl) throws SAXException, ParserConfigurationException, IOException {
-        final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
-        final Schema schema = schemaFactory.newSchema(schemaUrl);
-        final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
-        docFactory.setSchema(schema);
-        final DocumentBuilder builder = docFactory.newDocumentBuilder();
-
-        builder.setErrorHandler(new org.xml.sax.ErrorHandler() {
-            @Override
-            public void fatalError(final SAXParseException err) throws SAXException {
-                LOG.error("Config file line " + err.getLineNumber() + ", col " + err.getColumnNumber() + ", uri " + err.getSystemId() + " :message: " + err.getMessage());
-                if (LOG.isDebugEnabled()) {
-                    LOG.error("Error Stack Dump", err);
-                }
-                throw err;
-            }
-
-            @Override
-            public void error(final SAXParseException err) throws SAXParseException {
-                LOG.error("Config file line " + err.getLineNumber() + ", col " + err.getColumnNumber() + ", uri " + err.getSystemId() + " :message: " + err.getMessage());
-                if (LOG.isDebugEnabled()) {
-                    LOG.error("Error Stack Dump", err);
-                }
-                throw err;
-            }
-
-            @Override
-            public void warning(final SAXParseException err) throws SAXParseException {
-                LOG.warn(" Config file line " + err.getLineNumber() + ", uri " + err.getSystemId() + " : message : " + err.getMessage());
-                if (LOG.isDebugEnabled()) {
-                    LOG.warn("Warning stack dump", err);
-                }
-                throw err;
-            }
-        });
-
-        // build the docuemnt
-        final Document document = builder.parse(xmlFile);
-
-        // ensure schema compliance
-        final Validator validator = schema.newValidator();
-        validator.validate(new DOMSource(document));
-
-        return document;
-    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/SimpleProcessLogger.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/SimpleProcessLogger.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/SimpleProcessLogger.java
index 88f1790..25d8f10 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/SimpleProcessLogger.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/SimpleProcessLogger.java
@@ -28,16 +28,16 @@ public class SimpleProcessLogger implements ProcessorLog {
 
     private final Logger logger;
     private final LogRepository logRepository;
-    private final Processor processor;
+    private final Object component;
 
-    public SimpleProcessLogger(final String processorId, final Processor processor) {
-        this.logger = LoggerFactory.getLogger(processor.getClass());
-        this.logRepository = LogRepositoryFactory.getRepository(processorId);
-        this.processor = processor;
+    public SimpleProcessLogger(final String componentId, final Object component) {
+        this.logger = LoggerFactory.getLogger(component.getClass());
+        this.logRepository = LogRepositoryFactory.getRepository(componentId);
+        this.component = component;
     }
 
     private Object[] addProcessor(final Object[] originalArgs) {
-        return prependToArgs(originalArgs, processor);
+        return prependToArgs(originalArgs, component);
     }
 
     private Object[] prependToArgs(final Object[] originalArgs, final Object... toAdd) {
@@ -63,7 +63,7 @@ public class SimpleProcessLogger implements ProcessorLog {
 
     @Override
     public void warn(final String msg, final Throwable t) {
-        warn("{} " + msg, new Object[]{processor}, t);
+        warn("{} " + msg, new Object[]{component}, t);
     }
 
     @Override
@@ -93,15 +93,15 @@ public class SimpleProcessLogger implements ProcessorLog {
     @Override
     public void warn(String msg) {
         msg = "{} " + msg;
-        final Object[] os = {processor};
-        logger.warn(msg, processor);
+        final Object[] os = {component};
+        logger.warn(msg, component);
         logRepository.addLogMessage(LogLevel.WARN, msg, os);
     }
 
     @Override
     public void trace(String msg, Throwable t) {
         msg = "{} " + msg;
-        final Object[] os = {processor};
+        final Object[] os = {component};
         logger.trace(msg, os, t);
         logRepository.addLogMessage(LogLevel.TRACE, msg, os, t);
     }
@@ -117,7 +117,7 @@ public class SimpleProcessLogger implements ProcessorLog {
     @Override
     public void trace(String msg) {
         msg = "{} " + msg;
-        final Object[] os = {processor};
+        final Object[] os = {component};
         logger.trace(msg, os);
         logRepository.addLogMessage(LogLevel.TRACE, msg, os);
     }
@@ -160,7 +160,7 @@ public class SimpleProcessLogger implements ProcessorLog {
     @Override
     public void info(String msg, Throwable t) {
         msg = "{} " + msg;
-        final Object[] os = {processor};
+        final Object[] os = {component};
 
         logger.info(msg, os);
         if (logger.isDebugEnabled()) {
@@ -181,7 +181,7 @@ public class SimpleProcessLogger implements ProcessorLog {
     @Override
     public void info(String msg) {
         msg = "{} " + msg;
-        final Object[] os = {processor};
+        final Object[] os = {component};
 
         logger.info(msg, os);
         logRepository.addLogMessage(LogLevel.INFO, msg, os);
@@ -207,7 +207,7 @@ public class SimpleProcessLogger implements ProcessorLog {
     @Override
     public void error(String msg, Throwable t) {
         msg = "{} " + msg;
-        final Object[] os = {processor};
+        final Object[] os = {component};
 
         logger.error(msg, os, t);
         if (logger.isDebugEnabled()) {
@@ -231,7 +231,7 @@ public class SimpleProcessLogger implements ProcessorLog {
     @Override
     public void error(String msg) {
         msg = "{} " + msg;
-        final Object[] os = {processor};
+        final Object[] os = {component};
 
         logger.error(msg, os);
         logRepository.addLogMessage(LogLevel.ERROR, msg, os);
@@ -239,7 +239,7 @@ public class SimpleProcessLogger implements ProcessorLog {
 
     private Object[] addProcessorAndThrowable(final Object[] os, final Throwable t) {
         final Object[] modifiedArgs = new Object[os.length + 2];
-        modifiedArgs[0] = processor.toString();
+        modifiedArgs[0] = component.toString();
         for (int i = 0; i < os.length; i++) {
             modifiedArgs[i + 1] = os[i];
         }
@@ -263,7 +263,7 @@ public class SimpleProcessLogger implements ProcessorLog {
     @Override
     public void debug(String msg, Throwable t) {
         msg = "{} " + msg;
-        final Object[] os = {processor};
+        final Object[] os = {component};
 
         logger.debug(msg, os, t);
         logRepository.addLogMessage(LogLevel.DEBUG, msg, os, t);
@@ -298,7 +298,7 @@ public class SimpleProcessLogger implements ProcessorLog {
     @Override
     public void debug(String msg) {
         msg = "{} " + msg;
-        final Object[] os = {processor};
+        final Object[] os = {component};
 
         logger.debug(msg, os);
         logRepository.addLogMessage(LogLevel.DEBUG, msg, os);

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardProcessContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardProcessContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardProcessContext.java
index cd0d31c..d14a459 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardProcessContext.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardProcessContext.java
@@ -142,6 +142,11 @@ public class StandardProcessContext implements ProcessContext, ControllerService
     }
 
     @Override
+    public boolean isControllerServiceEnabling(final String serviceIdentifier) {
+        return controllerServiceProvider.isControllerServiceEnabling(serviceIdentifier);
+    }
+    
+    @Override
     public ControllerServiceLookup getControllerServiceLookup() {
         return this;
     }
@@ -170,4 +175,9 @@ public class StandardProcessContext implements ProcessContext, ControllerService
         return set;
     }
     
+    @Override
+    public String getControllerServiceName(final String serviceIdentifier) {
+    	return controllerServiceProvider.getControllerServiceName(serviceIdentifier);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardSchedulingContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardSchedulingContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardSchedulingContext.java
index ac58504..c37a80d 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardSchedulingContext.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardSchedulingContext.java
@@ -25,6 +25,7 @@ import org.apache.nifi.controller.ControllerServiceLookup;
 import org.apache.nifi.controller.ProcessorNode;
 import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.controller.service.ControllerServiceProvider;
+import org.apache.nifi.controller.service.ControllerServiceState;
 
 public class StandardSchedulingContext implements SchedulingContext {
 
@@ -45,8 +46,8 @@ public class StandardSchedulingContext implements SchedulingContext {
             throw new IllegalArgumentException("Cannot lease Controller Service because no Controller Service exists with identifier " + identifier);
         }
 
-        if (serviceNode.isDisabled()) {
-            throw new IllegalStateException("Cannot lease Controller Service because Controller Service " + serviceNode.getProxiedControllerService() + " is currently disabled");
+        if ( serviceNode.getState() != ControllerServiceState.ENABLED ) {
+            throw new IllegalStateException("Cannot lease Controller Service because Controller Service " + serviceNode.getProxiedControllerService() + " is not currently enabled");
         }
 
         if (!serviceNode.isValid()) {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContext.java
index 99322be..c51cb9a 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContext.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContext.java
@@ -20,6 +20,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.nifi.attribute.expression.language.PreparedQuery;
 import org.apache.nifi.attribute.expression.language.Query;
@@ -41,11 +42,17 @@ public class StandardValidationContext implements ValidationContext {
     private final Map<PropertyDescriptor, PreparedQuery> preparedQueries;
     private final Map<String, Boolean> expressionLanguageSupported;
     private final String annotationData;
+    private final Set<String> serviceIdentifiersToNotValidate;
 
     public StandardValidationContext(final ControllerServiceProvider controllerServiceProvider, final Map<PropertyDescriptor, String> properties, final String annotationData) {
+        this(controllerServiceProvider, Collections.<String>emptySet(), properties, annotationData);
+    }
+    
+    public StandardValidationContext(final ControllerServiceProvider controllerServiceProvider, final Set<String> serviceIdentifiersToNotValidate, final Map<PropertyDescriptor, String> properties, final String annotationData) {
         this.controllerServiceProvider = controllerServiceProvider;
         this.properties = new HashMap<>(properties);
         this.annotationData = annotationData;
+        this.serviceIdentifiersToNotValidate = serviceIdentifiersToNotValidate;
 
         preparedQueries = new HashMap<>(properties.size());
         for (final Map.Entry<PropertyDescriptor, String> entry : properties.entrySet()) {
@@ -101,6 +108,11 @@ public class StandardValidationContext implements ValidationContext {
     public ControllerServiceLookup getControllerServiceLookup() {
         return controllerServiceProvider;
     }
+
+    @Override
+    public boolean isValidationRequired(final ControllerService service) {
+        return !serviceIdentifiersToNotValidate.contains(service.getIdentifier());
+    }
     
     @Override
     public boolean isExpressionLanguagePresent(final String value) {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContextFactory.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContextFactory.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContextFactory.java
index e172f93..c3df987 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContextFactory.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContextFactory.java
@@ -17,6 +17,7 @@
 package org.apache.nifi.processor;
 
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.ValidationContext;
@@ -36,4 +37,8 @@ public class StandardValidationContextFactory implements ValidationContextFactor
         return new StandardValidationContext(serviceProvider, properties, annotationData);
     }
 
+    @Override
+    public ValidationContext newValidationContext(final Set<String> serviceIdentifiersToNotValidate, final Map<PropertyDescriptor, String> properties, final String annotationData) {
+        return new StandardValidationContext(serviceProvider, serviceIdentifiersToNotValidate, properties, annotationData);
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/util/DomUtils.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/util/DomUtils.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/util/DomUtils.java
index c8b6573..da4f04d 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/util/DomUtils.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/util/DomUtils.java
@@ -25,6 +25,16 @@ import org.w3c.dom.NodeList;
 
 public class DomUtils {
 
+    public static String getChildText(final Element element, final String tagName) {
+        final Element childElement = getChild(element, tagName);
+        if ( childElement == null ) {
+            return null;
+        }
+        
+        final String text = childElement.getTextContent();
+        return (text == null) ? null : text.trim();
+    }
+    
     public static Element getChild(final Element element, final String tagName) {
         final List<Element> children = getChildElementsByTagName(element, tagName);
         if (children.isEmpty()) {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/ControllerServiceConfiguration.xsd
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/ControllerServiceConfiguration.xsd b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/ControllerServiceConfiguration.xsd
deleted file mode 100644
index d3efed1..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/ControllerServiceConfiguration.xsd
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0"?>
-<!--
-  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.
--->
-<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0">
-    <xs:element name="services" type="ControllerServices"/>
-
-    <xs:complexType name="ControllerServices">
-        <xs:sequence>
-
-            <!-- Each "processor" defines the actual dataflow work horses that make dataflow happen-->
-            <xs:element name="service" type="ControllerServiceType" minOccurs="0" maxOccurs="unbounded"/>
-
-        </xs:sequence>
-    </xs:complexType>
-
-    <!-- the Controller Task "id" is a key that should be valid within each flowController-->
-    <xs:complexType name="ControllerServiceType">
-        <xs:sequence>
-
-            <!-- The "id" is a name used to uniquely identify the Controller Task. -->
-            <xs:element name="identifier" type="NonEmptyStringType"/>
-
-            <xs:element name="name" type="xs:string" minOccurs="0" maxOccurs="1" />
-
-            <!-- "class" is the actual Java class that performs the type of controller task desired-->
-            <xs:element name="class" type="NonEmptyStringType"/>
-
-            <!-- "optionalTaskProperty" are properties that may exist and offer further information about a task.
-            For instance, information about where a specific task should send information. -->
-            <xs:element name="property" type="PropertyType" minOccurs="0" maxOccurs="unbounded" />
-        </xs:sequence>
-    </xs:complexType>
-
-    <!-- Name/Value properties-->
-    <xs:complexType name="PropertyType">
-        <xs:simpleContent>
-            <xs:extension base="xs:string">
-                <xs:attribute name="name" type="xs:string"></xs:attribute>
-            </xs:extension>
-        </xs:simpleContent>
-    </xs:complexType>
-
-
-    <xs:simpleType name="NonEmptyStringType">
-        <xs:restriction base="xs:string">
-            <xs:minLength value="1"/>
-        </xs:restriction>
-    </xs:simpleType>
-</xs:schema>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/FlowConfiguration.xsd
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/FlowConfiguration.xsd b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/FlowConfiguration.xsd
index 1e6c25c..00c71ac 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/FlowConfiguration.xsd
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/FlowConfiguration.xsd
@@ -28,6 +28,10 @@
 
             <!-- Groupings of Processors/Ports -->
             <xs:element name="rootGroup" type="RootProcessGroupType" />
+            
+            <xs:element name="controllerServices" type="ControllerServicesType" minOccurs="0" maxOccurs="1" />
+            
+            <xs:element name="reportingTasks" type="ReportingTasksType" minOccurs="0" maxOccurs="1" />
         </xs:sequence>
     </xs:complexType>
 	
@@ -58,11 +62,6 @@
             IFF schedulingStrategy is EVENT_DRIVEN -->
             <xs:element name="maxConcurrentTasks" type="xs:nonNegativeInteger"/>
 
-            <!-- "schedulingPeriodSeconds" is the maximum number of seconds that should elapse
-            between successive executions of each task for this configured processor.  If a
-            task takes longer than the period specified then the next execution of this
-            task will take place immediately after termination of the previous run and as soon
-            as their is an available thread.-->
             <xs:element name="schedulingPeriod" type="NonEmptyStringType"/>
             
             <xs:element name="penalizationPeriod" type="TimePeriod" />
@@ -332,4 +331,44 @@
             <xs:enumeration value="CRON_DRIVEN"></xs:enumeration>
         </xs:restriction>
     </xs:simpleType>
+    
+    <xs:complexType name="ControllerServicesType">
+    	<xs:sequence>
+    		<xs:element name="controllerService" type="ControllerServiceType" minOccurs="0" maxOccurs="unbounded" />
+    	</xs:sequence>
+    </xs:complexType>
+    
+    <xs:complexType name="ControllerServiceType">
+    	<xs:sequence>
+    		<xs:element name="id" type="NonEmptyStringType" />
+    		<xs:element name="name" type="NonEmptyStringType" />
+    		<xs:element name="comment" type="xs:string" />
+    		<xs:element name="class" type="NonEmptyStringType" />
+    		<xs:element name="enabled" type="xs:boolean" />
+    		
+            <xs:element name="property" type="PropertyType" minOccurs="0" maxOccurs="unbounded"/>
+            <xs:element name="annotationData" type="xs:string" minOccurs="0" maxOccurs="1" />
+    	</xs:sequence>
+    </xs:complexType>
+    
+    <xs:complexType name="ReportingTasksType">
+    	<xs:sequence>
+    		<xs:element name="reportingTask" type="ReportingTaskType" minOccurs="0" maxOccurs="unbounded" />
+    	</xs:sequence>
+    </xs:complexType>
+    
+    <xs:complexType name="ReportingTaskType">
+    	<xs:sequence>
+    		<xs:element name="id" type="NonEmptyStringType" />
+    		<xs:element name="name" type="NonEmptyStringType" />
+    		<xs:element name="comment" type="xs:string" />
+    		<xs:element name="class" type="NonEmptyStringType" />
+            <xs:element name="schedulingPeriod" type="NonEmptyStringType"/>
+            <xs:element name="schedulingState" type="ScheduledState" />
+            <xs:element name="schedulingStrategy" type="SchedulingStrategy" />
+    		
+            <xs:element name="property" type="PropertyType" minOccurs="0" maxOccurs="unbounded"/>
+            <xs:element name="annotationData" type="xs:string" minOccurs="0" maxOccurs="1" />
+    	</xs:sequence>
+    </xs:complexType>
 </xs:schema>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/ReportingTaskConfiguration.xsd
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/ReportingTaskConfiguration.xsd b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/ReportingTaskConfiguration.xsd
deleted file mode 100644
index dcf1090..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/ReportingTaskConfiguration.xsd
+++ /dev/null
@@ -1,87 +0,0 @@
-<?xml version="1.0"?>
-<!--
-  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.
--->
-<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0">
-    <xs:element name="tasks" type="ReportingTasks"/>
-
-    <xs:complexType name="ReportingTasks">
-        <xs:sequence>
-
-            <!-- properties that may exist and offer further information about all tasks.
-            For instance, possibly the system that is sending the information. -->
-            <xs:element name="property" type="PropertyType" minOccurs="0" maxOccurs="unbounded"/>
-
-            <!-- Each "processor" defines the actual dataflow work horses that make dataflow happen-->
-            <xs:element name="task" type="ReportingTaskType" minOccurs="0" maxOccurs="unbounded"/>
-
-        </xs:sequence>
-    </xs:complexType>
-
-    <!-- the Controller Task "id" is a key that should be valid within each flowController-->
-    <xs:complexType name="ReportingTaskType">
-        <xs:sequence>
-
-            <!-- The "id" is a name used to uniquely identify the Controller Task. -->
-            <xs:element name="id" type="NonEmptyStringType"/>
-
-            <!-- The "name" is a nicely displayable description of the Controller Task's duty-->
-            <xs:element name="name" type="NonEmptyStringType"/>
-
-            <!-- "class" is the actual Java class that performs the type of controller task desired-->
-            <xs:element name="class" type="NonEmptyStringType"/>
-
-            <!-- 
-                "schedulingPeriod" is the amount of time that should elapse between successive 
-                executions of this task. The timer starts as soon as an execution finishes
-            -->
-            <xs:element name="schedulingPeriod" type="NonEmptyStringType"/>
-            
-            <xs:element name="schedulingStrategy" type="SchedulingStrategyType" minOccurs="0" default="TIMER_DRIVEN" />
-
-            <!-- "optionalTaskProperty" are properties that may exist and offer further information about a task.
-            For instance, information about where a specific task should send information. -->
-            <xs:element name="property" type="PropertyType" minOccurs="0" maxOccurs="unbounded" />
-        </xs:sequence>
-    </xs:complexType>
-
-    <!-- Name/Value properties-->
-    <xs:complexType name="PropertyType">
-        <xs:simpleContent>
-            <xs:extension base="xs:string">
-                <xs:attribute name="name" type="xs:string"></xs:attribute>
-            </xs:extension>
-        </xs:simpleContent>
-    </xs:complexType>
-
-    <xs:simpleType name="SchedulingStrategyType">
-        <xs:restriction base="xs:string">
-            <xs:enumeration value="TIMER_DRIVEN"></xs:enumeration>
-            <xs:enumeration value="CRON_DRIVEN"></xs:enumeration>
-        </xs:restriction>
-    </xs:simpleType>
-
-    <xs:simpleType name="NonEmptyStringType">
-        <xs:restriction base="xs:string">
-            <xs:minLength value="1"/>
-        </xs:restriction>
-    </xs:simpleType>
-    
-    <xs:simpleType name="TimePeriod">
-        <xs:restriction base="xs:string">
-            <xs:pattern value="\d+\s*(ns|nano|nanos|nanoseconds|ms|milli|millis|milliseconds|s|sec|secs|seconds|m|min|mins|minutes|h|hr|hrs|hours|d|day|days)"></xs:pattern>
-        </xs:restriction>
-    </xs:simpleType>
-    
-</xs:schema>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderTest.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderTest.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderTest.java
index b889bc8..7fef706 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderTest.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderTest.java
@@ -43,7 +43,7 @@ public class StandardControllerServiceProviderTest {
     public void setup() throws Exception {
     	String id = "id";
     	String clazz = "org.apache.nifi.controller.service.util.TestControllerService";  
-    	ControllerServiceProvider provider = new StandardControllerServiceProvider();
+    	ControllerServiceProvider provider = new StandardControllerServiceProvider(null, null);
     	ControllerServiceNode node = provider.createControllerService(clazz,id,true);
     	proxied = node.getProxiedControllerService();
     	implementation = node.getControllerServiceImplementation();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/TestStandardControllerServiceProvider.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/TestStandardControllerServiceProvider.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/TestStandardControllerServiceProvider.java
new file mode 100644
index 0000000..3dc1752
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/TestStandardControllerServiceProvider.java
@@ -0,0 +1,385 @@
+/*
+ * 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.controller.service;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.nifi.controller.ProcessScheduler;
+import org.apache.nifi.controller.ProcessorNode;
+import org.apache.nifi.controller.ScheduledState;
+import org.apache.nifi.controller.StandardProcessorNode;
+import org.apache.nifi.controller.service.mock.DummyProcessor;
+import org.apache.nifi.controller.service.mock.ServiceA;
+import org.apache.nifi.controller.service.mock.ServiceB;
+import org.apache.nifi.groups.ProcessGroup;
+import org.apache.nifi.processor.StandardProcessorInitializationContext;
+import org.apache.nifi.processor.StandardValidationContextFactory;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+public class TestStandardControllerServiceProvider {
+
+    private ProcessScheduler createScheduler() {
+        final ProcessScheduler scheduler = Mockito.mock(ProcessScheduler.class);
+        Mockito.doAnswer(new Answer<Object>() {
+            @Override
+            public Object answer(final InvocationOnMock invocation) throws Throwable {
+                final ControllerServiceNode node = (ControllerServiceNode) invocation.getArguments()[0];
+                node.verifyCanEnable();
+                node.setState(ControllerServiceState.ENABLED);
+                return null;
+            }
+        }).when(scheduler).enableControllerService(Mockito.any(ControllerServiceNode.class));
+        
+        Mockito.doAnswer(new Answer<Object>() {
+            @Override
+            public Object answer(final InvocationOnMock invocation) throws Throwable {
+                final ControllerServiceNode node = (ControllerServiceNode) invocation.getArguments()[0];
+                node.verifyCanDisable();
+                node.setState(ControllerServiceState.DISABLED);
+                return null;
+            }
+        }).when(scheduler).disableControllerService(Mockito.any(ControllerServiceNode.class));
+        
+        return scheduler;
+    }
+    
+    @Test
+    public void testDisableControllerService() {
+        final ProcessScheduler scheduler = createScheduler();
+        final StandardControllerServiceProvider provider = new StandardControllerServiceProvider(scheduler, null);
+        
+        final ControllerServiceNode serviceNode = provider.createControllerService(ServiceB.class.getName(), "B", false);
+        provider.enableControllerService(serviceNode);
+        provider.disableControllerService(serviceNode);
+    }
+    
+    @Test
+    public void testEnableDisableWithReference() {
+        final ProcessScheduler scheduler = createScheduler();
+        final StandardControllerServiceProvider provider = new StandardControllerServiceProvider(scheduler, null);
+        
+        final ControllerServiceNode serviceNodeB = provider.createControllerService(ServiceB.class.getName(), "B", false);
+        final ControllerServiceNode serviceNodeA = provider.createControllerService(ServiceA.class.getName(), "A", false);
+        
+        serviceNodeA.setProperty(ServiceA.OTHER_SERVICE.getName(), "B");
+        
+        try {
+            provider.enableControllerService(serviceNodeA);
+            Assert.fail("Was able to enable Service A but Service B is disabled.");
+        } catch (final IllegalStateException expected) {
+        }
+        
+        provider.enableControllerService(serviceNodeB);
+        provider.enableControllerService(serviceNodeA);
+        
+        try {
+            provider.disableControllerService(serviceNodeB);
+            Assert.fail("Was able to disable Service B but Service A is enabled and references B");
+        } catch (final IllegalStateException expected) {
+        }
+        
+        provider.disableControllerService(serviceNodeA);
+        provider.disableControllerService(serviceNodeB);
+    }
+    
+    
+    @Test
+    public void testEnableReferencingServicesGraph() {
+        final ProcessScheduler scheduler = createScheduler();
+        final StandardControllerServiceProvider provider = new StandardControllerServiceProvider(scheduler, null);
+        
+        // build a graph of controller services with dependencies as such:
+        //
+        // A -> B -> D
+        // C ---^----^
+        //
+        // In other words, A references B, which references D.
+        // AND
+        // C references B and D.
+        //
+        // So we have to verify that if D is enabled, when we enable its referencing services,
+        // we enable C and B, even if we attempt to enable C before B... i.e., if we try to enable C, we cannot do so
+        // until B is first enabled so ensure that we enable B first.
+        
+        final ControllerServiceNode serviceNode1 = provider.createControllerService(ServiceA.class.getName(), "1", false);
+        final ControllerServiceNode serviceNode2 = provider.createControllerService(ServiceA.class.getName(), "2", false);
+        final ControllerServiceNode serviceNode3 = provider.createControllerService(ServiceA.class.getName(), "3", false);
+        final ControllerServiceNode serviceNode4 = provider.createControllerService(ServiceB.class.getName(), "4", false);
+        
+        serviceNode1.setProperty(ServiceA.OTHER_SERVICE.getName(), "2");
+        serviceNode2.setProperty(ServiceA.OTHER_SERVICE.getName(), "4");
+        serviceNode3.setProperty(ServiceA.OTHER_SERVICE.getName(), "2");
+        serviceNode3.setProperty(ServiceA.OTHER_SERVICE_2.getName(), "4");
+        
+        provider.enableControllerService(serviceNode4);
+        provider.enableReferencingServices(serviceNode4);
+        
+        assertEquals(ControllerServiceState.ENABLED, serviceNode3.getState());
+        assertEquals(ControllerServiceState.ENABLED, serviceNode2.getState());
+        assertEquals(ControllerServiceState.ENABLED, serviceNode1.getState());
+    }
+    
+    
+    @Test
+    public void testStartStopReferencingComponents() {
+        final ProcessScheduler scheduler = createScheduler();
+        final StandardControllerServiceProvider provider = new StandardControllerServiceProvider(scheduler, null);
+        
+        // build a graph of reporting tasks and controller services with dependencies as such:
+        //
+        // Processor P1 -> A -> B -> D
+        // Processor P2 -> C ---^----^
+        //
+        // In other words, Processor P1 references Controller Service A, which references B, which references D.
+        // AND
+        // Processor P2 references Controller Service C, which references B and D.
+        //
+        // So we have to verify that if D is enabled, when we enable its referencing services,
+        // we enable C and B, even if we attempt to enable C before B... i.e., if we try to enable C, we cannot do so
+        // until B is first enabled so ensure that we enable B first.
+        
+        final ControllerServiceNode serviceNode1 = provider.createControllerService(ServiceA.class.getName(), "1", false);
+        final ControllerServiceNode serviceNode2 = provider.createControllerService(ServiceA.class.getName(), "2", false);
+        final ControllerServiceNode serviceNode3 = provider.createControllerService(ServiceA.class.getName(), "3", false);
+        final ControllerServiceNode serviceNode4 = provider.createControllerService(ServiceB.class.getName(), "4", false);
+        
+        final ProcessGroup mockProcessGroup = Mockito.mock(ProcessGroup.class);
+        Mockito.doAnswer(new Answer<Object>() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable {
+                final ProcessorNode procNode = (ProcessorNode) invocation.getArguments()[0];
+                procNode.verifyCanStart();
+                procNode.setScheduledState(ScheduledState.RUNNING);
+                return null;
+            }
+        }).when(mockProcessGroup).startProcessor(Mockito.any(ProcessorNode.class));
+        
+        Mockito.doAnswer(new Answer<Object>() {
+            @Override
+            public Object answer(final InvocationOnMock invocation) throws Throwable {
+                final ProcessorNode procNode = (ProcessorNode) invocation.getArguments()[0];
+                procNode.verifyCanStop();
+                procNode.setScheduledState(ScheduledState.STOPPED);
+                return null;
+            }
+        }).when(mockProcessGroup).stopProcessor(Mockito.any(ProcessorNode.class));
+        
+        final String id1 = UUID.randomUUID().toString();
+        final ProcessorNode procNodeA = new StandardProcessorNode(new DummyProcessor(), id1,
+                new StandardValidationContextFactory(provider), scheduler, provider);
+        procNodeA.getProcessor().initialize(new StandardProcessorInitializationContext(id1, null, provider));
+        procNodeA.setProperty(DummyProcessor.SERVICE.getName(), "1");
+        procNodeA.setProcessGroup(mockProcessGroup);
+        
+        final String id2 = UUID.randomUUID().toString();
+        final ProcessorNode procNodeB = new StandardProcessorNode(new DummyProcessor(),id2,
+                new StandardValidationContextFactory(provider), scheduler, provider);
+        procNodeB.getProcessor().initialize(new StandardProcessorInitializationContext(id2, null, provider));
+        procNodeB.setProperty(DummyProcessor.SERVICE.getName(), "3");
+        procNodeB.setProcessGroup(mockProcessGroup);
+        
+        serviceNode1.setProperty(ServiceA.OTHER_SERVICE.getName(), "2");
+        serviceNode2.setProperty(ServiceA.OTHER_SERVICE.getName(), "4");
+        serviceNode3.setProperty(ServiceA.OTHER_SERVICE.getName(), "2");
+        serviceNode3.setProperty(ServiceA.OTHER_SERVICE_2.getName(), "4");
+        
+        provider.enableControllerService(serviceNode4);
+        provider.enableReferencingServices(serviceNode4);
+        provider.scheduleReferencingComponents(serviceNode4);
+        
+        assertEquals(ControllerServiceState.ENABLED, serviceNode3.getState());
+        assertEquals(ControllerServiceState.ENABLED, serviceNode2.getState());
+        assertEquals(ControllerServiceState.ENABLED, serviceNode1.getState());
+        assertTrue(procNodeA.isRunning());
+        assertTrue(procNodeB.isRunning());
+        
+        // stop processors and verify results.
+        provider.unscheduleReferencingComponents(serviceNode4);
+        assertFalse(procNodeA.isRunning());
+        assertFalse(procNodeB.isRunning());
+        assertEquals(ControllerServiceState.ENABLED, serviceNode3.getState());
+        assertEquals(ControllerServiceState.ENABLED, serviceNode2.getState());
+        assertEquals(ControllerServiceState.ENABLED, serviceNode1.getState());
+        
+        provider.disableReferencingServices(serviceNode4);
+        assertEquals(ControllerServiceState.DISABLED, serviceNode3.getState());
+        assertEquals(ControllerServiceState.DISABLED, serviceNode2.getState());
+        assertEquals(ControllerServiceState.DISABLED, serviceNode1.getState());
+        assertEquals(ControllerServiceState.ENABLED, serviceNode4.getState());
+        
+        provider.disableControllerService(serviceNode4);
+        assertEquals(ControllerServiceState.DISABLED, serviceNode4.getState());
+    }
+    
+    
+    @Test
+    public void testOrderingOfServices() {
+        final StandardControllerServiceProvider provider = new StandardControllerServiceProvider(null, null);
+        final ControllerServiceNode serviceNode1 = provider.createControllerService(ServiceA.class.getName(), "1", false);
+        final ControllerServiceNode serviceNode2 = provider.createControllerService(ServiceB.class.getName(), "2", false);
+
+        serviceNode1.setProperty(ServiceA.OTHER_SERVICE.getName(), "2");
+
+        final Map<String, ControllerServiceNode> nodeMap = new LinkedHashMap<>();
+        nodeMap.put("1", serviceNode1);
+        nodeMap.put("2", serviceNode2);
+        
+        List<List<ControllerServiceNode>> branches = StandardControllerServiceProvider.determineEnablingOrder(nodeMap);
+        assertEquals(2, branches.size());
+        List<ControllerServiceNode> ordered = branches.get(0);
+        assertEquals(2, ordered.size());
+        assertTrue(ordered.get(0) == serviceNode2);
+        assertTrue(ordered.get(1) == serviceNode1);
+        assertEquals(1, branches.get(1).size());
+        assertTrue(branches.get(1).get(0) == serviceNode2);
+        
+        nodeMap.clear();
+        nodeMap.put("2", serviceNode2);
+        nodeMap.put("1", serviceNode1);
+        
+        branches = StandardControllerServiceProvider.determineEnablingOrder(nodeMap);
+        assertEquals(2, branches.size());
+        ordered = branches.get(1);
+        assertEquals(2, ordered.size());
+        assertTrue(ordered.get(0) == serviceNode2);
+        assertTrue(ordered.get(1) == serviceNode1);
+        assertEquals(1, branches.get(0).size());
+        assertTrue(branches.get(0).get(0) == serviceNode2);
+        
+        // add circular dependency on self.
+        nodeMap.clear();
+        serviceNode1.setProperty(ServiceA.OTHER_SERVICE_2.getName(), "1");
+        nodeMap.put("1", serviceNode1);
+        nodeMap.put("2", serviceNode2);
+        
+        branches = StandardControllerServiceProvider.determineEnablingOrder(nodeMap);
+        assertEquals(2, branches.size());
+        ordered = branches.get(0);
+        assertEquals(2, ordered.size());
+        assertTrue(ordered.get(0) == serviceNode2);
+        assertTrue(ordered.get(1) == serviceNode1);
+        
+        nodeMap.clear();
+        nodeMap.put("2", serviceNode2);
+        nodeMap.put("1", serviceNode1);
+        branches = StandardControllerServiceProvider.determineEnablingOrder(nodeMap);
+        assertEquals(2, branches.size());
+        ordered = branches.get(1);
+        assertEquals(2, ordered.size());
+        assertTrue(ordered.get(0) == serviceNode2);
+        assertTrue(ordered.get(1) == serviceNode1);
+        
+        // add circular dependency once removed. In this case, we won't actually be able to enable these because of the
+        // circular dependency because they will never be valid because they will always depend on a disabled service.
+        // But we want to ensure that the method returns successfully without throwing a StackOverflowException or anything
+        // like that.
+        nodeMap.clear();
+        final ControllerServiceNode serviceNode3 = provider.createControllerService(ServiceA.class.getName(), "3", false);
+        serviceNode1.setProperty(ServiceA.OTHER_SERVICE.getName(), "3");
+        serviceNode3.setProperty(ServiceA.OTHER_SERVICE.getName(), "1");
+        nodeMap.put("1", serviceNode1);
+        nodeMap.put("3", serviceNode3);
+        branches = StandardControllerServiceProvider.determineEnablingOrder(nodeMap);
+        assertEquals(2, branches.size());
+        ordered = branches.get(0);
+        assertEquals(2, ordered.size());
+        assertTrue(ordered.get(0) == serviceNode3);
+        assertTrue(ordered.get(1) == serviceNode1);
+        
+        nodeMap.clear();
+        nodeMap.put("3", serviceNode3);
+        nodeMap.put("1", serviceNode1);
+        branches = StandardControllerServiceProvider.determineEnablingOrder(nodeMap);
+        assertEquals(2, branches.size());
+        ordered = branches.get(1);
+        assertEquals(2, ordered.size());
+        assertTrue(ordered.get(0) == serviceNode3);
+        assertTrue(ordered.get(1) == serviceNode1);
+        
+        
+        // Add multiple completely disparate branches.
+        nodeMap.clear();
+        serviceNode1.setProperty(ServiceA.OTHER_SERVICE.getName(), "2");
+        final ControllerServiceNode serviceNode4 = provider.createControllerService(ServiceB.class.getName(), "4", false);
+        final ControllerServiceNode serviceNode5 = provider.createControllerService(ServiceB.class.getName(), "5", false);
+        serviceNode3.setProperty(ServiceA.OTHER_SERVICE.getName(), "4");
+        nodeMap.put("1", serviceNode1);
+        nodeMap.put("2", serviceNode2);
+        nodeMap.put("3", serviceNode3);
+        nodeMap.put("4", serviceNode4);
+        nodeMap.put("5", serviceNode5);
+        
+        branches = StandardControllerServiceProvider.determineEnablingOrder(nodeMap);
+        assertEquals(5, branches.size());
+
+        ordered = branches.get(0);
+        assertEquals(2, ordered.size());
+        assertTrue(ordered.get(0) == serviceNode2);
+        assertTrue(ordered.get(1) == serviceNode1);
+        
+        assertEquals(1, branches.get(1).size());
+        assertTrue(branches.get(1).get(0) == serviceNode2);
+        
+        ordered = branches.get(2);
+        assertEquals(2, ordered.size());
+        assertTrue(ordered.get(0) == serviceNode4);
+        assertTrue(ordered.get(1) == serviceNode3);
+        
+        assertEquals(1, branches.get(3).size());
+        assertTrue(branches.get(3).get(0) == serviceNode4);
+        
+        assertEquals(1, branches.get(4).size());
+        assertTrue(branches.get(4).get(0) == serviceNode5);
+        
+        // create 2 branches both dependent on the same service
+        nodeMap.clear();
+        serviceNode1.setProperty(ServiceA.OTHER_SERVICE.getName(), "2");
+        serviceNode3.setProperty(ServiceA.OTHER_SERVICE.getName(), "2");
+        nodeMap.put("1", serviceNode1);
+        nodeMap.put("2", serviceNode2);
+        nodeMap.put("3", serviceNode3);
+        
+        branches = StandardControllerServiceProvider.determineEnablingOrder(nodeMap);
+        assertEquals(3, branches.size());
+        
+        ordered = branches.get(0);
+        assertEquals(2, ordered.size());
+        assertTrue(ordered.get(0) == serviceNode2);
+        assertTrue(ordered.get(1) == serviceNode1);
+        
+        ordered = branches.get(1);
+        assertEquals(1, ordered.size());
+        assertTrue(ordered.get(0) == serviceNode2);
+        
+        ordered = branches.get(2);
+        assertEquals(2, ordered.size());
+        assertTrue(ordered.get(0) == serviceNode2);
+        assertTrue(ordered.get(1) == serviceNode3);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/DummyProcessor.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/DummyProcessor.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/DummyProcessor.java
new file mode 100644
index 0000000..615e172
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/DummyProcessor.java
@@ -0,0 +1,49 @@
+/*
+ * 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.controller.service.mock;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+
+public class DummyProcessor extends AbstractProcessor {
+
+    public static final PropertyDescriptor SERVICE = new PropertyDescriptor.Builder()
+        .name("Controller Service")
+        .identifiesControllerService(ControllerService.class)
+        .required(true)
+        .build();
+    
+    
+    @Override
+    protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        final List<PropertyDescriptor> descriptors = new ArrayList<>();
+        descriptors.add(SERVICE);
+        return descriptors;
+    }
+    
+    @Override
+    public void onTrigger(ProcessContext context, ProcessSession session) throws ProcessException {
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/ServiceA.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/ServiceA.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/ServiceA.java
new file mode 100644
index 0000000..4918468
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/mock/ServiceA.java
@@ -0,0 +1,49 @@
+/*
+ * 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.controller.service.mock;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.controller.AbstractControllerService;
+import org.apache.nifi.controller.ControllerService;
+
+public class ServiceA extends AbstractControllerService {
+
+    public static final PropertyDescriptor OTHER_SERVICE = new PropertyDescriptor.Builder()
+        .name("Other Service")
+        .identifiesControllerService(ControllerService.class)
+        .required(true)
+        .build();
+    
+    public static final PropertyDescriptor OTHER_SERVICE_2 = new PropertyDescriptor.Builder()
+        .name("Other Service 2")
+        .identifiesControllerService(ControllerService.class)
+        .required(false)
+        .build();
+
+    
+    @Override
+    protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        final List<PropertyDescriptor> descriptors = new ArrayList<>();
+        descriptors.add(OTHER_SERVICE);
+        descriptors.add(OTHER_SERVICE_2);
+        return descriptors;
+    }
+    
+}


[09/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css
index 3afdc12..e161efc 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/main.css
@@ -126,6 +126,10 @@ div.nifi-tooltip {
     white-space: normal;
 }
 
+input.filter-list {
+    color: #888;
+}
+
 .table-cell {
     overflow: hidden;
     white-space: nowrap;
@@ -134,6 +138,14 @@ div.nifi-tooltip {
     margin-top: 4px;
 }
 
+.collapsed {
+    background: transparent url(../images/iconTwistArrow.png) no-repeat scroll top left;
+}
+
+.expanded {
+    background: transparent url(../images/iconTwistArrow.png) no-repeat scroll top right;
+}
+
 input[type=text], textarea {
     background: white url(../images/bgInputText.png) repeat-x scroll top;
     border: 1px solid #ccc;
@@ -177,6 +189,15 @@ div.ajax-loading {
     background-image: url(../images/iconLoading.gif);
 }
 
+div.ajax-complete {
+    background-image: url(../images/iconCommit.png);
+}
+
+div.ajax-error {
+    /*margin-left: -2px;*/
+    background-image: url(../images/iconDelete.png);
+}
+
 div.button-refresh {
     background: transparent url(../images/buttonRefresh.png) no-repeat scroll top left;
 }
@@ -198,6 +219,13 @@ div.disabled {
     background: transparent url(../images/iconDisable.png) repeat scroll 0 0;
 }
 
+div.enabled {
+    float: left;
+    width: 16px;
+    height: 16px;
+    background: transparent url(../images/iconEnable.png) repeat scroll 0 0;
+}
+
 div.stopped {
     float: left;
     width: 16px;
@@ -249,20 +277,6 @@ div.has-bulletins {
     Styles for status/history/user tables.
 */
 
-div.expansion-button {
-    width: 11px;
-    height: 11px;
-    margin-left: 5px;
-}
-
-div.collapsed {
-    background: transparent url(../images/buttonExpandCollapse.png) no-repeat scroll top left;
-}
-
-div.expanded {
-    background: transparent url(../images/buttonExpandCollapse.png) no-repeat scroll top right;
-}
-
 span.label, div.label {
     font-weight: bold;
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-controller-service-dialog.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-controller-service-dialog.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-controller-service-dialog.css
new file mode 100644
index 0000000..ff8d19e
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-controller-service-dialog.css
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+/* 
+    New controller service dialog.
+*/
+
+#new-controller-service-dialog {
+    z-index: 1301;
+    display: none;
+    width: 800px;
+    height: 450px;
+    line-height: normal;
+}
+
+#controller-service-tag-cloud-container {
+    float: left;
+    width: 190px;
+    height: 350px;
+}
+
+#controller-service-types-container {
+    float: left;
+    width: 588px;
+    height: 350px;
+}
+
+#controller-service-description-container {
+    width: 588px;
+    height: 100px;
+    margin-top: 10px;
+}
+
+#controller-service-type-name {
+    font-size: 12pt;
+    font-weight: bold;
+    color: #527991;
+    margin-bottom: 8px;
+    width: 588px;
+}
+
+#controller-service-type-description {
+    width: 588px;
+    height: 60px;
+}
+
+#controller-service-availability-container {
+    position: relative;
+    left: 190px;
+    top: 9px;
+}
+
+div.availability-label {
+    float: left;
+    height: 24px;
+    line-height: 24px;
+}
+
+#controller-service-availability-combo {
+    float: left;
+    margin-left: 6px;
+    width: 125px;
+    height: 18px;
+    line-height: 18px;
+}
+
+#controller-service-types-table {
+    width: 586px;
+    height: 249px;
+    border: 1px solid #666;
+}
+
+#controller-service-types-table-container th {
+    vertical-align: middle;
+}
+
+/*
+    Processor tag cloud
+*/
+
+#controller-service-tag-cloud ul.tag-cloud {
+    max-height: 257px;
+}
+
+#controller-service-tag-cloud ul.tag-cloud li {
+    max-width: 180px;
+}
+
+#controller-service-tag-cloud div.tag-cloud-separator {
+    width: 180px;
+}
+
+#controller-service-tag-cloud ul.tag-filter li {
+    width: 180px;
+}
+
+#controller-service-tag-cloud div.selected-tag-text {
+    width: 160px;
+}
+
+/*
+    Processor type table filter
+*/
+
+#controller-service-type-filter-controls {
+    float: right;
+    margin-top: -35px;
+    margin-right: 2px;
+    margin-bottom: 7px;
+}
+
+#controller-service-type-filter-status {
+    font-size: 9px;
+    font-weight: bold;
+    color: #9f6000;
+    clear: left; 
+    line-height: normal;
+    margin-left: 5px;
+}
+
+#controller-service-type-filter {
+    padding: 3px 0px 1px 3px;
+    font-size: 12px;
+    height: 18px;
+    line-height: 20px;
+    width: 173px;
+    border: 1px solid #ccc;
+    margin-right: 3px;
+    float: left;
+}
+
+#controller-service-type-filter-options {
+    float: left;
+    height: 17px;
+    line-height: 17px;
+    width: 85px;
+    margin-top: 1px;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-processor-dialog.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-processor-dialog.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-processor-dialog.css
index 5456dea..d5871ac 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-processor-dialog.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-processor-dialog.css
@@ -23,11 +23,10 @@
     display: none;
     width: 800px;
     height: 450px;
-    border: 1px solid #eee;
     line-height: normal;
 }
 
-#tag-cloud-container {
+#processor-tag-cloud-container {
     float: left;
     width: 190px;
     height: 350px;
@@ -69,56 +68,29 @@
 }
 
 /*
-    Tag cloud
+    Processor tag cloud
 */
 
-#tag-cloud {
-    font-size: 90%;
-    padding: 2px;
-    margin-left: -5px;
+#processor-tag-cloud ul.tag-cloud {
     max-height: 257px;
-    overflow: hidden;
 }
 
-#tag-cloud li {
-    float: left;
-    list-style-type: none;
-    margin: 0 3px;
-    height: 20px;
-    line-height: 20px;
+#processor-tag-cloud ul.tag-cloud li {
     max-width: 180px;
-    overflow: hidden;
-    white-space: nowrap;
 }
 
-#tag-cloud-separator {
+#processor-tag-cloud div.tag-cloud-separator {
     width: 180px;
-    height: 1px;
-    border-bottom: 1px solid #aaa;
-    margin: 3px 0;
-}
-
-#tag-filter {
-    overflow: hidden;
 }
 
-#tag-filter li {
-    width: 185px;
-    height: 18px;
-    line-height: 18px;
-    padding: 2px;
+#processor-tag-cloud ul.tag-filter li {
+    width: 180px;
 }
 
-div.selected-tag-text {
-    float: left;
+#processor-tag-cloud div.selected-tag-text {
     width: 160px;
 }
 
-img.remove-selected-tag {
-    float: left;
-    margin-top: 1px;
-}
-
 /*
     Processor type table filter
 */
@@ -150,19 +122,10 @@ img.remove-selected-tag {
     float: left;
 }
 
-input.filter-list {
-    color: #888;
-}
-
 #processor-type-filter-options {
     float: left;
     height: 17px;
     line-height: 17px;
     width: 85px;
     margin-top: 1px;
-}
-
-.no-select {
-    -webkit-user-select: none;
-    -moz-user-select: none;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-reporting-task-dialog.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-reporting-task-dialog.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-reporting-task-dialog.css
new file mode 100644
index 0000000..24b4380
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-reporting-task-dialog.css
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+/* 
+    New controller service dialog.
+*/
+
+#new-reporting-task-dialog {
+    z-index: 1301;
+    display: none;
+    width: 800px;
+    height: 450px;
+    line-height: normal;
+}
+
+#reporting-task-tag-cloud-container {
+    float: left;
+    width: 190px;
+    height: 350px;
+}
+
+#reporting-task-types-container {
+    float: left;
+    width: 588px;
+    height: 350px;
+}
+
+#reporting-task-description-container {
+    width: 588px;
+    height: 100px;
+    margin-top: 10px;
+}
+
+#reporting-task-type-name {
+    font-size: 12pt;
+    font-weight: bold;
+    color: #527991;
+    margin-bottom: 8px;
+    width: 588px;
+}
+
+#reporting-task-type-description {
+    width: 588px;
+    height: 60px;
+}
+
+#reporting-task-availability-container {
+    position: relative;
+    left: 190px;
+    top: 9px;
+}
+
+div.availability-label {
+    float: left;
+    height: 24px;
+    line-height: 24px;
+}
+
+#reporting-task-availability-combo {
+    float: left;
+    margin-left: 6px;
+    width: 125px;
+    height: 18px;
+    line-height: 18px;
+}
+
+#reporting-task-types-table {
+    width: 586px;
+    height: 249px;
+    border: 1px solid #666;
+}
+
+#reporting-task-types-table-container th {
+    vertical-align: middle;
+}
+
+/*
+    Processor tag cloud
+*/
+
+#reporting-task-tag-cloud ul.tag-cloud {
+    max-height: 257px;
+}
+
+#reporting-task-tag-cloud ul.tag-cloud li {
+    max-width: 180px;
+}
+
+#reporting-task-tag-cloud div.tag-cloud-separator {
+    width: 180px;
+}
+
+#reporting-task-tag-cloud ul.tag-filter li {
+    width: 180px;
+}
+
+#reporting-task-tag-cloud div.selected-tag-text {
+    width: 160px;
+}
+
+/*
+    Processor type table filter
+*/
+
+#reporting-task-type-filter-controls {
+    float: right;
+    margin-top: -35px;
+    margin-right: 2px;
+    margin-bottom: 7px;
+}
+
+#reporting-task-type-filter-status {
+    font-size: 9px;
+    font-weight: bold;
+    color: #9f6000;
+    clear: left; 
+    line-height: normal;
+    margin-left: 5px;
+}
+
+#reporting-task-type-filter {
+    padding: 3px 0px 1px 3px;
+    font-size: 12px;
+    height: 18px;
+    line-height: 20px;
+    width: 173px;
+    border: 1px solid #ccc;
+    margin-right: 3px;
+    float: left;
+}
+
+#reporting-task-type-filter-options {
+    float: left;
+    height: 17px;
+    line-height: 17px;
+    width: 85px;
+    margin-top: 1px;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/port-configuration.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/port-configuration.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/port-configuration.css
index b8fc87c..ce1f00a 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/port-configuration.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/port-configuration.css
@@ -23,7 +23,6 @@
     display: none;
     width: 400px;
     height: 375px;
-    border: 1px solid #eee;
 }
 
 #port-name {
@@ -66,7 +65,6 @@ div.port-enabled-container {
     display: none;
     width: 400px;
     height: 450px;
-    border: 1px solid #eee;
 }
 
 #secure-port-configuration div.dialog-content {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/port-details.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/port-details.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/port-details.css
index 2cd8505..831c5ee 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/port-details.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/port-details.css
@@ -23,7 +23,6 @@
     display: none;
     width: 400px;
     height: 300px;
-    border: 1px solid #eee;
 }
 
 /*
@@ -35,7 +34,6 @@
     display: none;
     height: 425px;
     width: 400px;
-    border: 1px solid #eee;
 }
 
 #secure-port-details-tabs {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/process-group-configuration.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/process-group-configuration.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/process-group-configuration.css
index cfa3ce4..f0d781d 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/process-group-configuration.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/process-group-configuration.css
@@ -19,7 +19,6 @@
     display: none;
     width: 400px;
     height: 300px;
-    border: 1px solid #eee;
 }
 
 #process-group-configuration div.dialog-content {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/process-group-details.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/process-group-details.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/process-group-details.css
index ada6af9..af7c382 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/process-group-details.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/process-group-details.css
@@ -19,7 +19,6 @@
     display: none;
     width: 400px;
     height: 270px;
-    border: 1px solid #eee;
 }
 
 #process-group-details div.dialog-content {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-configuration.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-configuration.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-configuration.css
index add52e0..1dc52f3 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-configuration.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-configuration.css
@@ -26,7 +26,6 @@
     font-size: 10px;
     z-index: 1201;
     display: none;
-    border: 1px solid #eee;
 }
 
 div.processor-configuration-tab-container {
@@ -225,158 +224,6 @@ div.relationship-description {
     float: right;
 }
 
-/* properties */
-
-#processor-properties-header {
-    margin-bottom: 10px;
-    height: 19px;
-}
-
-#add-property {
-    float: right;
-    margin-right: 20px;
-}
-
-#processor-property-dialog .nfel-editor {
-    margin-bottom: 35px;
-}
-
-#new-property-name-container {
-    position: absolute;
-    height: 25px;
-    left: 10px;
-    right: 10px;
-    padding-right: 10px;
-    overflow: hidden; 
-    margin-bottom: 10px;
-}
-
-#new-property-name {
-    height: 14px;
-    width: 100%;
-}
-
-#new-property-button-container {
-    position: absolute;
-    left: 0;
-    bottom: 0;
-    right: 0;
-    padding: 0 8px 10px;
-}
-
-#add-property-icon {
-    width: 19px;
-    height: 19px;
-    cursor: pointer;
-    float: left;
-}
-
-div.add-icon-bg {
-    background: transparent url(../images/buttonNewProperty.png) no-repeat scroll top left;
-}
-
-div.add-icon-bg-hover {
-    background: transparent url(../images/buttonNewProperty.png) no-repeat scroll top right;
-}
-
-#add-text {
-    float: left;
-    margin-left: 5px;
-    height: 19px;
-    line-height: 19px;
-}
-
-#required-property-note {
-    font-weight: bold;
-    float: left;
-    height: 19px;
-    line-height: 19px;
-    margin-left: 6px;
-}
-
-#processor-properties {
-    width: 758px;
-    height: 290px;
-    border-bottom: 1px solid #666;
-    background-color: #fff;
-}
-
-#configuration-buttons {
-    text-align: center;
-    position: absolute;
-    right: 10px;
-    bottom: 10px;
-}
-
-/*
-    Styles for the processor property editor.
-*/
-
-div.slickgrid-nfel-editor .nfel-editor {
-    margin-bottom: 30px;
-}
-
-div.edit-property-value {
-    height: 20px;
-    padding-right: 60px;
-}
-
-textarea.value-field {
-    float: left;
-    width: 95%;
-    height: 26px;
-    font-size: 11px !important;
-    resize: vertical;
-    overflow-y: auto;
-}
-
-div.string-check-container {
-    float: left;
-    margin-top: 1px;
-    height: 20px;
-    line-height: 20px;
-}
-
-div.string-check {
-    width: 12px;
-    height: 12px;
-    float: left;
-    margin-top: 4px;
-    margin-left: 4px;
-}
-
-span.string-check-label {
-    font-size: 9px;
-    font-weight: normal;
-}
-
-div.value pre {
-    font-family: Verdana, Arial, Helvetica, sans-serif;
-    overflow: hidden;
-}
-
-/*
-    Styles for the property value combo
-*/
-
-div.edit-property-value-combo {
-    height: 22px;
-}
-
-div.value-combo {
-    height: 18px;
-    margin-top: 0px;
-    float: left;
-}
-
-/*
-    Table styles
-*/
-
-td.right-border {
-    border-right: 1px solid #aaa;
-}
-
 /*
     Comments
 */

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-details.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-details.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-details.css
index ef770cb..c259a30 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-details.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/processor-details.css
@@ -26,7 +26,6 @@
     font-size: 10px;
     z-index: 1301;
     display: none;
-    border: 1px solid #eee;
 }
 
 div.processor-details-tab-container {
@@ -90,30 +89,4 @@ div.concurrently-schedulable-tasks-setting, div.scheduling-period-setting, div.p
 
 div.scheduling-period-setting, div.yield-duration-setting {
     margin-left: 36px;
-}
-
-/* properties */
-
-#read-only-processor-properties-header {
-    margin-bottom: 10px;
-    height: 19px;
-}
-
-#read-only-required-property-note {
-    font-weight: bold;
-    float: left;
-    height: 19px;
-    line-height: 19px;
-    margin-left: 6px;
-}
-
-#read-only-processor-properties {
-    border-bottom: 1px solid #666;
-    width: 758px;
-    height: 290px;
-    background-color: #fff;
-}
-
-div.processor-property-detail .nfel-editor {
-    margin-bottom: 30px;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/registration.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/registration.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/registration.css
index d81596e..d4fdc7e 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/registration.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/registration.css
@@ -42,12 +42,4 @@
 #registration-justification {
     width: 600px;
     height: 200px;
-}
-
-div.registration-collapsed {
-    background: transparent url(../images/iconTwistArrow.png) no-repeat scroll top left;
-}
-
-div.registration-expanded {
-    background: transparent url(../images/iconTwistArrow.png) no-repeat scroll top right;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/remote-process-group-configuration.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/remote-process-group-configuration.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/remote-process-group-configuration.css
index fdbe745..ead5714 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/remote-process-group-configuration.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/remote-process-group-configuration.css
@@ -26,7 +26,6 @@
     font-size: 10px;
     z-index: 1201;
     display: none;
-    border: 1px solid #eee;
 }
 
 #remote-process-group-details {
@@ -37,7 +36,6 @@
     font-size: 10px;
     z-index: 1201;
     display: none;
-    border: 1px solid #eee;
 }
 
 #remote-process-group-ports {
@@ -48,7 +46,6 @@
     font-size: 10px;
     z-index: 1201;
     display: none;
-    border: 1px solid #eee;
 }
 
 /* remote process group settings */

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/reporting-task.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/reporting-task.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/reporting-task.css
new file mode 100644
index 0000000..8d5291e
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/reporting-task.css
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+/*
+    Reporting task configuration styles.
+*/
+
+#reporting-task-configuration {
+    position: absolute;
+    overflow: hidden;
+    width: 800px;
+    height: 450px;
+    font-size: 10px;
+    z-index: 1301;
+    display: none;
+}
+
+div.reporting-task-configuration-tab-container {
+    margin-top: -10px;
+    padding: 5px 11px;
+}
+
+#reporting-task-configuration-advanced {
+    display: none;
+}
+
+#reporting-task-configuration-tabs {
+    background-color: transparent;
+    width: 778px;
+    height: 21px;
+    border-bottom: 3px solid #666;
+}
+
+#reporting-task-configuration div.configuration-tab {
+    height: 320px;
+    overflow: auto;
+    padding: 10px;
+    background: #eee url(../images/bgTabContainer.png) repeat-x;
+    display: none;
+}
+
+/* reporting-task settings */
+
+#reporting-task-configuration div.settings-left {
+    float: left;
+    width: 356px;
+}
+
+#reporting-task-configuration div.settings-right {
+    float: left;
+    width: 356px;
+}
+
+#reporting-task-configuration div.spacer {
+    float: left;
+    margin-right: 40px;
+}
+
+#reporting-task-name {
+    font-size: 11px !important;
+    width: 250px;
+    float: left;
+}
+
+#reporting-task-enabled {
+    width: 12px;
+    height: 12px;
+    float: left;
+    margin-right: 4px;
+}
+
+div.reporting-task-enabled-container {
+    float: left;
+    margin-top: 5px;
+    margin-left: 10px;
+}
+
+div.availability-setting {
+    float: left;
+    width: 140px;
+}
+
+#reporting-task-scheduling-strategy-combo {
+    width: 150px;
+    height: 18px;
+    line-height: 18px;
+}
+
+input.reporting-task-scheduling-period {
+    font-size: 11px !important;
+    width: 150px;
+}
+
+/*
+    Comments
+*/
+
+#reporting-task-comments {
+    height: 250px;
+    width: 748px !important;
+    margin-top: 10px;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/settings.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/settings.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/settings.css
index e9d78d4..91805e1 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/settings.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/settings.css
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #settings {
     position: absolute;
     top: 0px;
@@ -21,23 +22,146 @@
     bottom: 0px;
     left: 0px;
     display: none;
+    padding: 20px;
 }
 
 #settings-header-text {
     height: 35px;
-    margin-top: 20px;
-    margin-left: 20px;
     font-size: 16px;
     font-weight: bold;
 }
 
 #settings-container {
-    margin-top: -10px;
+    margin-top: 18px;
+}
+
+#new-service-or-task {
+    float: right;
+    width: 19px;
+    height: 19px;
+    margin-top: 4px;
+    cursor: pointer;
+}
+
+span.expansion-button {
+    width: 10px;
+    height: 10px;
+    float: left;
+}
+
+span.ancestor-type {
+    font-weight: bold;
+}
+
+span.ancestor-type-rollup {
+    margin-left: 3px;
+    color: #aaa;
+}
+
+/* settings tabs */
+
+#settings-tabs-container {
+    border-bottom: 2px solid #666;
+}
+
+#settings-tabs {
+    float: left;
+}
+
+.settings-tab {
+    display: block;
+    padding: 0 5px;
+    height: 26px;
+    float: left;
+    color: #666;
+    background-color: #ccc;
+    border-left: 1px solid #b5b5b5;
+    border-top: 1px solid #b5b5b5;
+    border-right: 1px solid #b5b5b5;
+    margin-right: 5px;
+    text-align: center;
+    cursor: pointer;
+    line-height: 26px;
+    font-weight: bold;
+}
+
+.settings-selected-tab {
+    color: #fff;
+    background-color: #666;
+    border-left: 1px solid #666;
+    border-top: 1px solid #666;
+    border-right: 1px solid #666;
+}
+
+#settings-tab-background {
+    height: 200px;
+    margin-top: 1px;
+    background-color: transparent;
+    background: linear-gradient(to bottom, #dddddd, #ffffff);
+    filter: progid:DXImageTransform.Microsoft.gradient(gradientType=0, startColorstr=#dddddd, endColorstr=#ffffff);
 }
 
+#settings div.configuration-tab {
+    display: none;
+}
+
+div.settings-table {
+    position: absolute;
+    top: 115px;
+    left: 25px;
+    bottom: 20px;
+    right: 25px;
+    border: 1px solid #666;
+    overflow: hidden;
+    background-color: #fff;
+}
+
+span.sorted {
+    text-decoration: underline;
+}
+
+#settings-refresh-button {
+    height: 24px;
+    width: 26px;
+    float: left;
+}
+
+#settings-last-refreshed-container {
+    float: left;
+    color: #666;
+    font-weight: normal;
+    margin-top: 6px;
+    margin-left: 3px;
+    -webkit-user-select: none;
+    -moz-user-select: none;
+}
+
+#settings-loading-container {
+    float: left;
+    width: 16px;
+    height: 16px;
+    background-color: transparent;
+    margin-top: 4px;
+    margin-left: 3px;
+}
+
+#settings-last-refreshed {
+    font-weight: bold;
+}
+
+#settings-refresh-required-icon {
+    float: left;
+    margin-top: 4px;
+    margin-left: 3px;
+    width: 18px;
+    height: 16px;
+    background-image: url(../images/iconAlert.png);
+}
+
+/* general */
+
 #general-settings {
-    padding-left: 10px;
-    margin-top: 15px;
+    margin-top: -190px;
     margin-left: 10px;
 }
 
@@ -53,11 +177,11 @@
 }
 
 #settings-buttons {
-    margin-left: 260px;
+    margin-left: 304px;
     margin-top: 10px;
 }
 
-#settings-save, #settings-cancel {
+#settings-save {
     float: left;
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/shell.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/shell.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/shell.css
index 48e3d66..2cc8cfc 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/shell.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/shell.css
@@ -20,7 +20,7 @@
 
 #shell-dialog {
     display: none;
-    z-index: 1250;
+    z-index: 1201;
     position: absolute;
     top: 20px;
     left: 20px;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/status-history.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/status-history.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/status-history.css
index 0590ec8..e6d477c 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/status-history.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/status-history.css
@@ -19,7 +19,6 @@
 #status-history-dialog {
     display: none;
     z-index: 1301;
-    border: 1px solid #eee;
     padding: 10px;
     border: 3px solid #365C6A;
     box-shadow: 4px 4px 6px rgba(0, 0, 0, 0.9);

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/buttonNewProperty.png
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/buttonNewProperty.png b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/buttonNewProperty.png
deleted file mode 100755
index 3e51a15..0000000
Binary files a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/buttonNewProperty.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/iconEnable.png
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/iconEnable.png b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/iconEnable.png
new file mode 100644
index 0000000..154403f
Binary files /dev/null and b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/iconEnable.png differ

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/iconUndo.png
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/iconUndo.png b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/iconUndo.png
deleted file mode 100755
index f48895d..0000000
Binary files a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/iconUndo.png and /dev/null differ

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/jquery.each.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/jquery.each.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/jquery.each.js
index dd83159..90f76b8 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/jquery.each.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/jquery.each.js
@@ -27,5 +27,5 @@
             return origEach.call(this, object, callback, args);
         }
         return object;
-    }
+    };
 })(jQuery);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/modal/jquery.modal.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/modal/jquery.modal.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/modal/jquery.modal.css
index 555698d..f834275 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/modal/jquery.modal.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/modal/jquery.modal.css
@@ -24,11 +24,13 @@
     background-color: #eee;
 }
 
-.dialog-border {
-    background-color: #294c58;
-    z-index: 1300;
-    opacity: 0.65;
-    filter: alpha(opacity=65);
+.show-border {
+    border: 3px solid #365C6A;
+    box-shadow: 4px 4px 6px rgba(0, 0, 0, 0.9);
+}
+
+.overlay {
+    border: 1px solid #eee;
 }
 
 .dialog-header {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/modal/jquery.modal.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/modal/jquery.modal.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/modal/jquery.modal.js
index 9091db8..9faaefd 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/modal/jquery.modal.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/modal/jquery.modal.js
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 /**
  * Create a new dialog. The options are specified in the following
  * format:
@@ -36,11 +37,14 @@
  *      close: closeHandler
  *   }
  * }
+ * 
+ * The content of the dialog should be in a element with the class dialog-content
+ * directly under the dialog.
+ * 
+ * @argument {jQuery} $
  */
 (function ($) {
 
-    var overlayBackground = 'overlay-background';
-
     var isUndefined = function (obj) {
         return typeof obj === 'undefined';
     };
@@ -73,20 +77,12 @@
         }
     };
 
-    // private function for adding the border
-    var addBorder = function (dialog) {
-        var dialogParent = dialog.parent();
-
-        // detach the dialog
-        dialog.detach();
-
-        // create the wrapper
-        $('<div/>').append('<div class="dialog-border"></div>').append(dialog).appendTo(dialogParent);
-    };
-
     var methods = {
+        
         /**
          * Initializes the dialog.
+         * 
+         * @argument {object} options The options for the plugin
          */
         init: function (options) {
             return this.each(function () {
@@ -110,8 +106,10 @@
                     dialog.prepend(dialogHeader);
 
                     // determine whether a dialog border is necessary
-                    if (options.overlayBackground === false) {
-                        addBorder(dialog);
+                    if (options.overlayBackground === true) {
+                        dialog.addClass('overlay');
+                    } else {
+                        dialog.addClass('show-border');
                     }
 
                     // add the buttons
@@ -119,44 +117,44 @@
                 }
             });
         },
+        
         /**
          * Sets the handler that is used when the dialog is closed.
+         * 
+         * @argument {function} handler The function to call when hiding the dialog
          */
         setHandler: function (handler) {
             return this.each(function () {
                 $(this).data('handler', handler);
             });
         },
+        
         /**
          * Sets whether to overlay the background or if a border should be shown around the dialog.
+         * 
+         * @argument {boolean} overlayBackground Whether or not to overlay the background
          */
         setOverlayBackground: function (overlayBackground) {
             return this.each(function () {
                 if (isDefinedAndNotNull(overlayBackground)) {
                     var dialog = $(this);
-                    var prev = dialog.prev();
 
                     // if we should overlay the background
                     if (overlayBackground === true) {
-                        // if we're currently configured for a border
-                        if (prev.hasClass('dialog-border')) {
-                            // detach the dialog
-                            dialog.detach();
-
-                            // reinsert appropriately
-                            prev.parent().replaceWith(dialog);
-                        }
+                        dialog.addClass('overlay');
+                        dialog.removeClass('show-border');
                     } else {
-                        // if we're currently configured to overlay the background
-                        if (!prev.hasClass('dialog-border')) {
-                            addBorder(dialog);
-                        }
+                        dialog.addClass('show-border');
+                        dialog.removeClass('overlay');
                     }
                 }
             });
         },
+        
         /**
          * Updates the button model for the selected dialog.
+         * 
+         * @argument {array} buttons The new button model
          */
         setButtonModel: function (buttons) {
             return this.each(function () {
@@ -171,14 +169,18 @@
                 }
             });
         },
+        
         /**
          * Sets the header text of the dialog.
+         * 
+         * @argument {string} text Text to use a as a header
          */
         setHeaderText: function (text) {
             return this.each(function () {
                 $(this).find('span.dialog-header-text').text(text);
             });
         },
+        
         /**
          * Shows the dialog.
          */
@@ -187,33 +189,19 @@
                 // show the dialog
                 var dialog = $(this);
                 if (!dialog.is(':visible')) {
-                    // position the background/border
-                    var background;
-                    var prev = dialog.prev();
-                    if (!prev.hasClass('dialog-border')) {
-                        // add a marker style so we know how to remove the style when hiding the dialog
-                        dialog.addClass(overlayBackground);
-
-                        // get the faded background
-                        background = $('#faded-background');
+                    var title = dialog.find('span.dialog-header-text').text();
+                    if (isBlank(title)) {
+                        dialog.find('.dialog-content').css('margin-top', '-20px');
                     } else {
-                        // get the dialog border
-                        background = prev;
-
-                        // get the width and the height of the dialog container
-                        var width = parseInt(dialog.css('width'));
-                        var height = parseInt(dialog.css('height'));
-
-                        // set the width and height and center the dialog border
-                        background.css({
-                            'width': (width + 20) + 'px',
-                            'height': (height + 20) + 'px'
-                        }).center();
-
-                        // show the glass pane
+                        dialog.find('.dialog-content').css('margin-top', '0');
+                    }
+                    
+                    // show the appropriate background
+                    if (dialog.hasClass('show-border')) {
                         $('#glass-pane').show();
+                    } else {
+                        $('#faded-background').show();
                     }
-                    background.show();
 
                     // show the centered dialog - performing these operation in the
                     // opposite order one might expect. for some reason, the call to
@@ -223,6 +211,7 @@
                 }
             });
         },
+        
         /**
          * Hides the dialog.
          */
@@ -230,27 +219,21 @@
             return this.each(function () {
                 var dialog = $(this);
                 if (dialog.is(':visible')) {
-                    // determine how to hide the dialog
-                    if (dialog.hasClass(overlayBackground)) {
-                        dialog.removeClass(overlayBackground);
-
-                        // hide the faded background
-                        $('#faded-background').hide();
-                    } else {
-                        // hide the dialog border
-                        dialog.prev().hide();
-
-                        // hide the glass pane
+                    // hide the appropriate backgroun
+                    if (dialog.hasClass('show-border')) {
                         $('#glass-pane').hide();
+                    } else {
+                        $('#faded-background').hide();
                     }
 
+                    // hide the dialog
+                    dialog.hide();
+                    
+                    // invoke the handler
                     var handler = dialog.data('handler');
                     if (isDefinedAndNotNull(handler) && typeof handler.close === 'function') {
                         handler.close.call(dialog);
                     }
-
-                    // hide the dialog
-                    dialog.hide();
                 }
             });
         }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/jquery.nfeditor.js
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/jquery.nfeditor.js b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/jquery.nfeditor.js
index 84dd454..550ed37 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/jquery.nfeditor.js
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/nfeditor/jquery.nfeditor.js
@@ -14,6 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/* global CodeMirror, nf */
+
 /**
  * Create a new nf editor. The options are specified in the following
  * format:
@@ -30,7 +33,7 @@
  *   minHeight: 150
  * }
  * 
- * This editor requires
+ * @param {type} $
  */
 (function ($) {
 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/buttonNewProperty.png
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/buttonNewProperty.png b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/buttonNewProperty.png
new file mode 100755
index 0000000..3e51a15
Binary files /dev/null and b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/buttonNewProperty.png differ

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.css
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.css
new file mode 100644
index 0000000..74b6f4c
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/propertytable/jquery.propertytable.css
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+/*
+    Styles for the property table.
+*/
+
+div.properties-header {
+    margin-bottom: 10px;
+    height: 19px;
+}
+
+div.required-property-note {
+    font-weight: bold;
+    float: left;
+    height: 19px;
+    line-height: 19px;
+    margin-left: 6px;
+}
+
+div.add-property {
+    float: right;
+    margin-right: 20px;
+}
+
+div.add-property-icon {
+    width: 19px;
+    height: 19px;
+    cursor: pointer;
+    float: left;
+}
+
+div.add-icon-bg {
+    background: transparent url(buttonNewProperty.png) no-repeat scroll top left;
+}
+
+div.add-icon-bg-hover {
+    background: transparent url(buttonNewProperty.png) no-repeat scroll top right;
+}
+
+div.add-property-text {
+    float: left;
+    margin-left: 5px;
+    height: 19px;
+    line-height: 19px;
+}
+
+div.property-table {
+    width: 758px;
+    height: 290px;
+    border-bottom: 1px solid #666;
+    background-color: #fff;
+}
+
+/*
+    New property dialog
+*/
+
+div.new-property-dialog {
+    z-index: 1301;
+    display: none;
+    padding: 10px;
+    border: 3px solid #365C6A;
+    box-shadow: 4px 4px 6px rgba(0, 0, 0, 0.9);
+    cursor: move;
+    width: 300px;
+    height: 72px;
+}
+
+div.new-property-name-container {
+    position: absolute;
+    height: 25px;
+    left: 10px;
+    right: 10px;
+    padding-right: 10px;
+    overflow: hidden; 
+}
+
+input.new-property-name {
+    height: 14px;
+    width: 100%;
+}
+
+div.new-property-button-container {
+    position: absolute;
+    left: 0;
+    bottom: 0;
+    right: 0;
+    padding: 0 8px 10px;
+}
+
+/*
+    Styles for the property editor.
+*/
+
+div.slickgrid-nfel-editor .nfel-editor, div.property-detail .nfel-editor {
+    margin-bottom: 30px;
+}
+
+div.edit-property-value {
+    height: 20px;
+    padding-right: 60px;
+}
+
+textarea.value-field {
+    float: left;
+    width: 95%;
+    height: 26px;
+    font-size: 11px !important;
+    resize: vertical;
+    overflow-y: auto;
+}
+
+div.string-check-container {
+    float: left;
+    margin-top: 1px;
+    height: 20px;
+    line-height: 20px;
+}
+
+div.string-check {
+    width: 12px;
+    height: 12px;
+    float: left;
+    margin-top: 4px;
+    margin-left: 4px;
+}
+
+span.string-check-label {
+    font-size: 9px;
+    font-weight: normal;
+}
+
+div.value pre {
+    font-family: Verdana, Arial, Helvetica, sans-serif;
+    overflow: hidden;
+}
+
+/*
+    Styles for the property value combo
+*/
+
+div.edit-property-value-combo {
+    height: 22px;
+}
+
+div.value-combo {
+    height: 18px;
+    margin-top: 0px;
+    float: left;
+}
+
+/*
+    Table styles
+*/
+
+td.right-border {
+    border-right: 1px solid #aaa;
+}
\ No newline at end of file


[12/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
new file mode 100644
index 0000000..38ddc36
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
@@ -0,0 +1,663 @@
+/*
+ * 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.web.api;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import org.apache.nifi.cluster.manager.impl.WebClusterManager;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.ConfigurationSnapshot;
+import org.apache.nifi.web.NiFiServiceFacade;
+import org.apache.nifi.web.Revision;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.request.ClientIdParameter;
+import org.apache.nifi.web.api.request.LongParameter;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.ui.extension.UiExtension;
+import org.apache.nifi.ui.extension.UiExtensionMapping;
+import org.apache.nifi.web.UiExtensionType;
+import static org.apache.nifi.web.api.ApplicationResource.CLIENT_ID;
+import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
+import org.apache.nifi.web.api.dto.ReportingTaskDTO;
+import org.apache.nifi.web.api.entity.PropertyDescriptorEntity;
+import org.apache.nifi.web.api.entity.ReportingTaskEntity;
+import org.apache.nifi.web.api.entity.ReportingTasksEntity;
+import org.apache.nifi.web.util.Availability;
+import org.codehaus.enunciate.jaxrs.TypeHint;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.access.prepost.PreAuthorize;
+
+/**
+ * RESTful endpoint for managing a Reporting Task.
+ */
+public class ReportingTaskResource extends ApplicationResource {
+
+    private static final Logger logger = LoggerFactory.getLogger(ReportingTaskResource.class);
+
+    private NiFiServiceFacade serviceFacade;
+    private WebClusterManager clusterManager;
+    private NiFiProperties properties;
+    
+    @Context
+    private ServletContext servletContext;
+    
+    /**
+     * Populates the uri for the specified reporting task.
+     * 
+     * @param reportingTasks
+     * @return 
+     */
+    private Set<ReportingTaskDTO> populateRemainingReportingTasksContent(final String availability, final Set<ReportingTaskDTO> reportingTasks) {
+        for (ReportingTaskDTO reportingTask : reportingTasks) {
+            populateRemainingReportingTaskContent(availability, reportingTask);
+        }
+        return reportingTasks;
+    }
+    
+    /**
+     * Populates the uri for the specified reporting task.
+     */
+    private ReportingTaskDTO populateRemainingReportingTaskContent(final String availability, final ReportingTaskDTO reportingTask) {
+        // populate the reporting task href
+        reportingTask.setUri(generateResourceUri("controller", "reporting-tasks", availability, reportingTask.getId()));
+        reportingTask.setAvailability(availability);
+        
+        // see if this processor has any ui extensions
+        final UiExtensionMapping uiExtensionMapping = (UiExtensionMapping) servletContext.getAttribute("nifi-ui-extensions");
+        if (uiExtensionMapping.hasUiExtension(reportingTask.getType())) {
+            final List<UiExtension> uiExtensions = uiExtensionMapping.getUiExtension(reportingTask.getType());
+            for (final UiExtension uiExtension : uiExtensions) {
+                if (UiExtensionType.ReportingTaskConfiguration.equals(uiExtension.getExtensionType())) {
+                    reportingTask.setCustomUiUrl(uiExtension.getContextPath() + "/configure");
+                }
+            }
+        }
+        
+        return reportingTask;
+    }
+
+    /**
+     * Parses the availability and ensure that the specified availability makes sense for the
+     * given NiFi instance.
+     * 
+     * @param availability
+     * @return 
+     */
+    private Availability parseAvailability(final String availability) {
+        final Availability avail;
+        try {
+            avail = Availability.valueOf(availability.toUpperCase());
+        } catch (IllegalArgumentException iae) {
+            throw new IllegalArgumentException(String.format("Availability: Value must be one of [%s]", StringUtils.join(Availability.values(), ", ")));
+        }
+        
+        // ensure this nifi is an NCM is specifying NCM availability
+        if (!properties.isClusterManager() && Availability.NCM.equals(avail)) {
+            throw new IllegalArgumentException("Availability of NCM is only applicable when the NiFi instance is the cluster manager.");
+        }
+        
+        return avail;
+    }
+    
+    /**
+     * Retrieves all the of reporting tasks in this NiFi.
+     *
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @param availability Whether the reporting task is available on the NCM only (ncm) or on the 
+     * nodes only (node). If this instance is not clustered all tasks should use the node availability.
+     * @return A reportingTasksEntity.
+     */
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}")
+    @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
+    @TypeHint(ReportingTasksEntity.class)
+    public Response getReportingTasks(@QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, @PathParam("availability") String availability) {
+        final Availability avail = parseAvailability(availability);
+        
+        // replicate if cluster manager
+        if (properties.isClusterManager() && Availability.NODE.equals(avail)) {
+            return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
+        }
+
+        // get all the reporting tasks
+        final Set<ReportingTaskDTO> reportingTasks = populateRemainingReportingTasksContent(availability, serviceFacade.getReportingTasks());
+        
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+
+        // create the response entity
+        final ReportingTasksEntity entity = new ReportingTasksEntity();
+        entity.setRevision(revision);
+        entity.setReportingTasks(reportingTasks);
+
+        // generate the response
+        return clusterContext(generateOkResponse(entity)).build();
+    }
+
+    /**
+     * Creates a new reporting task.
+     *
+     * @param httpServletRequest
+     * @param version The revision is used to verify the client is working with
+     * the latest version of the flow.
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @param availability Whether the reporting task is available on the NCM only (ncm) or on the 
+     * nodes only (node). If this instance is not clustered all tasks should use the node availability.
+     * @param type The type of reporting task to create.
+     * @return A reportingTaskEntity.
+     */
+    @POST
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}")
+    @PreAuthorize("hasRole('ROLE_DFM')")
+    @TypeHint(ReportingTaskEntity.class)
+    public Response createReportingTask(
+            @Context HttpServletRequest httpServletRequest,
+            @FormParam(VERSION) LongParameter version,
+            @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+            @PathParam("availability") String availability, 
+            @FormParam("type") String type) {
+        
+        // create the reporting task DTO
+        final ReportingTaskDTO reportingTaskDTO = new ReportingTaskDTO();
+        reportingTaskDTO.setType(type);
+
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+        if (version != null) {
+            revision.setVersion(version.getLong());
+        }
+
+        // create the reporting task entity
+        final ReportingTaskEntity reportingTaskEntity = new ReportingTaskEntity();
+        reportingTaskEntity.setRevision(revision);
+        reportingTaskEntity.setReportingTask(reportingTaskDTO);
+
+        return createReportingTask(httpServletRequest, availability, reportingTaskEntity);
+    }
+
+    /**
+     * Creates a new Reporting Task.
+     *
+     * @param httpServletRequest
+     * @param availability Whether the reporting task is available on the NCM only (ncm) or on the 
+     * nodes only (node). If this instance is not clustered all tasks should use the node availability.
+     * @param reportingTaskEntity A reportingTaskEntity.
+     * @return A reportingTaskEntity.
+     */
+    @POST
+    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}")
+    @PreAuthorize("hasRole('ROLE_DFM')")
+    @TypeHint(ReportingTaskEntity.class)
+    public Response createReportingTask(
+            @Context HttpServletRequest httpServletRequest,
+            @PathParam("availability") String availability, 
+            ReportingTaskEntity reportingTaskEntity) {
+        
+        final Availability avail = parseAvailability(availability);
+
+        if (reportingTaskEntity == null || reportingTaskEntity.getReportingTask()== null) {
+            throw new IllegalArgumentException("Reporting task details must be specified.");
+        }
+
+        if (reportingTaskEntity.getRevision() == null) {
+            throw new IllegalArgumentException("Revision must be specified.");
+        }
+        
+        if (reportingTaskEntity.getReportingTask().getId() != null) {
+            throw new IllegalArgumentException("Reporting task ID cannot be specified.");
+        }
+        
+        if (StringUtils.isBlank(reportingTaskEntity.getReportingTask().getType())) {
+            throw new IllegalArgumentException("The type of reporting task to create must be specified.");
+        }
+        
+        // get the revision
+        final RevisionDTO revision = reportingTaskEntity.getRevision();
+
+        // if cluster manager, convert POST to PUT (to maintain same ID across nodes) and replicate
+        if (properties.isClusterManager() && Availability.NODE.equals(avail)) {
+            // create ID for resource
+            final String id = UUID.randomUUID().toString();
+
+            // set ID for resource
+            reportingTaskEntity.getReportingTask().setId(id);
+
+            // convert POST request to PUT request to force entity ID to be the same across nodes
+            URI putUri = null;
+            try {
+                putUri = new URI(getAbsolutePath().toString() + "/" + id);
+            } catch (final URISyntaxException e) {
+                throw new WebApplicationException(e);
+            }
+
+            // change content type to JSON for serializing entity
+            final Map<String, String> headersToOverride = new HashMap<>();
+            headersToOverride.put("content-type", MediaType.APPLICATION_JSON);
+
+            // replicate put request
+            return (Response) clusterManager.applyRequest(HttpMethod.PUT, putUri, updateClientId(reportingTaskEntity), getHeaders(headersToOverride)).getResponse();
+        }
+
+        // handle expects request (usually from the cluster manager)
+        final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER);
+        if (expects != null) {
+            return generateContinueResponse().build();
+        }
+
+        // create the reporting task and generate the json
+        final ConfigurationSnapshot<ReportingTaskDTO> controllerResponse = serviceFacade.createReportingTask(
+                new Revision(revision.getVersion(), revision.getClientId()), reportingTaskEntity.getReportingTask());
+        final ReportingTaskDTO reportingTask = controllerResponse.getConfiguration();
+
+        // get the updated revision
+        final RevisionDTO updatedRevision = new RevisionDTO();
+        updatedRevision.setClientId(revision.getClientId());
+        updatedRevision.setVersion(controllerResponse.getVersion());
+
+        // build the response entity
+        final ReportingTaskEntity entity = new ReportingTaskEntity();
+        entity.setRevision(updatedRevision);
+        entity.setReportingTask(populateRemainingReportingTaskContent(availability, reportingTask));
+
+        // build the response
+        return clusterContext(generateCreatedResponse(URI.create(reportingTask.getUri()), entity)).build();
+    }
+
+    /**
+     * Retrieves the specified reporting task.
+     *
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @param availability Whether the reporting task is available on the NCM only (ncm) or on the 
+     * nodes only (node). If this instance is not clustered all tasks should use the node availability.
+     * @param id The id of the reporting task to retrieve
+     * @return A reportingTaskEntity.
+     */
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}/{id}")
+    @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
+    @TypeHint(ReportingTaskEntity.class)
+    public Response getReportingTask(@QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, 
+            @PathParam("availability") String availability, @PathParam("id") String id) {
+
+        final Availability avail = parseAvailability(availability);
+        
+        // replicate if cluster manager
+        if (properties.isClusterManager() && Availability.NODE.equals(avail)) {
+            return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
+        }
+
+        // get the reporting task
+        final ReportingTaskDTO reportingTask = serviceFacade.getReportingTask(id);
+
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+
+        // create the response entity
+        final ReportingTaskEntity entity = new ReportingTaskEntity();
+        entity.setRevision(revision);
+        entity.setReportingTask(populateRemainingReportingTaskContent(availability, reportingTask));
+
+        return clusterContext(generateOkResponse(entity)).build();
+    }
+    
+    /**
+     * Returns the descriptor for the specified property.
+     * 
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @param availability
+     * @param id The id of the reporting task.
+     * @param propertyName The property
+     * @return a propertyDescriptorEntity
+     */
+    @GET
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}/{id}/descriptors")
+    @PreAuthorize("hasAnyRole('ROLE_MONITOR', 'ROLE_DFM', 'ROLE_ADMIN')")
+    @TypeHint(PropertyDescriptorEntity.class)
+    public Response getPropertyDescriptor(
+            @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId, 
+            @PathParam("availability") String availability, @PathParam("id") String id, 
+            @QueryParam("propertyName") String propertyName) {
+        
+        final Availability avail = parseAvailability(availability);
+        
+        // ensure the property name is specified
+        if (propertyName == null) {
+            throw new IllegalArgumentException("The property name must be specified.");
+        }
+        
+        // replicate if cluster manager and task is on node
+        if (properties.isClusterManager() && Availability.NODE.equals(avail)) {
+            return clusterManager.applyRequest(HttpMethod.GET, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
+        }
+        
+        // get the property descriptor
+        final PropertyDescriptorDTO descriptor = serviceFacade.getReportingTaskPropertyDescriptor(id, propertyName);
+        
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+        
+        // generate the response entity
+        final PropertyDescriptorEntity entity = new PropertyDescriptorEntity();
+        entity.setRevision(revision);
+        entity.setPropertyDescriptor(descriptor);
+        
+        // generate the response
+        return clusterContext(generateOkResponse(entity)).build();
+    }
+    
+    /**
+     * Updates the specified reporting task.
+     *
+     * @param httpServletRequest
+     * @param version The revision is used to verify the client is working with
+     * the latest version of the flow.
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @param availability Whether the reporting task is available on the NCM only (ncm) or on the 
+     * nodes only (node). If this instance is not clustered all tasks should use the node availability.
+     * @param id The id of the reporting task to update.
+     * @param name The name of the reporting task
+     * @param annotationData The annotation data for the reporting task
+     * @param markedForDeletion Array of property names whose value should be removed.
+     * @param state The updated scheduled state
+     * @param schedulingStrategy The scheduling strategy for this reporting task
+     * @param schedulingPeriod The scheduling period for this reporting task
+     * @param comments The comments for this reporting task
+     * @param formParams Additionally, the processor properties and styles are
+     * specified in the form parameters. Because the property names and styles
+     * differ from processor to processor they are specified in a map-like
+     * fashion:
+     * <br>
+     * <ul>
+     * <li>properties[required.file.path]=/path/to/file</li>
+     * <li>properties[required.hostname]=localhost</li>
+     * <li>properties[required.port]=80</li>
+     * <li>properties[optional.file.path]=/path/to/file</li>
+     * <li>properties[optional.hostname]=localhost</li>
+     * <li>properties[optional.port]=80</li>
+     * <li>properties[user.defined.pattern]=^.*?s.*$</li>
+     * </ul>
+     * @return A reportingTaskEntity.
+     */
+    @PUT
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}/{id}")
+    @PreAuthorize("hasRole('ROLE_DFM')")
+    @TypeHint(ReportingTaskEntity.class)
+    public Response updateReportingTask(
+            @Context HttpServletRequest httpServletRequest,
+            @FormParam(VERSION) LongParameter version,
+            @FormParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+            @PathParam("availability") String availability, @PathParam("id") String id, @FormParam("name") String name,
+            @FormParam("annotationData") String annotationData, @FormParam("markedForDeletion[]") List<String> markedForDeletion,
+            @FormParam("state") String state, @FormParam("schedulingStrategy") String schedulingStrategy,
+            @FormParam("schedulingPeriod") String schedulingPeriod, @FormParam("comments") String comments,
+            MultivaluedMap<String, String> formParams) {
+
+        // create collections for holding the reporting task properties
+        final Map<String, String> updatedProperties = new LinkedHashMap<>();
+        
+        // go through each parameter and look for processor properties
+        for (String parameterName : formParams.keySet()) {
+            if (StringUtils.isNotBlank(parameterName)) {
+                // see if the parameter name starts with an expected parameter type...
+                // if so, store the parameter name and value in the corresponding collection
+                if (parameterName.startsWith("properties")) {
+                    final int startIndex = StringUtils.indexOf(parameterName, "[");
+                    final int endIndex = StringUtils.lastIndexOf(parameterName, "]");
+                    if (startIndex != -1 && endIndex != -1) {
+                        final String propertyName = StringUtils.substring(parameterName, startIndex + 1, endIndex);
+                        updatedProperties.put(propertyName, formParams.getFirst(parameterName));
+                    }
+                }
+            }
+        }
+        
+        // set the properties to remove
+        for (String propertyToDelete : markedForDeletion) {
+            updatedProperties.put(propertyToDelete, null);
+        }
+        
+        // create the reporting task DTO
+        final ReportingTaskDTO reportingTaskDTO = new ReportingTaskDTO();
+        reportingTaskDTO.setId(id);
+        reportingTaskDTO.setName(name);
+        reportingTaskDTO.setState(state);
+        reportingTaskDTO.setSchedulingStrategy(schedulingStrategy);
+        reportingTaskDTO.setSchedulingPeriod(schedulingPeriod);
+        reportingTaskDTO.setAnnotationData(annotationData);
+        reportingTaskDTO.setComments(comments);
+
+        // only set the properties when appropriate
+        if (!updatedProperties.isEmpty()) {
+            reportingTaskDTO.setProperties(updatedProperties);
+        }
+        
+        // create the revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+        if (version != null) {
+            revision.setVersion(version.getLong());
+        }
+
+        // create the reporting task entity
+        final ReportingTaskEntity reportingTaskEntity = new ReportingTaskEntity();
+        reportingTaskEntity.setRevision(revision);
+        reportingTaskEntity.setReportingTask(reportingTaskDTO);
+
+        // update the reporting task
+        return updateReportingTask(httpServletRequest, availability, id, reportingTaskEntity);
+    }
+
+    /**
+     * Updates the specified a Reporting Task.
+     *
+     * @param httpServletRequest
+     * @param availability Whether the reporting task is available on the NCM only (ncm) or on the 
+     * nodes only (node). If this instance is not clustered all tasks should use the node availability.
+     * @param id The id of the reporting task to update.
+     * @param reportingTaskEntity A reportingTaskEntity.
+     * @return A reportingTaskEntity.
+     */
+    @PUT
+    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}/{id}")
+    @PreAuthorize("hasRole('ROLE_DFM')")
+    @TypeHint(ReportingTaskEntity.class)
+    public Response updateReportingTask(
+            @Context HttpServletRequest httpServletRequest,
+            @PathParam("availability") String availability, 
+            @PathParam("id") String id,
+            ReportingTaskEntity reportingTaskEntity) {
+
+        final Availability avail = parseAvailability(availability);
+        
+        if (reportingTaskEntity == null || reportingTaskEntity.getReportingTask() == null) {
+            throw new IllegalArgumentException("Reporting task details must be specified.");
+        }
+
+        if (reportingTaskEntity.getRevision() == null) {
+            throw new IllegalArgumentException("Revision must be specified.");
+        }
+
+        // ensure the ids are the same
+        final ReportingTaskDTO requestReportingTaskDTO = reportingTaskEntity.getReportingTask();
+        if (!id.equals(requestReportingTaskDTO.getId())) {
+            throw new IllegalArgumentException(String.format("The reporting task id (%s) in the request body does not equal the "
+                    + "reporting task id of the requested resource (%s).", requestReportingTaskDTO.getId(), id));
+        }
+
+        // replicate if cluster manager
+        if (properties.isClusterManager() && Availability.NODE.equals(avail)) {
+            // change content type to JSON for serializing entity
+            final Map<String, String> headersToOverride = new HashMap<>();
+            headersToOverride.put("content-type", MediaType.APPLICATION_JSON);
+
+            // replicate the request
+            return clusterManager.applyRequest(HttpMethod.PUT, getAbsolutePath(), updateClientId(reportingTaskEntity), getHeaders(headersToOverride)).getResponse();
+        }
+        
+        // handle expects request (usually from the cluster manager)
+        final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER);
+        if (expects != null) {
+            serviceFacade.verifyUpdateReportingTask(requestReportingTaskDTO);
+            return generateContinueResponse().build();
+        }
+
+        // update the reporting task
+        final RevisionDTO revision = reportingTaskEntity.getRevision();
+        final ConfigurationSnapshot<ReportingTaskDTO> controllerResponse = serviceFacade.updateReportingTask(
+                new Revision(revision.getVersion(), revision.getClientId()), requestReportingTaskDTO);
+
+        // get the results
+        final ReportingTaskDTO responseReportingTaskDTO = controllerResponse.getConfiguration();
+
+        // get the updated revision
+        final RevisionDTO updatedRevision = new RevisionDTO();
+        updatedRevision.setClientId(revision.getClientId());
+        updatedRevision.setVersion(controllerResponse.getVersion());
+
+        // build the response entity
+        final ReportingTaskEntity entity = new ReportingTaskEntity();
+        entity.setRevision(updatedRevision);
+        entity.setReportingTask(populateRemainingReportingTaskContent(availability, responseReportingTaskDTO));
+
+        return clusterContext(generateOkResponse(entity)).build();
+    }
+
+    /**
+     * Removes the specified reporting task.
+     *
+     * @param httpServletRequest
+     * @param version The revision is used to verify the client is working with
+     * the latest version of the flow.
+     * @param clientId Optional client id. If the client id is not specified, a
+     * new one will be generated. This value (whether specified or generated) is
+     * included in the response.
+     * @param availability Whether the reporting task is available on the NCM only (ncm) or on the 
+     * nodes only (node). If this instance is not clustered all tasks should use the node availability.
+     * @param id The id of the reporting task to remove.
+     * @return A entity containing the client id and an updated revision.
+     */
+    @DELETE
+    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+    @Path("/{availability}/{id}")
+    @PreAuthorize("hasRole('ROLE_DFM')")
+    @TypeHint(ReportingTaskEntity.class)
+    public Response removeReportingTask(
+            @Context HttpServletRequest httpServletRequest,
+            @QueryParam(VERSION) LongParameter version,
+            @QueryParam(CLIENT_ID) @DefaultValue(StringUtils.EMPTY) ClientIdParameter clientId,
+            @PathParam("availability") String availability, @PathParam("id") String id) {
+
+        final Availability avail = parseAvailability(availability);
+        
+        // replicate if cluster manager
+        if (properties.isClusterManager() && Availability.NODE.equals(avail)) {
+            return clusterManager.applyRequest(HttpMethod.DELETE, getAbsolutePath(), getRequestParameters(true), getHeaders()).getResponse();
+        }
+
+        // handle expects request (usually from the cluster manager)
+        final String expects = httpServletRequest.getHeader(WebClusterManager.NCM_EXPECTS_HTTP_HEADER);
+        if (expects != null) {
+            serviceFacade.verifyDeleteReportingTask(id);
+            return generateContinueResponse().build();
+        }
+
+        // determine the specified version
+        Long clientVersion = null;
+        if (version != null) {
+            clientVersion = version.getLong();
+        }
+
+        // delete the specified reporting task
+        final ConfigurationSnapshot<Void> controllerResponse = serviceFacade.deleteReportingTask(new Revision(clientVersion, clientId.getClientId()), id);
+
+        // get the updated revision
+        final RevisionDTO revision = new RevisionDTO();
+        revision.setClientId(clientId.getClientId());
+        revision.setVersion(controllerResponse.getVersion());
+
+        // build the response entity
+        final ReportingTaskEntity entity = new ReportingTaskEntity();
+        entity.setRevision(revision);
+
+        return clusterContext(generateOkResponse(entity)).build();
+    }
+
+    // setters
+    
+    public void setServiceFacade(NiFiServiceFacade serviceFacade) {
+        this.serviceFacade = serviceFacade;
+    }
+
+    public void setClusterManager(WebClusterManager clusterManager) {
+        this.clusterManager = clusterManager;
+    }
+
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SnippetResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SnippetResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SnippetResource.java
index e0b7788..275b133 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SnippetResource.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/SnippetResource.java
@@ -340,7 +340,7 @@ public class SnippetResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(response.getRevision());
+        updatedRevision.setVersion(response.getVersion());
 
         // build the response entity
         SnippetEntity entity = new SnippetEntity();
@@ -520,7 +520,7 @@ public class SnippetResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO updatedRevision = new RevisionDTO();
         updatedRevision.setClientId(revision.getClientId());
-        updatedRevision.setVersion(controllerResponse.getRevision());
+        updatedRevision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         SnippetEntity entity = new SnippetEntity();
@@ -577,7 +577,7 @@ public class SnippetResource extends ApplicationResource {
         // get the updated revision
         final RevisionDTO revision = new RevisionDTO();
         revision.setClientId(clientId.getClientId());
-        revision.setVersion(controllerResponse.getRevision());
+        revision.setVersion(controllerResponse.getVersion());
 
         // build the response entity
         SnippetEntity entity = new SnippetEntity();

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/ThrowableMapper.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/ThrowableMapper.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/ThrowableMapper.java
index 091653a..0ef6edb 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/ThrowableMapper.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/config/ThrowableMapper.java
@@ -34,11 +34,8 @@ public class ThrowableMapper implements ExceptionMapper<Throwable> {
     @Override
     public Response toResponse(Throwable exception) {
         // log the error
-        logger.info(String.format("An unexpected error has occurred: %s. Returning %s response.", exception, Response.Status.INTERNAL_SERVER_ERROR));
-
-        if (logger.isDebugEnabled()) {
-            logger.debug(StringUtils.EMPTY, exception);
-        }
+        logger.error(String.format("An unexpected error has occurred: %s. Returning %s response.", exception, Response.Status.INTERNAL_SERVER_ERROR));
+        logger.error(StringUtils.EMPTY, exception);
 
         return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("An unexpected error has occurred. Please check the logs for additional details.").type("text/plain").build();
     }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
index 92a5449..7fe76ad 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
@@ -39,7 +39,7 @@ import javax.ws.rs.WebApplicationException;
 
 import org.apache.nifi.action.Action;
 import org.apache.nifi.action.component.details.ComponentDetails;
-import org.apache.nifi.action.component.details.ProcessorDetails;
+import org.apache.nifi.action.component.details.ExtensionDetails;
 import org.apache.nifi.action.component.details.RemoteProcessGroupDetails;
 import org.apache.nifi.action.details.ActionDetails;
 import org.apache.nifi.action.details.ConfigureDetails;
@@ -97,14 +97,12 @@ import org.apache.nifi.scheduling.SchedulingStrategy;
 import org.apache.nifi.user.NiFiUser;
 import org.apache.nifi.user.NiFiUserGroup;
 import org.apache.nifi.util.FormatUtils;
-import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.Revision;
-import org.apache.nifi.web.api.dto.ProcessorConfigDTO.AllowableValueDTO;
-import org.apache.nifi.web.api.dto.ProcessorConfigDTO.PropertyDescriptorDTO;
+import org.apache.nifi.web.api.dto.PropertyDescriptorDTO.AllowableValueDTO;
 import org.apache.nifi.web.api.dto.action.ActionDTO;
 import org.apache.nifi.web.api.dto.action.HistoryDTO;
 import org.apache.nifi.web.api.dto.action.component.details.ComponentDetailsDTO;
-import org.apache.nifi.web.api.dto.action.component.details.ProcessorDetailsDTO;
+import org.apache.nifi.web.api.dto.action.component.details.ExtensionDetailsDTO;
 import org.apache.nifi.web.api.dto.action.component.details.RemoteProcessGroupDetailsDTO;
 import org.apache.nifi.web.api.dto.action.details.ActionDetailsDTO;
 import org.apache.nifi.web.api.dto.action.details.ConfigureDetailsDTO;
@@ -124,6 +122,12 @@ import org.apache.nifi.web.api.dto.status.ProcessorStatusDTO;
 import org.apache.nifi.web.api.dto.status.RemoteProcessGroupStatusDTO;
 import org.apache.nifi.web.api.dto.status.StatusDTO;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.controller.ConfiguredComponent;
+import org.apache.nifi.controller.ReportingTaskNode;
+import org.apache.nifi.controller.service.ControllerServiceNode;
+import org.apache.nifi.controller.service.ControllerServiceReference;
+import org.apache.nifi.reporting.ReportingTask;
+import org.apache.nifi.web.FlowModification;
 
 /**
  *
@@ -218,9 +222,9 @@ public final class DtoFactory {
             return null;
         }
 
-        if (componentDetails instanceof ProcessorDetails) {
-            final ProcessorDetailsDTO processorDetails = new ProcessorDetailsDTO();
-            processorDetails.setType(((ProcessorDetails) componentDetails).getType());
+        if (componentDetails instanceof ExtensionDetails) {
+            final ExtensionDetailsDTO processorDetails = new ExtensionDetailsDTO();
+            processorDetails.setType(((ExtensionDetails) componentDetails).getType());
             return processorDetails;
         } else if (componentDetails instanceof RemoteProcessGroupDetails) {
             final RemoteProcessGroupDetailsDTO remoteProcessGroupDetails = new RemoteProcessGroupDetailsDTO();
@@ -834,6 +838,241 @@ public final class DtoFactory {
         return dto;
     }
 
+    public ReportingTaskDTO createReportingTaskDto(final ReportingTaskNode reportingTaskNode) {
+        final ReportingTaskDTO dto = new ReportingTaskDTO();
+        dto.setId(reportingTaskNode.getIdentifier());
+        dto.setName(reportingTaskNode.getName());
+        dto.setType(reportingTaskNode.getReportingTask().getClass().getName());
+        dto.setSchedulingStrategy(reportingTaskNode.getSchedulingStrategy().name());
+        dto.setSchedulingPeriod(reportingTaskNode.getSchedulingPeriod());
+        dto.setState(reportingTaskNode.getScheduledState().name());
+        dto.setActiveThreadCount(reportingTaskNode.getActiveThreadCount());
+        dto.setAnnotationData(reportingTaskNode.getAnnotationData());
+        dto.setComments(reportingTaskNode.getComments());
+
+        final Map<String, String> defaultSchedulingPeriod = new HashMap<>();
+        defaultSchedulingPeriod.put(SchedulingStrategy.TIMER_DRIVEN.name(), SchedulingStrategy.TIMER_DRIVEN.getDefaultSchedulingPeriod());
+        defaultSchedulingPeriod.put(SchedulingStrategy.CRON_DRIVEN.name(), SchedulingStrategy.CRON_DRIVEN.getDefaultSchedulingPeriod());
+        dto.setDefaultSchedulingPeriod(defaultSchedulingPeriod);
+        
+        // sort a copy of the properties
+        final Map<PropertyDescriptor, String> sortedProperties = new TreeMap<>(new Comparator<PropertyDescriptor>() {
+            @Override
+            public int compare(PropertyDescriptor o1, PropertyDescriptor o2) {
+                return Collator.getInstance(Locale.US).compare(o1.getName(), o2.getName());
+            }
+        });
+        sortedProperties.putAll(reportingTaskNode.getProperties());
+
+        // get the property order from the reporting task
+        final ReportingTask reportingTask = reportingTaskNode.getReportingTask();
+        final Map<PropertyDescriptor, String> orderedProperties = new LinkedHashMap<>();
+        final List<PropertyDescriptor> descriptors = reportingTask.getPropertyDescriptors();
+        if (descriptors != null && !descriptors.isEmpty()) {
+            for (PropertyDescriptor descriptor : descriptors) {
+                orderedProperties.put(descriptor, null);
+            }
+        }
+        orderedProperties.putAll(sortedProperties);
+        
+        // build the descriptor and property dtos
+        dto.setDescriptors(new LinkedHashMap<String, PropertyDescriptorDTO>());
+        dto.setProperties(new LinkedHashMap<String, String>());
+        for (final Map.Entry<PropertyDescriptor, String> entry : orderedProperties.entrySet()) {
+            final PropertyDescriptor descriptor = entry.getKey();
+
+            // store the property descriptor
+            dto.getDescriptors().put(descriptor.getName(), createPropertyDescriptorDto(descriptor));
+
+            // determine the property value - don't include sensitive properties
+            String propertyValue = entry.getValue();
+            if (propertyValue != null && descriptor.isSensitive()) {
+                propertyValue = "********";
+            }
+
+            // set the property value
+            dto.getProperties().put(descriptor.getName(), propertyValue);
+        }
+        
+        // add the validation errors
+        final Collection<ValidationResult> validationErrors = reportingTaskNode.getValidationErrors();
+        if (validationErrors != null && !validationErrors.isEmpty()) {
+            final List<String> errors = new ArrayList<>();
+            for (final ValidationResult validationResult : validationErrors) {
+                errors.add(validationResult.toString());
+            }
+
+            dto.setValidationErrors(errors);
+        }
+        
+        return dto;
+    }
+    
+    public ControllerServiceDTO createControllerServiceDto(final ControllerServiceNode controllerServiceNode) {
+        final ControllerServiceDTO dto = new ControllerServiceDTO();
+        dto.setId(controllerServiceNode.getIdentifier());
+        dto.setName(controllerServiceNode.getName());
+        dto.setType(controllerServiceNode.getControllerServiceImplementation().getClass().getName());
+        dto.setState(controllerServiceNode.getState().name());
+        dto.setAnnotationData(controllerServiceNode.getAnnotationData());
+        dto.setComments(controllerServiceNode.getComments());
+        
+        // sort a copy of the properties
+        final Map<PropertyDescriptor, String> sortedProperties = new TreeMap<>(new Comparator<PropertyDescriptor>() {
+            @Override
+            public int compare(PropertyDescriptor o1, PropertyDescriptor o2) {
+                return Collator.getInstance(Locale.US).compare(o1.getName(), o2.getName());
+            }
+        });
+        sortedProperties.putAll(controllerServiceNode.getProperties());
+
+        // get the property order from the controller service
+        final ControllerService controllerService = controllerServiceNode.getControllerServiceImplementation();
+        final Map<PropertyDescriptor, String> orderedProperties = new LinkedHashMap<>();
+        final List<PropertyDescriptor> descriptors = controllerService.getPropertyDescriptors();
+        if (descriptors != null && !descriptors.isEmpty()) {
+            for (PropertyDescriptor descriptor : descriptors) {
+                orderedProperties.put(descriptor, null);
+            }
+        }
+        orderedProperties.putAll(sortedProperties);
+        
+        // build the descriptor and property dtos
+        dto.setDescriptors(new LinkedHashMap<String, PropertyDescriptorDTO>());
+        dto.setProperties(new LinkedHashMap<String, String>());
+        for (final Map.Entry<PropertyDescriptor, String> entry : orderedProperties.entrySet()) {
+            final PropertyDescriptor descriptor = entry.getKey();
+
+            // store the property descriptor
+            dto.getDescriptors().put(descriptor.getName(), createPropertyDescriptorDto(descriptor));
+
+            // determine the property value - don't include sensitive properties
+            String propertyValue = entry.getValue();
+            if (propertyValue != null && descriptor.isSensitive()) {
+                propertyValue = "********";
+            }
+
+            // set the property value
+            dto.getProperties().put(descriptor.getName(), propertyValue);
+        }
+        
+        // create the reference dto's
+        dto.setReferencingComponents(createControllerServiceReferencingComponentsDto(controllerServiceNode.getReferences()));
+
+        // add the validation errors
+        final Collection<ValidationResult> validationErrors = controllerServiceNode.getValidationErrors();
+        if (validationErrors != null && !validationErrors.isEmpty()) {
+            final List<String> errors = new ArrayList<>();
+            for (final ValidationResult validationResult : validationErrors) {
+                errors.add(validationResult.toString());
+            }
+
+            dto.setValidationErrors(errors);
+        }
+        
+        return dto;
+    }
+    
+    public Set<ControllerServiceReferencingComponentDTO> createControllerServiceReferencingComponentsDto(final ControllerServiceReference reference) {
+        return createControllerServiceReferencingComponentsDto(reference, new HashSet<ControllerServiceNode>());
+    }
+    
+    private Set<ControllerServiceReferencingComponentDTO> createControllerServiceReferencingComponentsDto(final ControllerServiceReference reference, final Set<ControllerServiceNode> visited) {
+        final Set<ControllerServiceReferencingComponentDTO> referencingComponents = new LinkedHashSet<>();
+        
+        // get all references
+        for (final ConfiguredComponent component : reference.getReferencingComponents()) {
+            final ControllerServiceReferencingComponentDTO dto = new ControllerServiceReferencingComponentDTO();
+            dto.setId(component.getIdentifier());
+            dto.setName(component.getName());
+            
+            List<PropertyDescriptor> propertyDescriptors = null;
+            Collection<ValidationResult> validationErrors = null;
+            if (component instanceof ProcessorNode) {
+                final ProcessorNode node = ((ProcessorNode) component);
+                dto.setGroupId(node.getProcessGroup().getIdentifier());
+                dto.setState(node.getScheduledState().name());
+                dto.setActiveThreadCount(node.getActiveThreadCount());
+                dto.setType(node.getProcessor().getClass().getName());
+                dto.setReferenceType(Processor.class.getSimpleName());
+                
+                propertyDescriptors = node.getProcessor().getPropertyDescriptors();
+                validationErrors = node.getValidationErrors();
+            } else if (component instanceof ControllerServiceNode) {
+                final ControllerServiceNode node = ((ControllerServiceNode) component);
+                dto.setState(node.getState().name());
+                dto.setType(node.getControllerServiceImplementation().getClass().getName());
+                dto.setReferenceType(ControllerService.class.getSimpleName());
+                dto.setReferenceCycle(visited.contains(node));
+                
+                // if we haven't encountered this service before include it's referencing components
+                if (!dto.getReferenceCycle()) {
+                    dto.setReferencingComponents(createControllerServiceReferencingComponentsDto(node.getReferences(), visited));
+                }
+                
+                propertyDescriptors = node.getControllerServiceImplementation().getPropertyDescriptors();
+                validationErrors = node.getValidationErrors();
+            } else if (component instanceof ReportingTaskNode) {
+                final ReportingTaskNode node = ((ReportingTaskNode) component);
+                dto.setState(node.getScheduledState().name());
+                dto.setActiveThreadCount(node.getActiveThreadCount());
+                dto.setType(node.getReportingTask().getClass().getName());
+                dto.setReferenceType(ReportingTask.class.getSimpleName());
+                
+                propertyDescriptors = node.getReportingTask().getPropertyDescriptors();
+                validationErrors = node.getValidationErrors();
+            }
+            
+            if (propertyDescriptors != null && !propertyDescriptors.isEmpty()) {
+                final Map<PropertyDescriptor, String> sortedProperties = new TreeMap<>(new Comparator<PropertyDescriptor>() {
+                    @Override
+                    public int compare(PropertyDescriptor o1, PropertyDescriptor o2) {
+                        return Collator.getInstance(Locale.US).compare(o1.getName(), o2.getName());
+                    }
+                });
+                sortedProperties.putAll(component.getProperties());
+
+                final Map<PropertyDescriptor, String> orderedProperties = new LinkedHashMap<>();
+                for (PropertyDescriptor descriptor : propertyDescriptors) {
+                    orderedProperties.put(descriptor, null);
+                }
+                orderedProperties.putAll(sortedProperties);
+
+                // build the descriptor and property dtos
+                dto.setDescriptors(new LinkedHashMap<String, PropertyDescriptorDTO>());
+                dto.setProperties(new LinkedHashMap<String, String>());
+                for (final Map.Entry<PropertyDescriptor, String> entry : orderedProperties.entrySet()) {
+                    final PropertyDescriptor descriptor = entry.getKey();
+
+                    // store the property descriptor
+                    dto.getDescriptors().put(descriptor.getName(), createPropertyDescriptorDto(descriptor));
+
+                    // determine the property value - don't include sensitive properties
+                    String propertyValue = entry.getValue();
+                    if (propertyValue != null && descriptor.isSensitive()) {
+                        propertyValue = "********";
+                    }
+
+                    // set the property value
+                    dto.getProperties().put(descriptor.getName(), propertyValue);
+                }
+            }
+            
+            if (validationErrors != null && !validationErrors.isEmpty()) {
+                final List<String> errors = new ArrayList<>();
+                for (final ValidationResult validationResult : validationErrors) {
+                    errors.add(validationResult.toString());
+                }
+
+                dto.setValidationErrors(errors);
+            }
+            
+            referencingComponents.add(dto);
+        }
+        
+        return referencingComponents;
+    }
+    
     public RemoteProcessGroupPortDTO createRemoteProcessGroupPortDto(final RemoteGroupPort port) {
         if (port == null) {
             return null;
@@ -1143,6 +1382,135 @@ public final class DtoFactory {
 
         return types;
     }
+    
+    /**
+     * Identifies all baseTypes for the specified type that are assignable to the specified baseType.
+     * 
+     * @param baseType
+     * @param type
+     * @param baseTypes 
+     */
+    private void identifyBaseTypes(final Class baseType, final Class type, final Set<Class> baseTypes, final  boolean recurse) {
+        final Class[] interfaces = type.getInterfaces();
+        for (final Class i : interfaces) {
+            if (baseType.isAssignableFrom(i) && !baseType.equals(i)) {
+                baseTypes.add(i);
+            }
+        }
+        
+        if (recurse) {
+            if (type.getSuperclass() != null) {
+                identifyBaseTypes(baseType, type.getSuperclass(), baseTypes, recurse);
+            }
+        }
+    }
+    
+    /**
+     * Gets the DocumentedTypeDTOs from the specified classes for the specified baseClass.
+     *
+     * @param baseClass
+     * @param classes
+     * @return
+     */
+    public Set<DocumentedTypeDTO> fromDocumentedTypes(final Class baseClass, final Set<Class> classes) {
+        final Set<DocumentedTypeDTO> types = new LinkedHashSet<>();
+        final Set<Class> sortedClasses = new TreeSet<>(CLASS_NAME_COMPARATOR);
+        sortedClasses.addAll(classes);
+        
+        // identify all interfaces that extend baseClass for all classes
+        final Set<Class> interfaces = new HashSet<>();
+        for (final Class<?> cls : sortedClasses) {
+            identifyBaseTypes(baseClass, cls, interfaces, true);
+        }
+        
+        // build a lookup of all interfaces
+        final Map<Class, DocumentedTypeDTO> lookup = new HashMap<>();
+        
+        // convert the interfaces to DTO form
+        for (final Class<?> i : interfaces) {
+            final DocumentedTypeDTO type = new DocumentedTypeDTO();
+            type.setType(i.getName());
+            type.setDescription(getCapabilityDescription(i));
+            type.setTags(getTags(i));
+            type.setChildTypes(new LinkedHashSet<DocumentedTypeDTO>());
+            lookup.put(i, type);
+        }
+        
+        // move the interfaces into the appropriate hierarchy
+        final Collection<Class> rootTypes = new ArrayList<>();
+        for (final Class<?> i : interfaces) {
+            rootTypes.add(i);
+            
+            // identify the base types
+            final Set<Class> baseTypes = new LinkedHashSet<>();
+            identifyBaseTypes(baseClass, i, baseTypes, false);
+            
+            // move this interfaces into the hierarchy where appropriate
+            if (!baseTypes.isEmpty()) {
+                // get the DTO for each base type
+                for (final Class baseType : baseTypes) {
+                    final DocumentedTypeDTO parentInteface = lookup.get(baseType);
+                    final DocumentedTypeDTO childInterface = lookup.get(i);
+                    
+                    // include all parent tags in the respective children
+                    childInterface.getTags().addAll(parentInteface.getTags());
+                    
+                    // update the hierarchy
+                    parentInteface.getChildTypes().add(childInterface);
+                }
+                
+                // remove this interface from the lookup (this will only
+                // leave the interfaces that are ancestor roots)
+                rootTypes.remove(i);
+            }
+        }
+
+        // include the interfaces
+        sortedClasses.addAll(rootTypes);
+        
+        // get the DTO form for all interfaces and classes
+        for (final Class<?> cls : sortedClasses) {
+            boolean add = false;
+            
+            final DocumentedTypeDTO type;
+            if (rootTypes.contains(cls)) {
+                type = lookup.get(cls);
+                add = true;
+            } else {
+                type = new DocumentedTypeDTO();
+                type.setType(cls.getName());
+                type.setDescription(getCapabilityDescription(cls));
+                type.setTags(getTags(cls));
+            }
+            
+            // identify the base types
+            final Set<Class> baseTypes = new LinkedHashSet<>();
+            identifyBaseTypes(baseClass, cls, baseTypes, false);
+            
+            // include this type if it doesn't belong to another hierarchy
+            if (baseTypes.isEmpty()) {
+                add = true;
+            } else {
+                // get the DTO for each base type
+                for (final Class baseType : baseTypes) {
+                    final DocumentedTypeDTO parentInterface = lookup.get(baseType);
+
+                    // include all parent tags in the respective children
+                    type.getTags().addAll(parentInterface.getTags());
+
+                    // update the hierarchy
+                    parentInterface.getChildTypes().add(type);
+                }
+            }
+            
+            // add if appropriate
+            if (add) {
+                types.add(type);
+            }
+        }
+
+        return types;
+    }
 
     /**
      * Creates a ProcessorDTO from the specified ProcessorNode.
@@ -1575,12 +1943,12 @@ public final class DtoFactory {
      * @param propertyDescriptor
      * @return
      */
-    private ProcessorConfigDTO.PropertyDescriptorDTO createPropertyDescriptorDto(final PropertyDescriptor propertyDescriptor) {
+    public PropertyDescriptorDTO createPropertyDescriptorDto(final PropertyDescriptor propertyDescriptor) {
         if (propertyDescriptor == null) {
             return null;
         }
 
-        final ProcessorConfigDTO.PropertyDescriptorDTO dto = new ProcessorConfigDTO.PropertyDescriptorDTO();
+        final PropertyDescriptorDTO dto = new PropertyDescriptorDTO();
 
         dto.setName(propertyDescriptor.getName());
         dto.setDisplayName(propertyDescriptor.getDisplayName());
@@ -1590,18 +1958,16 @@ public final class DtoFactory {
         dto.setDescription(propertyDescriptor.getDescription());
         dto.setDefaultValue(propertyDescriptor.getDefaultValue());
         dto.setSupportsEl(propertyDescriptor.isExpressionLanguageSupported());
+        dto.setIdentifiesControllerService(propertyDescriptor.getControllerServiceDefinition() != null);
 
         final Class<? extends ControllerService> serviceDefinition = propertyDescriptor.getControllerServiceDefinition();
         if (propertyDescriptor.getAllowableValues() == null) {
             if (serviceDefinition == null) {
                 dto.setAllowableValues(null);
             } else {
-                final Set<AllowableValueDTO> allowableValues = new LinkedHashSet<>();
+                final List<AllowableValueDTO> allowableValues = new ArrayList<>();
                 for (final String serviceIdentifier : controllerServiceLookup.getControllerServiceIdentifiers(serviceDefinition)) {
-                    String displayName = serviceIdentifier;
-
-                    // TODO: attempt to get the controller service name
-                    final ControllerService controllerService = controllerServiceLookup.getControllerService(serviceIdentifier);
+                	final String displayName = controllerServiceLookup.getControllerServiceName(serviceIdentifier);
 
                     final AllowableValueDTO allowableValue = new AllowableValueDTO();
                     allowableValue.setDisplayName(displayName);
@@ -1611,7 +1977,7 @@ public final class DtoFactory {
                 dto.setAllowableValues(allowableValues);
             }
         } else {
-            final Set<AllowableValueDTO> allowableValues = new LinkedHashSet<>();
+            final List<AllowableValueDTO> allowableValues = new ArrayList<>();
             for (final AllowableValue allowableValue : propertyDescriptor.getAllowableValues()) {
                 final AllowableValueDTO allowableValueDto = new AllowableValueDTO();
                 allowableValueDto.setDisplayName(allowableValue.getDisplayName());
@@ -1642,6 +2008,25 @@ public final class DtoFactory {
         return copy;
     }
 
+    
+    public ControllerServiceDTO copy(final ControllerServiceDTO original) {
+        final ControllerServiceDTO copy = new ControllerServiceDTO();
+        copy.setAnnotationData(original.getAnnotationData());
+        copy.setAvailability(original.getAvailability());
+        copy.setComments(original.getComments());
+        copy.setCustomUiUrl(original.getCustomUiUrl());
+        copy.setDescriptors(copy(original.getDescriptors()));
+        copy.setId(original.getId());
+        copy.setName(original.getName());
+        copy.setProperties(copy(original.getProperties()));
+        copy.setReferencingComponents(copy(original.getReferencingComponents()));
+        copy.setState(original.getState());
+        copy.setType(original.getType());
+        copy.setUri(original.getUri());
+        copy.setValidationErrors(copy(original.getValidationErrors()));
+        return copy;
+    }
+    
     public FunnelDTO copy(final FunnelDTO original) {
         final FunnelDTO copy = new FunnelDTO();
         copy.setId(original.getId());
@@ -2029,14 +2414,17 @@ public final class DtoFactory {
     /**
      * Factory method for creating a new RevisionDTO based on this controller.
      *
-     * @param revision
+     * @param lastMod
      * @return
      */
-    public RevisionDTO createRevisionDTO(Revision revision) {
+    public RevisionDTO createRevisionDTO(FlowModification lastMod) {
+        final Revision revision = lastMod.getRevision();
+        
         // create the dto
         final RevisionDTO revisionDTO = new RevisionDTO();
         revisionDTO.setVersion(revision.getVersion());
         revisionDTO.setClientId(revision.getClientId());
+        revisionDTO.setLastModifier(lastMod.getLastModifier());
 
         return revisionDTO;
     }
@@ -2146,8 +2534,6 @@ public final class DtoFactory {
     }
 
     /* setters */
-    public void setProperties(NiFiProperties properties) {
-    }
 
     public void setControllerServiceLookup(ControllerServiceLookup lookup) {
         this.controllerServiceLookup = lookup;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
index 117555a..a373f05 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
@@ -52,8 +52,6 @@ import org.apache.nifi.controller.ProcessorNode;
 import org.apache.nifi.controller.ScheduledState;
 import org.apache.nifi.controller.repository.ContentNotFoundException;
 import org.apache.nifi.controller.repository.claim.ContentDirection;
-import org.apache.nifi.controller.service.ControllerServiceNode;
-import org.apache.nifi.controller.service.ControllerServiceProvider;
 import org.apache.nifi.controller.status.ProcessGroupStatus;
 import org.apache.nifi.diagnostics.SystemDiagnostics;
 import org.apache.nifi.flowfile.FlowFilePrioritizer;
@@ -79,11 +77,11 @@ import org.apache.nifi.provenance.search.SearchableField;
 import org.apache.nifi.remote.RootGroupPort;
 import org.apache.nifi.reporting.Bulletin;
 import org.apache.nifi.reporting.BulletinRepository;
+import org.apache.nifi.reporting.ReportingTask;
 import org.apache.nifi.scheduling.SchedulingStrategy;
 import org.apache.nifi.search.SearchContext;
 import org.apache.nifi.search.SearchResult;
 import org.apache.nifi.search.Searchable;
-import org.apache.nifi.web.security.user.NiFiUserUtils;
 import org.apache.nifi.services.FlowService;
 import org.apache.nifi.user.NiFiUser;
 import org.apache.nifi.util.FormatUtils;
@@ -114,6 +112,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.admin.service.UserService;
 import org.apache.nifi.authorization.DownloadAuthorization;
 import org.apache.nifi.processor.DataUnit;
+import org.apache.nifi.web.security.user.NiFiUserUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.access.AccessDeniedException;
@@ -121,7 +120,7 @@ import org.springframework.security.access.AccessDeniedException;
 /**
  *
  */
-public class ControllerFacade implements ControllerServiceProvider {
+public class ControllerFacade {
 
     private static final Logger logger = LoggerFactory.getLogger(ControllerFacade.class);
 
@@ -347,6 +346,24 @@ public class ControllerFacade implements ControllerServiceProvider {
     public Set<DocumentedTypeDTO> getFlowFileComparatorTypes() {
         return dtoFactory.fromDocumentedTypes(ExtensionManager.getExtensions(FlowFilePrioritizer.class));
     }
+    
+    /**
+     * Gets the ControllerService types that this controller supports.
+     * 
+     * @return 
+     */
+    public Set<DocumentedTypeDTO> getControllerServiceTypes() {
+        return dtoFactory.fromDocumentedTypes(ControllerService.class, ExtensionManager.getExtensions(ControllerService.class));
+    }
+    
+    /**
+     * Gets the ReportingTask types that this controller supports.
+     * 
+     * @return 
+     */
+    public Set<DocumentedTypeDTO> getReportingTaskTypes() {
+        return dtoFactory.fromDocumentedTypes(ReportingTask.class, ExtensionManager.getExtensions(ReportingTask.class));
+    }
 
     /**
      * Gets the counters for this controller.
@@ -371,56 +388,6 @@ public class ControllerFacade implements ControllerServiceProvider {
 
         return counter;
     }
-
-    /**
-     * Return the controller service for the specified identifier.
-     *
-     * @param serviceIdentifier
-     * @return
-     */
-    @Override
-    public ControllerService getControllerService(String serviceIdentifier) {
-        return flowController.getControllerService(serviceIdentifier);
-    }
-
-    @Override
-    public ControllerServiceNode createControllerService(String type, String id, boolean firstTimeAdded) {
-        return flowController.createControllerService(type, id, firstTimeAdded);
-    }
-    
-    public void removeControllerService(ControllerServiceNode serviceNode) {
-        flowController.removeControllerService(serviceNode);
-    }
-
-    @Override
-    public Set<String> getControllerServiceIdentifiers(Class<? extends ControllerService> serviceType) {
-        return flowController.getControllerServiceIdentifiers(serviceType);
-    }
-
-    @Override
-    public ControllerServiceNode getControllerServiceNode(final String id) {
-        return flowController.getControllerServiceNode(id);
-    }
-
-    @Override
-    public boolean isControllerServiceEnabled(final ControllerService service) {
-        return flowController.isControllerServiceEnabled(service);
-    }
-
-    @Override
-    public boolean isControllerServiceEnabled(final String serviceIdentifier) {
-        return flowController.isControllerServiceEnabled(serviceIdentifier);
-    }
-
-    @Override
-    public void enableControllerService(final ControllerServiceNode serviceNode) {
-        flowController.enableControllerService(serviceNode);
-    }
-    
-    @Override
-    public void disableControllerService(ControllerServiceNode serviceNode) {
-        flowController.disableControllerService(serviceNode);
-    }
     
     /**
      * Gets the status of this controller.

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ControllerServiceDAO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ControllerServiceDAO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ControllerServiceDAO.java
new file mode 100644
index 0000000..c1fba0c
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ControllerServiceDAO.java
@@ -0,0 +1,110 @@
+/*
+ * 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.web.dao;
+
+import java.util.Set;
+import org.apache.nifi.controller.ScheduledState;
+
+import org.apache.nifi.controller.service.ControllerServiceNode;
+import org.apache.nifi.controller.service.ControllerServiceReference;
+import org.apache.nifi.controller.service.ControllerServiceState;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
+
+/**
+ *
+ */
+public interface ControllerServiceDAO {
+
+    /**
+     * Determines if the specified controller service exists.
+     *
+     * @param controllerServiceId
+     * @return
+     */
+    boolean hasControllerService(String controllerServiceId);
+
+    /**
+     * Creates a controller service.
+     *
+     * @param controllerServiceDTO The controller service DTO
+     * @return The controller service
+     */
+    ControllerServiceNode createControllerService(ControllerServiceDTO controllerServiceDTO);
+
+    /**
+     * Gets the specified controller service.
+     *
+     * @param controllerServiceId The controller service id
+     * @return The controller service
+     */
+    ControllerServiceNode getControllerService(String controllerServiceId);
+
+    /**
+     * Gets all of the controller services.
+     *
+     * @return The controller services
+     */
+    Set<ControllerServiceNode> getControllerServices();
+
+    /**
+     * Updates the specified controller service.
+     *
+     * @param controllerServiceDTO The controller service DTO
+     * @return The controller service
+     */
+    ControllerServiceNode updateControllerService(ControllerServiceDTO controllerServiceDTO);
+
+    /**
+     * Updates the referencing components for the specified controller service.
+     * 
+     * @param controllerServiceId
+     * @param scheduledState
+     * @param controllerServiceState the value of state 
+     * @return the org.apache.nifi.controller.service.ControllerServiceReference 
+     */
+    ControllerServiceReference updateControllerServiceReferencingComponents(String controllerServiceId, ScheduledState scheduledState, ControllerServiceState controllerServiceState);
+    
+    /**
+     * Determines whether this controller service can be updated.
+     *
+     * @param controllerServiceDTO
+     */
+    void verifyUpdate(ControllerServiceDTO controllerServiceDTO);
+    
+    /**
+     * Determines whether the referencing component of the specified controller service can be updated.
+     * 
+     * @param controllerServiceId
+     * @param scheduledState
+     * @param controllerServiceState 
+     */
+    void verifyUpdateReferencingComponents(String controllerServiceId, ScheduledState scheduledState, ControllerServiceState controllerServiceState);
+    
+    /**
+     * Determines whether this controller service can be removed.
+     *
+     * @param controllerServiceId
+     */
+    void verifyDelete(String controllerServiceId);
+
+    /**
+     * Deletes the specified controller service.
+     *
+     * @param controllerServiceId The controller service id
+     */
+    void deleteControllerService(String controllerServiceId);
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ReportingTaskDAO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ReportingTaskDAO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ReportingTaskDAO.java
new file mode 100644
index 0000000..49446d3
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ReportingTaskDAO.java
@@ -0,0 +1,88 @@
+/*
+ * 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.web.dao;
+
+import java.util.Set;
+import org.apache.nifi.controller.ReportingTaskNode;
+
+import org.apache.nifi.web.api.dto.ReportingTaskDTO;
+
+/**
+ *
+ */
+public interface ReportingTaskDAO {
+
+    /**
+     * Determines if the specified reporting task exists.
+     *
+     * @param reportingTaskId
+     * @return
+     */
+    boolean hasReportingTask(String reportingTaskId);
+
+    /**
+     * Creates a reporting task.
+     *
+     * @param reportingTaskDTO The reporting task DTO
+     * @return The reporting task
+     */
+    ReportingTaskNode createReportingTask(ReportingTaskDTO reportingTaskDTO);
+
+    /**
+     * Gets the specified reporting task.
+     *
+     * @param reportingTaskId The reporting task id
+     * @return The reporting task
+     */
+    ReportingTaskNode getReportingTask(String reportingTaskId);
+
+    /**
+     * Gets all of the reporting tasks.
+     *
+     * @return The reporting tasks
+     */
+    Set<ReportingTaskNode> getReportingTasks();
+
+    /**
+     * Updates the specified reporting task.
+     *
+     * @param reportingTaskDTO The reporting task DTO
+     * @return The reporting task
+     */
+    ReportingTaskNode updateReportingTask(ReportingTaskDTO reportingTaskDTO);
+
+    /**
+     * Determines whether this reporting task can be updated.
+     *
+     * @param reportingTaskDTO
+     */
+    void verifyUpdate(ReportingTaskDTO reportingTaskDTO);
+    
+    /**
+     * Determines whether this reporting task can be removed.
+     *
+     * @param reportingTaskId
+     */
+    void verifyDelete(String reportingTaskId);
+
+    /**
+     * Deletes the specified reporting task.
+     *
+     * @param reportingTaskId The reporting task id
+     */
+    void deleteReportingTask(String reportingTaskId);
+}


[53/62] [abbrv] incubator-nifi git commit: 504: Initial import of geo enrich processors

Posted by ma...@apache.org.
504: Initial import of geo enrich processors


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/ff0bd2c6
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/ff0bd2c6
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/ff0bd2c6

Branch: refs/heads/NIFI-25
Commit: ff0bd2c6696e73d3157eb2d0116913e337410f38
Parents: 45416dc
Author: Mark Payne <ma...@hotmail.com>
Authored: Thu Apr 9 17:54:59 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Thu Apr 9 17:54:59 2015 -0400

----------------------------------------------------------------------
 .../nifi-geo-bundle/nifi-geo-nar/pom.xml        |  33 +++
 .../nifi-geo-processors/.gitignore              |   1 +
 .../nifi-geo-bundle/nifi-geo-processors/pom.xml |  43 +++
 .../org/apache/nifi/processors/GeoEnrichIP.java | 210 ++++++++++++++
 .../nifi/processors/maxmind/DatabaseReader.java | 286 +++++++++++++++++++
 .../org.apache.nifi.processor.Processor         |  16 ++
 nifi/nifi-nar-bundles/nifi-geo-bundle/pom.xml   |  42 +++
 7 files changed, 631 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/ff0bd2c6/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-nar/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-nar/pom.xml b/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-nar/pom.xml
new file mode 100644
index 0000000..484e291
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-nar/pom.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-geo-bundle</artifactId>
+        <version>0.1.0-incubating-SNAPSHOT</version>
+    </parent>
+    <artifactId>nifi-geo-nar</artifactId>
+    <packaging>nar</packaging>
+    <description>NiFi Geo Enrichment NAR</description>
+	
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-geo-processors</artifactId>
+        </dependency>
+    </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/ff0bd2c6/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/.gitignore
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/.gitignore b/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/.gitignore
@@ -0,0 +1 @@
+/target/

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/ff0bd2c6/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/pom.xml b/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/pom.xml
new file mode 100644
index 0000000..67bc253
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/pom.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-geo-bundle</artifactId>
+        <version>0.1.0-incubating-SNAPSHOT</version>
+    </parent>
+    <artifactId>nifi-geo-processors</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-processor-utils</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.maxmind.geoip2</groupId>
+            <artifactId>geoip2</artifactId>
+            <version>2.1.0</version>
+        </dependency>        
+    </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/ff0bd2c6/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/src/main/java/org/apache/nifi/processors/GeoEnrichIP.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/src/main/java/org/apache/nifi/processors/GeoEnrichIP.java b/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/src/main/java/org/apache/nifi/processors/GeoEnrichIP.java
new file mode 100644
index 0000000..fed0e7e
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/src/main/java/org/apache/nifi/processors/GeoEnrichIP.java
@@ -0,0 +1,210 @@
+/*
+ * 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.processors;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.annotation.behavior.EventDriven;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.SupportsBatching;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.annotation.lifecycle.OnStopped;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.processors.maxmind.DatabaseReader;
+import org.apache.nifi.util.StopWatch;
+
+import com.maxmind.geoip2.exception.GeoIp2Exception;
+import com.maxmind.geoip2.model.CityResponse;
+import com.maxmind.geoip2.record.Subdivision;
+
+@EventDriven
+@SideEffectFree
+@SupportsBatching
+@Tags({"geo", "enrich", "ip", "maxmind"})
+@CapabilityDescription("Looks up geolocation information for an IP address and adds the geo information to FlowFile attributes. The "
+		+ "geo data is provided as a MaxMind database. The attribute that contains the IP address to lookup is provided by the "
+		+ "'IP Address Attribute' property. If the name of the attribute provided is 'X', then the the attributes added by enrichment "
+		+ "will take the form X.geo.<fieldName>")
+@WritesAttributes({
+	@WritesAttribute(attribute="X.geo.lookup.micros", description="The number of microseconds that the geo lookup took"),
+	@WritesAttribute(attribute="X.geo.city", description="The city identified for the IP address"),
+	@WritesAttribute(attribute="X.geo.latitude", description="The latitude identified for this IP address"),
+	@WritesAttribute(attribute="X.geo.longitude", description="The longitude identified for this IP address"),
+	@WritesAttribute(attribute="X.geo.subdivision.N", description="Each subdivision that is identified for this IP address is added with a one-up number appended to the attribute name, starting with 0"),
+	@WritesAttribute(attribute="X.geo.subdivision.isocode.N", description="The ISO code for the subdivision that is identified by X.geo.subdivision.N"),
+	@WritesAttribute(attribute="X.geo.country", description="The country identified for this IP address"),
+	@WritesAttribute(attribute="X.geo.country.isocode", description="The ISO Code for the country identified"),
+	@WritesAttribute(attribute="X.geo.postalcode", description="The postal code for the country identified"),
+})
+public class GeoEnrichIP extends AbstractProcessor {
+
+    public static final PropertyDescriptor GEO_DATABASE_FILE = new PropertyDescriptor.Builder()
+            .name("Geo Database File")
+            .description("Path to Maxmind Geo Enrichment Database File")
+            .required(true)
+            .addValidator(StandardValidators.FILE_EXISTS_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor IP_ADDRESS_ATTRIBUTE = new PropertyDescriptor.Builder()
+            .name("IP Address Attribute")
+            .required(true)
+            .description("The name of an attribute whose value is a dotted decimal IP address for which enrichment should occur")
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final Relationship REL_FOUND = new Relationship.Builder()
+            .name("found")
+            .description("Where to route flow files after successfully enriching attributes with geo data")
+            .build();
+
+    public static final Relationship REL_NOT_FOUND = new Relationship.Builder()
+            .name("not found")
+            .description("Where to route flow files after unsuccessfully enriching attributes because no geo data was found")
+            .build();
+
+    private Set<Relationship> relationships;
+    private List<PropertyDescriptor> propertyDescriptors;
+    private final AtomicReference<DatabaseReader> databaseReaderRef = new AtomicReference<>(null);
+
+    @Override
+    public Set<Relationship> getRelationships() {
+        return relationships;
+    }
+
+    @Override
+    protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        return propertyDescriptors;
+    }
+
+    @OnScheduled
+    public final void onScheduled(final ProcessContext context) throws IOException {
+        final String dbFileString = context.getProperty(GEO_DATABASE_FILE).getValue();
+        final File dbFile = new File(dbFileString);
+        final StopWatch stopWatch = new StopWatch(true);
+        final DatabaseReader reader = new DatabaseReader.Builder(dbFile).build();
+        stopWatch.stop();
+        getLogger().info("Completed loading of Maxmind Geo Database.  Elapsed time was {} milliseconds.", new Object[]{stopWatch.getDuration(TimeUnit.MILLISECONDS)});
+        databaseReaderRef.set(reader);
+    }
+
+    @OnStopped
+    public void closeReader() throws IOException {
+    	final DatabaseReader reader = databaseReaderRef.get();
+    	if ( reader != null ) {
+    		reader.close();
+    	}
+    }
+    
+    @Override
+    protected void init(final ProcessorInitializationContext context) {
+        final Set<Relationship> rels = new HashSet<>();
+        rels.add(REL_FOUND);
+        rels.add(REL_NOT_FOUND);
+        this.relationships = Collections.unmodifiableSet(rels);
+
+        final List<PropertyDescriptor> props = new ArrayList<>();
+        props.add(GEO_DATABASE_FILE);
+        props.add(IP_ADDRESS_ATTRIBUTE);
+        this.propertyDescriptors = Collections.unmodifiableList(props);
+    }
+
+    @Override
+    public void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException {
+        FlowFile flowFile = session.get();
+        if (flowFile == null) {
+            return;
+        }
+
+        final DatabaseReader dbReader = databaseReaderRef.get();
+        final String ipAttributeName = context.getProperty(IP_ADDRESS_ATTRIBUTE).getValue();
+        final String ipAttributeValue = flowFile.getAttribute(ipAttributeName);
+        if (StringUtils.isEmpty(ipAttributeName)) { //TODO need to add additional validation - should look like an IPv4 or IPv6 addr for instance
+            session.transfer(flowFile, REL_NOT_FOUND);
+            getLogger().warn("Unable to find ip address for {}", new Object[]{flowFile});
+            return;
+        }
+        InetAddress inetAddress = null;
+        CityResponse response = null;
+
+        try {
+            inetAddress = InetAddress.getByName(ipAttributeValue);
+        } catch (final IOException ioe) {
+            session.transfer(flowFile, REL_NOT_FOUND);
+            getLogger().warn("Could not resolve {} to ip address for {}", new Object[]{ipAttributeValue, flowFile}, ioe);
+            return;
+        }
+        final StopWatch stopWatch = new StopWatch(true);
+        try {
+            response = dbReader.city(inetAddress);
+            stopWatch.stop();
+        } catch (final IOException | GeoIp2Exception ex) {
+            session.transfer(flowFile, REL_NOT_FOUND);
+            getLogger().warn("Failure while trying to find enrichment data for {} due to {}", new Object[]{flowFile, ex}, ex);
+            return;
+        }
+        
+        if (response == null) {
+            session.transfer(flowFile, REL_NOT_FOUND);
+            getLogger().warn("No enrichment data found for ip {} of {}", new Object[]{ipAttributeValue, flowFile});
+            return;
+        }
+
+        final Map<String, String> attrs = new HashMap<>();
+        attrs.put(new StringBuilder(ipAttributeName).append(".geo.lookup.micros").toString(), String.valueOf(stopWatch.getDuration(TimeUnit.MICROSECONDS)));
+        attrs.put(new StringBuilder(ipAttributeName).append(".geo.city").toString(), response.getCity().getName());
+        attrs.put(new StringBuilder(ipAttributeName).append(".geo.latitude").toString(), response.getLocation().getLatitude().toString());
+        attrs.put(new StringBuilder(ipAttributeName).append(".geo.longitude").toString(), response.getLocation().getLongitude().toString());
+        int i = 0;
+        for (final Subdivision subd : response.getSubdivisions()) {
+            attrs.put(new StringBuilder(ipAttributeName).append(".geo.subdivision.").append(i).toString(), subd.getName());
+            attrs.put(new StringBuilder(ipAttributeName).append(".geo.subdivision.isocode.").append(i).toString(), subd.getIsoCode());
+            i++;
+        }
+        attrs.put(new StringBuilder(ipAttributeName).append(".geo.country").toString(), response.getCountry().getName());
+        attrs.put(new StringBuilder(ipAttributeName).append(".geo.country.isocode").toString(), response.getCountry().getIsoCode());
+        attrs.put(new StringBuilder(ipAttributeName).append(".geo.postalcode").toString(), response.getPostal().getCode());
+        flowFile = session.putAllAttributes(flowFile, attrs);
+
+        session.transfer(flowFile, REL_FOUND);
+        getLogger().info("Completed lookup of IP geo information for {}", new Object[]{flowFile});
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/ff0bd2c6/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/src/main/java/org/apache/nifi/processors/maxmind/DatabaseReader.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/src/main/java/org/apache/nifi/processors/maxmind/DatabaseReader.java b/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/src/main/java/org/apache/nifi/processors/maxmind/DatabaseReader.java
new file mode 100644
index 0000000..796a7af
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/src/main/java/org/apache/nifi/processors/maxmind/DatabaseReader.java
@@ -0,0 +1,286 @@
+/*
+ * 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.processors.maxmind;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.List;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.InjectableValues;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.maxmind.db.Metadata;
+import com.maxmind.db.Reader;
+import com.maxmind.db.Reader.FileMode;
+import com.maxmind.geoip2.GeoIp2Provider;
+import com.maxmind.geoip2.exception.AddressNotFoundException;
+import com.maxmind.geoip2.exception.GeoIp2Exception;
+import com.maxmind.geoip2.model.AnonymousIpResponse;
+import com.maxmind.geoip2.model.CityResponse;
+import com.maxmind.geoip2.model.ConnectionTypeResponse;
+import com.maxmind.geoip2.model.CountryResponse;
+import com.maxmind.geoip2.model.DomainResponse;
+import com.maxmind.geoip2.model.IspResponse;
+
+/**
+ * <p>
+ * This class was copied from 
+ * https://raw.githubusercontent.com/maxmind/GeoIP2-java/master/src/main/java/com/maxmind/geoip2/DatabaseReader.java
+ * It is written by Maxmind and it is available under Apache Software License V2
+ * Copyright (c) 2013 by MaxMind, Inc.
+ * The modification we're making to the code below is to stop using exceptions for
+ * mainline flow control.  Specifically we don't want to throw an exception
+ * simply because an address was not found.
+ * </p>
+ * 
+ * Instances of this class provide a reader for the GeoIP2 database format. IP
+ * addresses can be looked up using the <code>get</code> method.
+ */
+public class DatabaseReader implements GeoIp2Provider, Closeable {
+
+    private final Reader reader;
+
+    private final ObjectMapper om;
+
+    private DatabaseReader(Builder builder) throws IOException {
+        if (builder.stream != null) {
+            this.reader = new Reader(builder.stream);
+        } else if (builder.database != null) {
+            this.reader = new Reader(builder.database, builder.mode);
+        } else {
+            // This should never happen. If it does, review the Builder class
+            // constructors for errors.
+            throw new IllegalArgumentException(
+                    "Unsupported Builder configuration: expected either File or URL");
+        }
+        this.om = new ObjectMapper();
+        this.om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
+                false);
+        this.om.configure(
+                DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
+        InjectableValues inject = new InjectableValues.Std().addValue(
+                "locales", builder.locales);
+        this.om.setInjectableValues(inject);
+    }
+
+    /**
+     * <p>
+     * Constructs a Builder for the DatabaseReader. The file passed to it must
+     * be a valid GeoIP2 database file.
+     * </p>
+     * <p>
+     * <code>Builder</code> creates instances of <code>DatabaseReader</code>
+     * from values set by the methods.
+     * </p>
+     * <p>
+     * Only the values set in the <code>Builder</code> constructor are required.
+     * </p>
+     */
+    public final static class Builder {
+        final File database;
+        final InputStream stream;
+
+        List<String> locales = Arrays.asList("en");
+        FileMode mode = FileMode.MEMORY_MAPPED;
+
+        /**
+         * @param stream the stream containing the GeoIP2 database to use.
+         */
+        public Builder(InputStream stream) {
+            this.stream = stream;
+            this.database = null;
+        }
+
+        /**
+         * @param database the GeoIP2 database file to use.
+         */
+        public Builder(File database) {
+            this.database = database;
+            this.stream = null;
+        }
+
+        /**
+         * @param val List of locale codes to use in name property from most
+         *            preferred to least preferred.
+         * @return Builder object
+         */
+        public Builder locales(List<String> val) {
+            this.locales = val;
+            return this;
+        }
+
+        /**
+         * @param val The file mode used to open the GeoIP2 database
+         * @return Builder object
+         * @throws java.lang.IllegalArgumentException if you initialized the Builder with a URL, which uses
+         *                                            {@link FileMode#MEMORY}, but you provided a different
+         *                                            FileMode to this method.
+         */
+        public Builder fileMode(FileMode val) {
+            if (this.stream != null && !FileMode.MEMORY.equals(val)) {
+                throw new IllegalArgumentException(
+                        "Only FileMode.MEMORY is supported when using an InputStream.");
+            }
+            this.mode = val;
+            return this;
+        }
+
+        /**
+         * @return an instance of <code>DatabaseReader</code> created from the
+         * fields set on this builder.
+         * @throws IOException if there is an error reading the database
+         */
+        public DatabaseReader build() throws IOException {
+            return new DatabaseReader(this);
+        }
+    }
+
+    /**
+     * @param ipAddress IPv4 or IPv6 address to lookup.
+     * @return A <T> object with the data for the IP address or null if no 
+     * information could be found for the given IP address
+     * @throws IOException              if there is an error opening or reading from the file.
+     */
+    private <T> T get(InetAddress ipAddress, Class<T> cls, boolean hasTraits,
+                      String type) throws IOException, AddressNotFoundException {
+
+        String databaseType = this.getMetadata().getDatabaseType();
+        if (!databaseType.contains(type)) {
+            String caller = Thread.currentThread().getStackTrace()[2]
+                    .getMethodName();
+            throw new UnsupportedOperationException(
+                    "Invalid attempt to open a " + databaseType
+                            + " database using the " + caller + " method");
+        }
+
+        ObjectNode node = (ObjectNode) this.reader.get(ipAddress);
+
+        if (node == null) {
+            return null;
+        }
+
+        ObjectNode ipNode;
+        if (hasTraits) {
+            if (!node.has("traits")) {
+                node.set("traits", this.om.createObjectNode());
+            }
+            ipNode = (ObjectNode) node.get("traits");
+        } else {
+            ipNode = node;
+        }
+        ipNode.put("ip_address", ipAddress.getHostAddress());
+
+        return this.om.treeToValue(node, cls);
+    }
+
+    /**
+     * <p>
+     * Closes the database.
+     * </p>
+     * <p>
+     * If you are using <code>FileMode.MEMORY_MAPPED</code>, this will
+     * <em>not</em> unmap the underlying file due to a limitation in Java's
+     * <code>MappedByteBuffer</code>. It will however set the reference to
+     * the buffer to <code>null</code>, allowing the garbage collector to
+     * collect it.
+     * </p>
+     *
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    public void close() throws IOException {
+        this.reader.close();
+    }
+
+    @Override
+    public CountryResponse country(InetAddress ipAddress) throws IOException,
+            GeoIp2Exception {
+        return this.get(ipAddress, CountryResponse.class, true, "Country");
+    }
+
+    @Override
+    public CityResponse city(InetAddress ipAddress) throws IOException,
+            GeoIp2Exception {
+        return this.get(ipAddress, CityResponse.class, true, "City");
+    }
+
+    /**
+     * Look up an IP address in a GeoIP2 Anonymous IP.
+     *
+     * @param ipAddress IPv4 or IPv6 address to lookup.
+     * @return a AnonymousIpResponse for the requested IP address.
+     * @throws GeoIp2Exception if there is an error looking up the IP
+     * @throws IOException     if there is an IO error
+     */
+    public AnonymousIpResponse anonymousIp(InetAddress ipAddress) throws IOException,
+            GeoIp2Exception {
+        return this.get(ipAddress, AnonymousIpResponse.class, false, "GeoIP2-Anonymous-IP");
+    }
+
+    /**
+     * Look up an IP address in a GeoIP2 Connection Type database.
+     *
+     * @param ipAddress IPv4 or IPv6 address to lookup.
+     * @return a ConnectTypeResponse for the requested IP address.
+     * @throws GeoIp2Exception if there is an error looking up the IP
+     * @throws IOException     if there is an IO error
+     */
+    public ConnectionTypeResponse connectionType(InetAddress ipAddress)
+            throws IOException, GeoIp2Exception {
+        return this.get(ipAddress, ConnectionTypeResponse.class, false,
+                "GeoIP2-Connection-Type");
+    }
+
+    /**
+     * Look up an IP address in a GeoIP2 Domain database.
+     *
+     * @param ipAddress IPv4 or IPv6 address to lookup.
+     * @return a DomainResponse for the requested IP address.
+     * @throws GeoIp2Exception if there is an error looking up the IP
+     * @throws IOException     if there is an IO error
+     */
+    public DomainResponse domain(InetAddress ipAddress) throws IOException,
+            GeoIp2Exception {
+        return this
+                .get(ipAddress, DomainResponse.class, false, "GeoIP2-Domain");
+    }
+
+    /**
+     * Look up an IP address in a GeoIP2 ISP database.
+     *
+     * @param ipAddress IPv4 or IPv6 address to lookup.
+     * @return an IspResponse for the requested IP address.
+     * @throws GeoIp2Exception if there is an error looking up the IP
+     * @throws IOException     if there is an IO error
+     */
+    public IspResponse isp(InetAddress ipAddress) throws IOException,
+            GeoIp2Exception {
+        return this.get(ipAddress, IspResponse.class, false, "GeoIP2-ISP");
+    }
+
+    /**
+     * @return the metadata for the open MaxMind DB file.
+     */
+    public Metadata getMetadata() {
+        return this.reader.getMetadata();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/ff0bd2c6/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor b/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
new file mode 100644
index 0000000..9b1be71
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
@@ -0,0 +1,16 @@
+# 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.
+
+org.apache.nifi.processors.GeoEnrichIP
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/ff0bd2c6/nifi/nifi-nar-bundles/nifi-geo-bundle/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-geo-bundle/pom.xml b/nifi/nifi-nar-bundles/nifi-geo-bundle/pom.xml
new file mode 100644
index 0000000..2dbd32f
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-geo-bundle/pom.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-nar-bundles</artifactId>
+        <version>0.1.0-incubating-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>nifi-geo-bundle</artifactId>
+    <packaging>pom</packaging>
+    <description>NiFi Geo Enrichment Capability Set</description>
+	
+    <modules>
+        <module>nifi-geo-processors</module>
+        <module>nifi-geo-nar</module>
+    </modules>
+	
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.apache.nifi</groupId>
+                <artifactId>nifi-geo-processors</artifactId>
+                <version>0.1.0-incubating-SNAPSHOT</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>


[29/62] [abbrv] incubator-nifi git commit: NIFI-449: - Adding a link to the nfel styles.

Posted by ma...@apache.org.
NIFI-449:
- Adding a link to the nfel styles.

Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/2c352267
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/2c352267
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/2c352267

Branch: refs/heads/NIFI-25
Commit: 2c352267afec2a1a2d7bab731b55837a6f123428
Parents: e287817
Author: Matt Gilman <ma...@gmail.com>
Authored: Wed Apr 1 11:40:17 2015 -0400
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Apr 1 11:40:17 2015 -0400

----------------------------------------------------------------------
 .../src/main/webapp/WEB-INF/jsp/worksheet.jsp                       | 1 +
 1 file changed, 1 insertion(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/2c352267/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp b/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp
index 4a23de4..1d7c17b 100644
--- a/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp
+++ b/nifi/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-ui/src/main/webapp/WEB-INF/jsp/worksheet.jsp
@@ -28,6 +28,7 @@
         <link rel="stylesheet" href="../nifi/js/codemirror/lib/codemirror.css" type="text/css" />
         <link rel="stylesheet" href="../nifi/js/codemirror/addon/hint/show-hint.css" type="text/css" />
         <link rel="stylesheet" href="../nifi/js/jquery/nfeditor/jquery.nfeditor.css" type="text/css" />
+        <link rel="stylesheet" href="../nifi/js/jquery/nfeditor/languages/nfel.css" type="text/css" />
         <link rel="stylesheet" href="../nifi/css/reset.css" type="text/css" />
         <link rel="stylesheet" href="css/main.css" type="text/css" />
         <script type="text/javascript" src="../nifi/js/jquery/jquery-2.1.1.min.js"></script>


[46/62] [abbrv] incubator-nifi git commit: NIFI-495: Fixed handling of FlowFiles if destination full by rolling back session

Posted by ma...@apache.org.
NIFI-495: Fixed handling of FlowFiles if destination full by rolling back session


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/7819afbe
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/7819afbe
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/7819afbe

Branch: refs/heads/NIFI-25
Commit: 7819afbefd980ce68f43093302997024926d9f51
Parents: 8d20b82
Author: Mark Payne <ma...@hotmail.com>
Authored: Wed Apr 8 13:38:33 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Wed Apr 8 13:38:33 2015 -0400

----------------------------------------------------------------------
 .../java/org/apache/nifi/remote/StandardRemoteGroupPort.java    | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/7819afbe/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/StandardRemoteGroupPort.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/StandardRemoteGroupPort.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/StandardRemoteGroupPort.java
index 740e405..69ba0fd 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/StandardRemoteGroupPort.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/StandardRemoteGroupPort.java
@@ -171,6 +171,7 @@ public class StandardRemoteGroupPort extends RemoteGroupPort {
             this.targetRunning.set(false);
             final String message = String.format("%s failed to communicate with %s because the remote instance indicates that the port is not in a valid state", this, url);
             logger.error(message);
+          	session.rollback();
             remoteGroup.getEventReporter().reportEvent(Severity.ERROR, CATEGORY, message);
             return;
         } catch (final UnknownPortException e) {
@@ -178,6 +179,7 @@ public class StandardRemoteGroupPort extends RemoteGroupPort {
             this.targetExists.set(false);
             final String message = String.format("%s failed to communicate with %s because the remote instance indicates that the port no longer exists", this, url);
             logger.error(message);
+          	session.rollback();
             remoteGroup.getEventReporter().reportEvent(Severity.ERROR, CATEGORY, message);
             return;
         } catch (final IOException e) {
@@ -186,13 +188,14 @@ public class StandardRemoteGroupPort extends RemoteGroupPort {
             if ( logger.isDebugEnabled() ) {
                 logger.error("", e);
             }
+          	session.rollback();
             remoteGroup.getEventReporter().reportEvent(Severity.ERROR, CATEGORY, message);
-            session.rollback();
             return;
         }
         
         if ( transaction == null ) {
             logger.debug("{} Unable to create transaction to communicate with; all peers must be penalized, so yielding context", this);
+            session.rollback();
             context.yield();
             return;
         }


[54/62] [abbrv] incubator-nifi git commit: NIFI-505: Initial import of language translation nar

Posted by ma...@apache.org.
NIFI-505: Initial import of language translation nar


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/178c5cd2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/178c5cd2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/178c5cd2

Branch: refs/heads/NIFI-25
Commit: 178c5cd287eed734bd3d93665df27682db941a8b
Parents: ff0bd2c
Author: Mark Payne <ma...@hotmail.com>
Authored: Thu Apr 9 17:55:33 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Thu Apr 9 17:55:33 2015 -0400

----------------------------------------------------------------------
 .../nifi-language-translation-nar/pom.xml       |  36 ++
 .../nifi-yandex-processors/.gitignore           |   1 +
 .../nifi-yandex-processors/pom.xml              |  63 ++++
 .../nifi/processors/yandex/YandexTranslate.java | 325 +++++++++++++++++++
 .../processors/yandex/model/Translation.java    |  52 +++
 .../nifi/processors/yandex/util/Languages.java  |  86 +++++
 .../yandex/util/ObjectMapperResolver.java       |  48 +++
 .../org.apache.nifi.processor.Processor         |  16 +
 .../processors/yandex/TestYandexTranslate.java  | 141 ++++++++
 .../nifi-language-translation-bundle/pom.xml    |  48 +++
 10 files changed, 816 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/178c5cd2/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-language-translation-nar/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-language-translation-nar/pom.xml b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-language-translation-nar/pom.xml
new file mode 100644
index 0000000..4d8b790
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-language-translation-nar/pom.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-language-translation-bundle</artifactId>
+        <version>0.1.0-incubating-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>nifi-language-translation-nar</artifactId>
+    <packaging>nar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-yandex-processors</artifactId>
+            <version>0.1.0-incubating-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/178c5cd2/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/.gitignore
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/.gitignore b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/.gitignore
@@ -0,0 +1 @@
+/target/

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/178c5cd2/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/pom.xml b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/pom.xml
new file mode 100644
index 0000000..a5f9f0e
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/pom.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-language-translation-bundle</artifactId>
+        <version>0.1.0-incubating-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>nifi-yandex-processors</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-processor-utils</artifactId>
+        </dependency>
+        
+        <dependency>
+        	<groupId>com.sun.jersey</groupId>
+        	<artifactId>jersey-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-json</artifactId>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-mock</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/178c5cd2/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/java/org/apache/nifi/processors/yandex/YandexTranslate.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/java/org/apache/nifi/processors/yandex/YandexTranslate.java b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/java/org/apache/nifi/processors/yandex/YandexTranslate.java
new file mode 100644
index 0000000..a5eecc6
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/java/org/apache/nifi/processors/yandex/YandexTranslate.java
@@ -0,0 +1,325 @@
+/*
+ * 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.processors.yandex;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+
+import org.apache.nifi.annotation.behavior.DynamicProperty;
+import org.apache.nifi.annotation.behavior.SupportsBatching;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.annotation.lifecycle.OnStopped;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.ValidationContext;
+import org.apache.nifi.components.ValidationResult;
+import org.apache.nifi.components.Validator;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.InputStreamCallback;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.processors.yandex.model.Translation;
+import org.apache.nifi.processors.yandex.util.Languages;
+import org.apache.nifi.processors.yandex.util.ObjectMapperResolver;
+import org.apache.nifi.stream.io.StreamUtils;
+import org.apache.nifi.util.StopWatch;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.ClientResponse.Status;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+import com.sun.jersey.api.json.JSONConfiguration;
+import com.sun.jersey.core.util.MultivaluedMapImpl;
+
+@SupportsBatching
+@Tags({"yandex", "translate", "translation", "language"})
+@CapabilityDescription("Translates content and attributes from one language to another")
+@WritesAttributes({
+	@WritesAttribute(attribute="yandex.translate.failure.reason", description="If the text cannot be translated, this attribute will be set indicating the reason for the failure"),
+	@WritesAttribute(attribute="language", description="When the translation succeeds, if the content was translated, this attribute will be set indicating the new language of the content")
+})
+@DynamicProperty(name="The name of an attribute to set that will contain the translated text of the value", 
+	value="The value to translate", 
+	supportsExpressionLanguage=true, 
+	description="User-defined properties are used to translate arbitrary text based on attributes.")
+public class YandexTranslate extends AbstractProcessor {
+	
+    public static final PropertyDescriptor KEY = new PropertyDescriptor.Builder()
+    	.name("Yandex API Key")
+        .description("The API Key that is registered with Yandex")
+        .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+        .required(true)
+        .build();
+    public static final PropertyDescriptor SOURCE_LANGUAGE = new PropertyDescriptor.Builder()
+		.name("Input Language")
+		.description("The language of incoming data")
+		.required(true)
+		.defaultValue("sp")
+		.expressionLanguageSupported(true)
+		.addValidator(new LanguageNameValidator())
+		.build();
+    public static final PropertyDescriptor TARGET_LANGUAGE = new PropertyDescriptor.Builder()
+		.name("Target Language")
+		.description("The language to translate the text into")
+		.required(true)
+		.defaultValue("en")
+		.expressionLanguageSupported(true)
+		.addValidator(new LanguageNameValidator())
+		.build();
+    public static final PropertyDescriptor TRANSLATE_CONTENT = new PropertyDescriptor.Builder()
+	    .name("Translate Content")
+	    .description("Specifies whether or not the content should be translated. If false, only the text specified by user-defined properties will be translated.")
+	    .required(true)
+	    .allowableValues("true", "false")
+	    .defaultValue("false")
+	    .build();
+    public static final PropertyDescriptor CHARACTER_SET = new PropertyDescriptor.Builder()
+		.name("Character Set")
+		.description("Specifies the character set of the data to be translated")
+		.required(true)
+		.defaultValue("UTF-8")
+		.expressionLanguageSupported(true)
+		.addValidator(StandardValidators.CHARACTER_SET_VALIDATOR)
+		.build();
+
+    public static final Relationship REL_SUCCESS = new Relationship.Builder()
+	    .name("success")
+	    .description("This relationship is used when the translation is successful")
+	    .build();
+    public static final Relationship REL_COMMS_FAILURE = new Relationship.Builder()
+    	.name("comms.failure")
+    	.description("This relationship is used when the translation fails due to a problem such as a network failure, and for which the translation should be attempted again")
+    	.build();
+    public static final Relationship REL_TRANSLATION_FAILED = new Relationship.Builder()
+    	.name("translation.failure")
+    	.description("This relationship is used if the translation cannot be performed for some reason other than communications failure")
+    	.build();
+
+    private List<PropertyDescriptor> descriptors;
+    private Set<Relationship> relationships;
+    
+    private volatile Client client;
+
+    private static final String URL = "https://translate.yandex.net/api/v1.5/tr.json/translate";
+    
+    @Override
+    protected void init(final ProcessorInitializationContext context) {
+        final List<PropertyDescriptor> descriptors = new ArrayList<PropertyDescriptor>();
+        descriptors.add(KEY);
+        descriptors.add(SOURCE_LANGUAGE);
+        descriptors.add(TARGET_LANGUAGE);
+        descriptors.add(TRANSLATE_CONTENT);
+        descriptors.add(CHARACTER_SET);
+        this.descriptors = Collections.unmodifiableList(descriptors);
+
+        final Set<Relationship> relationships = new HashSet<Relationship>();
+        relationships.add(REL_SUCCESS);
+        relationships.add(REL_COMMS_FAILURE);
+        relationships.add(REL_TRANSLATION_FAILED);
+        this.relationships = Collections.unmodifiableSet(relationships);
+    }
+
+    @Override
+    public Set<Relationship> getRelationships() {
+        return this.relationships;
+    }
+
+    @Override
+    public final List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        return descriptors;
+    }
+    
+    @Override
+    protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(final String propertyDescriptorName) {
+    	return new PropertyDescriptor.Builder()
+    		.name(propertyDescriptorName)
+    		.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+    		.expressionLanguageSupported(true)
+    		.dynamic(true)
+    		.build();
+    }
+
+    @Override
+    protected Collection<ValidationResult> customValidate(final ValidationContext validationContext) {
+    	final List<ValidationResult> results = new ArrayList<>();
+    	if ( validationContext.getProperty(TRANSLATE_CONTENT).asBoolean().equals(Boolean.FALSE) ) {
+    		boolean foundDynamic = false;
+    		for ( final PropertyDescriptor descriptor : validationContext.getProperties().keySet() ) {
+    			if ( descriptor.isDynamic() ) {
+    				foundDynamic = true;
+    				break;
+    			}
+    		}
+    		
+    		if ( !foundDynamic ) {
+    			results.add(new ValidationResult.Builder().subject("Text to translate").input("<none>").valid(false).explanation("Must either set 'Translate Content' to true or add at least one user-defined property").build());
+    		}
+    	}
+    	
+    	return results;
+    }
+    
+    @OnScheduled
+    public void onScheduled(final ProcessContext context) {
+    	final ClientConfig config = new DefaultClientConfig();
+        config.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
+        config.getClasses().add(ObjectMapperResolver.class);
+
+    	client = Client.create(config);
+    }
+
+    @OnStopped
+    public void destroyClient() {
+    	if ( client != null ) {
+    		client.destroy();
+    	}
+    }
+    
+    @Override
+    public void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException {
+		FlowFile flowFile = session.get();
+		if ( flowFile == null ) {
+			return;
+		}
+
+		final StopWatch stopWatch = new StopWatch(true);
+		final String key = context.getProperty(KEY).getValue();
+		final String sourceLanguage = context.getProperty(SOURCE_LANGUAGE).evaluateAttributeExpressions(flowFile).getValue();
+		final String targetLanguage = context.getProperty(TARGET_LANGUAGE).evaluateAttributeExpressions(flowFile).getValue();
+		final String encoding = context.getProperty(CHARACTER_SET).evaluateAttributeExpressions(flowFile).getValue();
+		
+		final List<String> attributeNames = new ArrayList<>();
+		final List<String> textValues = new ArrayList<>();
+		for ( final PropertyDescriptor descriptor : context.getProperties().keySet() ) {
+			if ( descriptor.isDynamic() ) {
+				attributeNames.add(descriptor.getName());	// add to list so that we know the order when the translations come back.
+				textValues.add(context.getProperty(descriptor).evaluateAttributeExpressions(flowFile).getValue());
+			}
+		}
+		
+		if ( context.getProperty(TRANSLATE_CONTENT).asBoolean() ) {
+			final byte[] buff = new byte[(int) flowFile.getSize()];
+			session.read(flowFile, new InputStreamCallback() {
+				@Override
+				public void process(final InputStream in) throws IOException {
+					StreamUtils.fillBuffer(in, buff);
+				}
+			});
+			final String content = new String(buff, Charset.forName(encoding));
+			textValues.add(content);
+		}
+		
+		WebResource webResource = client.resource(URL);
+		
+		final MultivaluedMap<String, String> paramMap = new MultivaluedMapImpl();
+		paramMap.put("text", textValues);
+		paramMap.add("key", key);
+		paramMap.add("lang", sourceLanguage + "-" + targetLanguage);
+		
+		WebResource.Builder builder = webResource
+				.accept(MediaType.APPLICATION_JSON)
+				.type(MediaType.APPLICATION_FORM_URLENCODED);
+		builder = builder.entity(paramMap);
+		
+		final ClientResponse response;
+		try {
+			response = builder.post(ClientResponse.class);
+		} catch (final Exception e) {
+			getLogger().error("Failed to make request to Yandex to transate text for {} due to {}; routing to comms.failure", new Object[] {flowFile, e});
+			session.transfer(flowFile, REL_COMMS_FAILURE);
+			return;
+		}
+		
+		if ( response.getStatus() != Status.OK.getStatusCode() ) {
+			getLogger().error("Failed to translate text using Yandex for {}; response was {}: {}; routing to {}", new Object[] {
+					flowFile, response.getStatus(), response.getStatusInfo().getReasonPhrase(), REL_TRANSLATION_FAILED.getName()});
+			flowFile = session.putAttribute(flowFile, "yandex.translate.failure.reason", response.getStatusInfo().getReasonPhrase());
+			session.transfer(flowFile, REL_TRANSLATION_FAILED);
+			return;
+		}
+		
+		final Map<String, String> newAttributes = new HashMap<>();
+		final Translation translation = response.getEntity(Translation.class);
+		final List<String> texts = translation.getText();
+		for (int i=0; i < texts.size(); i++) {
+			final String text = texts.get(i);
+			if ( i < attributeNames.size() ) {
+				final String attributeName = attributeNames.get(i);
+				newAttributes.put(attributeName, text);
+			} else {
+				flowFile = session.write(flowFile, new OutputStreamCallback() {
+					@Override
+					public void process(final OutputStream out) throws IOException {
+						out.write(text.getBytes(encoding));
+					}
+				});
+				
+				newAttributes.put("language", targetLanguage);
+			}
+		}
+		
+		if ( !newAttributes.isEmpty() ) {
+			flowFile = session.putAllAttributes(flowFile, newAttributes);
+		}
+		
+		stopWatch.stop();
+		session.transfer(flowFile, REL_SUCCESS);
+		getLogger().info("Successfully translated {} items for {} from {} to {} in {}; routing to success", new Object[] {texts.size(), flowFile, sourceLanguage, targetLanguage, stopWatch.getDuration()});
+    }
+
+    
+    private static class LanguageNameValidator implements Validator {
+    	
+		@Override
+		public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
+			if ( context.isExpressionLanguagePresent(input) ) {
+				return new ValidationResult.Builder().subject(subject).input(input).valid(true).explanation("Expression Language Present").build();
+			}
+
+			if ( Languages.getLanguageMap().keySet().contains(input.toLowerCase()) ) {
+				return new ValidationResult.Builder().subject(subject).input(input).valid(true).build();
+			}
+			
+			return new ValidationResult.Builder().subject(subject).input(input).valid(false).explanation(input + " is not a language that is supported by Yandex").build();
+		}
+    	
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/178c5cd2/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/java/org/apache/nifi/processors/yandex/model/Translation.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/java/org/apache/nifi/processors/yandex/model/Translation.java b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/java/org/apache/nifi/processors/yandex/model/Translation.java
new file mode 100644
index 0000000..eeb7c3f
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/java/org/apache/nifi/processors/yandex/model/Translation.java
@@ -0,0 +1,52 @@
+/*
+ * 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.processors.yandex.model;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "translation")
+public class Translation {
+	private int code;
+	private String lang;
+	private List<String> text;
+	
+	public int getCode() {
+		return code;
+	}
+	
+	public void setCode(final int code) {
+		this.code = code;
+	}
+	
+	public String getLang() {
+		return lang;
+	}
+	
+	public void setLang(final String lang) {
+		this.lang = lang;
+	}
+	
+	public List<String> getText() {
+		return text;
+	}
+	
+	public void setText(final List<String> text) {
+		this.text = text;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/178c5cd2/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/java/org/apache/nifi/processors/yandex/util/Languages.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/java/org/apache/nifi/processors/yandex/util/Languages.java b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/java/org/apache/nifi/processors/yandex/util/Languages.java
new file mode 100644
index 0000000..791d6a3
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/java/org/apache/nifi/processors/yandex/util/Languages.java
@@ -0,0 +1,86 @@
+/*
+ * 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.processors.yandex.util;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Languages {
+	private static final Map<String, String> languageAbbreviationMap = new HashMap<>();
+	
+	static {
+		languageAbbreviationMap.put("ar", "arabic");
+		languageAbbreviationMap.put("az", "azerbaijani");
+		languageAbbreviationMap.put("be", "belarusian");
+		languageAbbreviationMap.put("bg", "bulgarian");
+		languageAbbreviationMap.put("bs", "bosnian");
+		languageAbbreviationMap.put("ca", "catalan");
+		languageAbbreviationMap.put("cs", "czech");
+		languageAbbreviationMap.put("da", "danish");
+		languageAbbreviationMap.put("de", "german");
+		languageAbbreviationMap.put("el", "greek");
+		languageAbbreviationMap.put("en", "english");
+		languageAbbreviationMap.put("es", "spanish");
+		languageAbbreviationMap.put("et", "estonian");
+		languageAbbreviationMap.put("fi", "finnish");
+		languageAbbreviationMap.put("fr", "french");
+		languageAbbreviationMap.put("he", "hebrew");
+		languageAbbreviationMap.put("hr", "croatian");
+		languageAbbreviationMap.put("hu", "hungarian");
+		languageAbbreviationMap.put("hy", "armenian");
+		languageAbbreviationMap.put("id", "indonesian");
+		languageAbbreviationMap.put("is", "icelandic");
+		languageAbbreviationMap.put("it", "italian");
+		languageAbbreviationMap.put("ja", "japanese");
+		languageAbbreviationMap.put("ka", "georgian");
+		languageAbbreviationMap.put("ko", "korean");
+		languageAbbreviationMap.put("lt", "lithuanian");
+		languageAbbreviationMap.put("lv", "latvian");
+		languageAbbreviationMap.put("mk", "macedonian");
+		languageAbbreviationMap.put("ms", "malay");
+		languageAbbreviationMap.put("mt", "maltese");
+		languageAbbreviationMap.put("nl", "dutch");
+		languageAbbreviationMap.put("no", "norwegian");
+		languageAbbreviationMap.put("pl", "polish");
+		languageAbbreviationMap.put("pt", "portuguese");
+		languageAbbreviationMap.put("ro", "romanian");
+		languageAbbreviationMap.put("ru", "russian");
+		languageAbbreviationMap.put("sk", "slovak");
+		languageAbbreviationMap.put("sl", "slovenian");
+		languageAbbreviationMap.put("sq", "albanian");
+		languageAbbreviationMap.put("sr", "serbian");
+		languageAbbreviationMap.put("sv", "swedish");
+		languageAbbreviationMap.put("th", "thai");
+		languageAbbreviationMap.put("tr", "turkish");
+		languageAbbreviationMap.put("uk", "ukrainian");
+		languageAbbreviationMap.put("vi", "vietnamese");
+		languageAbbreviationMap.put("zh", "chinese");
+		
+		final Map<String, String> reverseMap = new HashMap<>();
+		for ( final Map.Entry<String, String> entry : languageAbbreviationMap.entrySet() ) {
+			reverseMap.put(entry.getValue(), entry.getKey());
+		}
+		
+		languageAbbreviationMap.putAll(reverseMap);
+	}
+	
+	
+	public static Map<String, String> getLanguageMap() {
+		return Collections.unmodifiableMap(languageAbbreviationMap);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/178c5cd2/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/java/org/apache/nifi/processors/yandex/util/ObjectMapperResolver.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/java/org/apache/nifi/processors/yandex/util/ObjectMapperResolver.java b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/java/org/apache/nifi/processors/yandex/util/ObjectMapperResolver.java
new file mode 100644
index 0000000..085cf7f
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/java/org/apache/nifi/processors/yandex/util/ObjectMapperResolver.java
@@ -0,0 +1,48 @@
+/*
+ * 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.processors.yandex.util;
+
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+import org.codehaus.jackson.map.AnnotationIntrospector;
+import org.codehaus.jackson.map.DeserializationConfig;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.SerializationConfig;
+import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
+import org.codehaus.jackson.xc.JaxbAnnotationIntrospector;
+
+@Provider
+public class ObjectMapperResolver implements ContextResolver<ObjectMapper> {
+
+    private final ObjectMapper mapper;
+
+    public ObjectMapperResolver() throws Exception {
+        mapper = new ObjectMapper();
+
+        final AnnotationIntrospector jaxbIntrospector = new JaxbAnnotationIntrospector();
+        final SerializationConfig serializationConfig = mapper.getSerializationConfig();
+        final DeserializationConfig deserializationConfig = mapper.getDeserializationConfig();
+
+        mapper.setSerializationConfig(serializationConfig.withSerializationInclusion(Inclusion.NON_NULL).withAnnotationIntrospector(jaxbIntrospector));
+        mapper.setDeserializationConfig(deserializationConfig.withAnnotationIntrospector(jaxbIntrospector));
+    }
+
+    @Override
+    public ObjectMapper getContext(Class<?> objectType) {
+        return mapper;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/178c5cd2/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
new file mode 100644
index 0000000..dc81439
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
@@ -0,0 +1,16 @@
+# 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.
+
+org.apache.nifi.processors.yandex.YandexTranslate
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/178c5cd2/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/test/java/org/apache/nifi/processors/yandex/TestYandexTranslate.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/test/java/org/apache/nifi/processors/yandex/TestYandexTranslate.java b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/test/java/org/apache/nifi/processors/yandex/TestYandexTranslate.java
new file mode 100644
index 0000000..4f82049
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/src/test/java/org/apache/nifi/processors/yandex/TestYandexTranslate.java
@@ -0,0 +1,141 @@
+/*
+ * 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.processors.yandex;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.apache.nifi.processors.yandex.YandexTranslate;
+import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.util.TestRunner;
+import org.apache.nifi.util.TestRunners;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+@Ignore("For local testing only; requires local file to be populated with Yandex API Key")
+public class TestYandexTranslate {
+
+    private TestRunner testRunner;
+    private String apiKey;
+
+    @Before
+    public void init() throws IOException {
+        testRunner = TestRunners.newTestRunner(YandexTranslate.class);
+        
+        final Properties properties = new Properties();
+        try (final InputStream in = new FileInputStream(new File("C:/dev/notes/yandex-info.txt"))) {
+        	properties.load(in);
+        }
+        apiKey = properties.getProperty("api_key").trim();
+    }
+
+    
+    @Test
+    public void testTranslateContent() {
+    	testRunner.setProperty(YandexTranslate.KEY, apiKey);
+    	testRunner.setProperty(YandexTranslate.SOURCE_LANGUAGE, "fr");
+    	testRunner.setProperty(YandexTranslate.TARGET_LANGUAGE, "en");
+    	testRunner.setProperty(YandexTranslate.TRANSLATE_CONTENT, "true");
+    	testRunner.setProperty(YandexTranslate.CHARACTER_SET, "UTF-8");
+    	
+    	testRunner.enqueue("bonjour".getBytes());
+    	testRunner.run();
+    	
+    	testRunner.assertAllFlowFilesTransferred(YandexTranslate.REL_SUCCESS, 1);
+    	final MockFlowFile out = testRunner.getFlowFilesForRelationship(YandexTranslate.REL_SUCCESS).get(0);
+    	
+    	final String outText = new String(out.toByteArray());
+    	assertEquals("hello", outText);
+    }
+
+    
+    @Test
+    public void testTranslateSingleAttribute() {
+    	testRunner.setProperty(YandexTranslate.KEY, apiKey);
+    	testRunner.setProperty(YandexTranslate.SOURCE_LANGUAGE, "fr");
+    	testRunner.setProperty(YandexTranslate.TARGET_LANGUAGE, "en");
+    	testRunner.setProperty(YandexTranslate.TRANSLATE_CONTENT, "false");
+    	testRunner.setProperty(YandexTranslate.CHARACTER_SET, "UTF-8");
+    	testRunner.setProperty("translated", "bonjour");
+    	
+    	testRunner.enqueue(new byte[0]);
+    	testRunner.run();
+    	
+    	testRunner.assertAllFlowFilesTransferred(YandexTranslate.REL_SUCCESS, 1);
+    	final MockFlowFile out = testRunner.getFlowFilesForRelationship(YandexTranslate.REL_SUCCESS).get(0);
+    	
+    	assertEquals(0, out.toByteArray().length);
+    	out.assertAttributeEquals("translated", "hello");
+    }
+    
+    @Test
+    public void testTranslateMultipleAttributes() {
+    	testRunner.setProperty(YandexTranslate.KEY, apiKey);
+    	testRunner.setProperty(YandexTranslate.SOURCE_LANGUAGE, "fr");
+    	testRunner.setProperty(YandexTranslate.TARGET_LANGUAGE, "en");
+    	testRunner.setProperty(YandexTranslate.TRANSLATE_CONTENT, "false");
+    	testRunner.setProperty(YandexTranslate.CHARACTER_SET, "UTF-8");
+    	testRunner.setProperty("hello", "bonjour");
+    	testRunner.setProperty("translate", "traduire");
+    	testRunner.setProperty("fun", "amusant");
+    	
+    	testRunner.enqueue(new byte[0]);
+    	testRunner.run();
+    	
+    	testRunner.assertAllFlowFilesTransferred(YandexTranslate.REL_SUCCESS, 1);
+    	final MockFlowFile out = testRunner.getFlowFilesForRelationship(YandexTranslate.REL_SUCCESS).get(0);
+    	
+    	assertEquals(0, out.toByteArray().length);
+    	out.assertAttributeEquals("hello", "hello");
+    	out.assertAttributeEquals("translate", "translate");
+    	out.assertAttributeEquals("fun", "fun");
+    }
+
+    
+    @Test
+    public void testTranslateContentAndMultipleAttributes() {
+    	testRunner.setProperty(YandexTranslate.KEY, apiKey);
+    	testRunner.setProperty(YandexTranslate.SOURCE_LANGUAGE, "fr");
+    	testRunner.setProperty(YandexTranslate.TARGET_LANGUAGE, "en");
+    	testRunner.setProperty(YandexTranslate.TRANSLATE_CONTENT, "true");
+    	testRunner.setProperty(YandexTranslate.CHARACTER_SET, "UTF-8");
+    	testRunner.setProperty("hello", "bonjour");
+    	testRunner.setProperty("translate", "traduire");
+    	testRunner.setProperty("fun", "amusant");
+    	testRunner.setProperty("nifi", "nifi");
+    	
+    	testRunner.enqueue("ordinateur".getBytes());
+    	testRunner.run();
+    	
+    	testRunner.assertAllFlowFilesTransferred(YandexTranslate.REL_SUCCESS, 1);
+    	final MockFlowFile out = testRunner.getFlowFilesForRelationship(YandexTranslate.REL_SUCCESS).get(0);
+    	
+    	out.assertContentEquals("computer");
+    	
+    	out.assertAttributeEquals("hello", "hello");
+    	out.assertAttributeEquals("translate", "translate");
+    	out.assertAttributeEquals("fun", "fun");
+    	out.assertAttributeEquals("nifi", "nifi");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/178c5cd2/nifi/nifi-nar-bundles/nifi-language-translation-bundle/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-language-translation-bundle/pom.xml b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/pom.xml
new file mode 100644
index 0000000..43573e7
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-language-translation-bundle/pom.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-nar-bundles</artifactId>
+        <version>0.1.0-incubating-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>nifi-language-translation-bundle</artifactId>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>nifi-yandex-processors</module>
+        <module>nifi-language-translation-nar</module>
+    </modules>
+
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>com.sun.jersey</groupId>
+				<artifactId>jersey-client</artifactId>
+				<version>${jersey.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>com.sun.jersey</groupId>
+				<artifactId>jersey-json</artifactId>
+				<version>${jersey.version}</version>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
+	
+</project>


[58/62] [abbrv] incubator-nifi git commit: NIFI-244: Fixed copy/paste error that called out website as http://http://... instead of just http://...

Posted by ma...@apache.org.
NIFI-244: Fixed copy/paste error that called out website as http://http://... instead of just http://...


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/10d002c7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/10d002c7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/10d002c7

Branch: refs/heads/NIFI-25
Commit: 10d002c78a7fbd31669d840199f3138d715d6189
Parents: b682b6f
Author: Mark Payne <ma...@hotmail.com>
Authored: Fri Apr 10 08:40:22 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Fri Apr 10 08:40:22 2015 -0400

----------------------------------------------------------------------
 nifi/nifi-assembly/NOTICE | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/10d002c7/nifi/nifi-assembly/NOTICE
----------------------------------------------------------------------
diff --git a/nifi/nifi-assembly/NOTICE b/nifi/nifi-assembly/NOTICE
index d95e2ff..9da37ae 100644
--- a/nifi/nifi-assembly/NOTICE
+++ b/nifi/nifi-assembly/NOTICE
@@ -605,8 +605,8 @@ Mozilla Public License v1.1
 
 The following binary components are provided under the Mozilla Public License v1.1.  See project link for details.
 
-    (MPL 1.1) HAPI Base (ca.uhn.hapi:hapi-base:2.2 - http://http://hl7api.sourceforge.net/)
-    (MPL 1.1) HAPI Structures (ca.uhn.hapi:hapi-structures-v*:2.2 - http://http://hl7api.sourceforge.net/)
+    (MPL 1.1) HAPI Base (ca.uhn.hapi:hapi-base:2.2 - http://hl7api.sourceforge.net/)
+    (MPL 1.1) HAPI Structures (ca.uhn.hapi:hapi-structures-v*:2.2 - http://hl7api.sourceforge.net/)
 
 *****************
 Public Domain


[11/62] [abbrv] incubator-nifi git commit: Squashed commit of the following:

Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardControllerServiceDAO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardControllerServiceDAO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardControllerServiceDAO.java
new file mode 100644
index 0000000..14217c5
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardControllerServiceDAO.java
@@ -0,0 +1,320 @@
+/*
+ * 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.web.dao.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.nifi.controller.ScheduledState;
+import org.apache.nifi.controller.exception.ControllerServiceInstantiationException;
+
+import org.apache.nifi.controller.exception.ValidationException;
+import org.apache.nifi.controller.service.ControllerServiceNode;
+import org.apache.nifi.controller.service.ControllerServiceProvider;
+import org.apache.nifi.controller.service.ControllerServiceReference;
+import org.apache.nifi.controller.service.ControllerServiceState;
+import org.apache.nifi.web.NiFiCoreException;
+import org.apache.nifi.web.ResourceNotFoundException;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
+import org.apache.nifi.web.dao.ControllerServiceDAO;
+
+public class StandardControllerServiceDAO extends ComponentDAO implements ControllerServiceDAO {
+
+    private ControllerServiceProvider serviceProvider;
+
+    /**
+     * Locates the specified controller service.
+     *
+     * @param controllerServiceId
+     * @return
+     */
+    private ControllerServiceNode locateControllerService(final String controllerServiceId) {
+        // get the controller service
+        final ControllerServiceNode controllerService = serviceProvider.getControllerServiceNode(controllerServiceId);
+
+        // ensure the controller service exists
+        if (controllerService == null) {
+            throw new ResourceNotFoundException(String.format("Unable to locate controller service with id '%s'.", controllerServiceId));
+        }
+
+        return controllerService;
+    }
+
+    /**
+     * Creates a controller service.
+     *
+     * @param controllerServiceDTO The controller service DTO
+     * @return The controller service
+     */
+    @Override
+    public ControllerServiceNode createControllerService(final ControllerServiceDTO controllerServiceDTO) {
+        // ensure the type is specified
+        if (controllerServiceDTO.getType() == null) {
+            throw new IllegalArgumentException("The controller service type must be specified.");
+        }
+        
+        try {
+            // create the controller service
+            final ControllerServiceNode controllerService = serviceProvider.createControllerService(controllerServiceDTO.getType(), controllerServiceDTO.getId(), true);
+
+            // ensure we can perform the update 
+            verifyUpdate(controllerService, controllerServiceDTO);
+
+            // perform the update
+            configureControllerService(controllerService, controllerServiceDTO);
+
+            return controllerService;
+        } catch (final ControllerServiceInstantiationException csie) {
+            throw new NiFiCoreException(csie.getMessage(), csie);
+        }
+    }
+
+    /**
+     * Gets the specified controller service.
+     *
+     * @param controllerServiceId The controller service id
+     * @return The controller service
+     */
+    @Override
+    public ControllerServiceNode getControllerService(final String controllerServiceId) {
+        return locateControllerService(controllerServiceId);
+    }
+
+    /**
+     * Determines if the specified controller service exists.
+     *
+     * @param controllerServiceId
+     * @return
+     */
+    @Override
+    public boolean hasControllerService(final String controllerServiceId) {
+        return serviceProvider.getControllerServiceNode(controllerServiceId) != null;
+    }
+
+    /**
+     * Gets all of the controller services.
+     *
+     * @return The controller services
+     */
+    @Override
+    public Set<ControllerServiceNode> getControllerServices() {
+        return serviceProvider.getAllControllerServices();
+    }
+
+    /**
+     * Updates the specified controller service.
+     *
+     * @param controllerServiceDTO The controller service DTO
+     * @return The controller service
+     */
+    @Override
+    public ControllerServiceNode updateControllerService(final ControllerServiceDTO controllerServiceDTO) {
+        // get the controller service
+        final ControllerServiceNode controllerService = locateControllerService(controllerServiceDTO.getId());
+        
+        // ensure we can perform the update 
+        verifyUpdate(controllerService, controllerServiceDTO);
+        
+        // perform the update
+        configureControllerService(controllerService, controllerServiceDTO);
+
+        // enable or disable as appropriate
+        if (isNotNull(controllerServiceDTO.getState())) {
+            final ControllerServiceState purposedControllerServiceState = ControllerServiceState.valueOf(controllerServiceDTO.getState());
+
+            // only attempt an action if it is changing
+            if (!purposedControllerServiceState.equals(controllerService.getState())) {
+                if (ControllerServiceState.ENABLED.equals(purposedControllerServiceState)) {
+                    serviceProvider.enableControllerService(controllerService);
+                } else if (ControllerServiceState.DISABLED.equals(purposedControllerServiceState)) {
+                    serviceProvider.disableControllerService(controllerService);
+                }
+            }
+        }
+        
+        return controllerService;
+    }
+
+    @Override
+    public ControllerServiceReference updateControllerServiceReferencingComponents(final String controllerServiceId, final ScheduledState scheduledState, final ControllerServiceState controllerServiceState) {
+        // get the controller service
+        final ControllerServiceNode controllerService = locateControllerService(controllerServiceId);
+        
+        // this request is either acting upon referncing services or schedulable components
+        if (controllerServiceState != null) {
+            if (ControllerServiceState.ENABLED.equals(controllerServiceState)) {
+                serviceProvider.enableReferencingServices(controllerService);
+            } else {
+                serviceProvider.disableReferencingServices(controllerService);
+            }
+        } else if (scheduledState != null) {
+            if (ScheduledState.RUNNING.equals(scheduledState)) {
+                serviceProvider.scheduleReferencingComponents(controllerService);
+            } else {
+                serviceProvider.unscheduleReferencingComponents(controllerService);
+            }
+        }
+        
+        return controllerService.getReferences();
+    }
+
+    /**
+     * Validates the specified configuration for the specified controller service.
+     * 
+     * @param controllerService
+     * @param controllerServiceDTO
+     * @return 
+     */
+    private List<String> validateProposedConfiguration(final ControllerServiceNode controllerService, final ControllerServiceDTO controllerServiceDTO) {
+        final List<String> validationErrors = new ArrayList<>();
+        return validationErrors;
+    }
+    
+    @Override
+    public void verifyDelete(final String controllerServiceId) {
+        final ControllerServiceNode controllerService = locateControllerService(controllerServiceId);
+        controllerService.verifyCanDelete();
+    }
+
+    @Override
+    public void verifyUpdate(final ControllerServiceDTO controllerServiceDTO) {
+        final ControllerServiceNode controllerService = locateControllerService(controllerServiceDTO.getId());
+        verifyUpdate(controllerService, controllerServiceDTO);
+    }
+
+    @Override
+    public void verifyUpdateReferencingComponents(String controllerServiceId, ScheduledState scheduledState, ControllerServiceState controllerServiceState) {
+        final ControllerServiceNode controllerService = locateControllerService(controllerServiceId);
+        
+        if (controllerServiceState != null) {
+            if (ControllerServiceState.ENABLED.equals(controllerServiceState)) {
+                serviceProvider.verifyCanEnableReferencingServices(controllerService);
+            } else {
+                serviceProvider.verifyCanDisableReferencingServices(controllerService);
+            }
+        } else if (scheduledState != null) {
+            if (ScheduledState.RUNNING.equals(scheduledState)) {
+                serviceProvider.verifyCanScheduleReferencingComponents(controllerService);
+            } else {
+                serviceProvider.verifyCanStopReferencingComponents(controllerService);
+            }
+        }
+    }
+    
+    /**
+     * Verifies the controller service can be updated.
+     * 
+     * @param controllerService
+     * @param controllerServiceDTO 
+     */
+    private void verifyUpdate(final ControllerServiceNode controllerService, final ControllerServiceDTO controllerServiceDTO) {
+        // validate the new controller service state if appropriate
+        if (isNotNull(controllerServiceDTO.getState())) {
+            try {
+                // attempt to parse the service state
+                final ControllerServiceState purposedControllerServiceState = ControllerServiceState.valueOf(controllerServiceDTO.getState());
+                
+                // ensure the state is valid
+                if (ControllerServiceState.ENABLING.equals(purposedControllerServiceState) || ControllerServiceState.DISABLING.equals(purposedControllerServiceState)) {
+                    throw new IllegalArgumentException();
+                }
+                
+                // only attempt an action if it is changing
+                if (!purposedControllerServiceState.equals(controllerService.getState())) {
+                    if (ControllerServiceState.ENABLED.equals(purposedControllerServiceState)) {
+                        controllerService.verifyCanEnable();
+                    } else if (ControllerServiceState.DISABLED.equals(purposedControllerServiceState)) {
+                        controllerService.verifyCanDisable();
+                    }
+                }
+            } catch (IllegalArgumentException iae) {
+                throw new IllegalArgumentException("Controller Service state: Value must be one of [ENABLED, DISABLED]");
+            }
+        }
+        
+        boolean modificationRequest = false;
+        if (isAnyNotNull(controllerServiceDTO.getName(),
+                controllerServiceDTO.getAnnotationData(),
+                controllerServiceDTO.getComments(),
+                controllerServiceDTO.getProperties())) {
+            modificationRequest = true;
+            
+            // validate the request
+            final List<String> requestValidation = validateProposedConfiguration(controllerService, controllerServiceDTO);
+
+            // ensure there was no validation errors
+            if (!requestValidation.isEmpty()) {
+                throw new ValidationException(requestValidation);
+            }
+        }
+        
+        if (modificationRequest) {
+            controllerService.verifyCanUpdate();
+        }
+    }
+    
+    /**
+     * Configures the specified controller service.
+     * 
+     * @param controllerService
+     * @param controllerServiceDTO 
+     */
+    private void configureControllerService(final ControllerServiceNode controllerService, final ControllerServiceDTO controllerServiceDTO) {
+        final String name = controllerServiceDTO.getName();
+        final String annotationData = controllerServiceDTO.getAnnotationData();
+        final String comments = controllerServiceDTO.getComments();
+        final Map<String, String> properties = controllerServiceDTO.getProperties();
+        
+        if (isNotNull(name)) {
+            controllerService.setName(name);
+        }
+        if (isNotNull(annotationData)) {
+            controllerService.setAnnotationData(annotationData);
+        }
+        if (isNotNull(comments)) {
+            controllerService.setComments(comments);
+        }
+        if (isNotNull(properties)) {
+            for (final Map.Entry<String, String> entry : properties.entrySet()) {
+                final String propName = entry.getKey();
+                final String propVal = entry.getValue();
+                if (isNotNull(propName) && propVal == null) {
+                    controllerService.removeProperty(propName);
+                } else if (isNotNull(propName)) {
+                    controllerService.setProperty(propName, propVal);
+                }
+            }
+        }
+    }
+    
+    /**
+     * Deletes the specified controller service.
+     *
+     * @param controllerServiceId The controller service id
+     */
+    @Override
+    public void deleteControllerService(String controllerServiceId) {
+        final ControllerServiceNode controllerService = locateControllerService(controllerServiceId);
+        serviceProvider.removeControllerService(controllerService);
+    }
+
+    /* setters */
+    public void setServiceProvider(ControllerServiceProvider serviceProvider) {
+        this.serviceProvider = serviceProvider;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessorDAO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessorDAO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessorDAO.java
index 633f8e2..0c587fe 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessorDAO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardProcessorDAO.java
@@ -99,6 +99,11 @@ public class StandardProcessorDAO extends ComponentDAO implements ProcessorDAO {
         if (processorDTO.getParentGroupId() != null && !flowController.areGroupsSame(groupId, processorDTO.getParentGroupId())) {
             throw new IllegalArgumentException("Cannot specify a different Parent Group ID than the Group to which the Processor is being added.");
         }
+        
+        // ensure the type is specified
+        if (processorDTO.getType() == null) {
+            throw new IllegalArgumentException("The processor type must be specified.");
+        }
 
         // get the group to add the processor to
         ProcessGroup group = locateProcessGroup(flowController, groupId);

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardReportingTaskDAO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardReportingTaskDAO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardReportingTaskDAO.java
new file mode 100644
index 0000000..d9fd74c
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardReportingTaskDAO.java
@@ -0,0 +1,365 @@
+/*
+ * 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.web.dao.impl;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.regex.Matcher;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.controller.ReportingTaskNode;
+import org.apache.nifi.controller.ScheduledState;
+import org.apache.nifi.controller.exception.ProcessorLifeCycleException;
+
+import org.apache.nifi.controller.exception.ValidationException;
+import org.apache.nifi.controller.reporting.ReportingTaskInstantiationException;
+import org.apache.nifi.controller.reporting.ReportingTaskProvider;
+import org.apache.nifi.scheduling.SchedulingStrategy;
+import org.apache.nifi.util.FormatUtils;
+import org.apache.nifi.web.NiFiCoreException;
+import org.apache.nifi.web.ResourceNotFoundException;
+import org.apache.nifi.web.api.dto.ReportingTaskDTO;
+import org.apache.nifi.web.dao.ReportingTaskDAO;
+import org.quartz.CronExpression;
+
+public class StandardReportingTaskDAO extends ComponentDAO implements ReportingTaskDAO {
+
+    private ReportingTaskProvider reportingTaskProvider;
+
+    /**
+     * Locates the specified reporting task.
+     *
+     * @param reportingTaskId
+     * @return
+     */
+    private ReportingTaskNode locateReportingTask(final String reportingTaskId) {
+        // get the reporting task
+        final ReportingTaskNode reportingTask = reportingTaskProvider.getReportingTaskNode(reportingTaskId);
+
+        // ensure the reporting task exists
+        if (reportingTask == null) {
+            throw new ResourceNotFoundException(String.format("Unable to locate reporting task with id '%s'.", reportingTaskId));
+        }
+
+        return reportingTask;
+    }
+
+    /**
+     * Creates a reporting task.
+     *
+     * @param reportingTaskDTO The reporting task DTO
+     * @return The reporting task
+     */
+    @Override
+    public ReportingTaskNode createReportingTask(final ReportingTaskDTO reportingTaskDTO) {
+        // ensure the type is specified
+        if (reportingTaskDTO.getType() == null) {
+            throw new IllegalArgumentException("The reporting task type must be specified.");
+        }
+        
+        try {
+            // create the reporting task
+            final ReportingTaskNode reportingTask = reportingTaskProvider.createReportingTask(reportingTaskDTO.getType(), reportingTaskDTO.getId(), true);
+
+            // ensure we can perform the update 
+            verifyUpdate(reportingTask, reportingTaskDTO);
+
+            // perform the update
+            configureReportingTask(reportingTask, reportingTaskDTO);
+
+            return reportingTask;
+        } catch (ReportingTaskInstantiationException rtie) {
+            throw new NiFiCoreException(rtie.getMessage(), rtie);
+        }
+    }
+
+    /**
+     * Gets the specified reporting task.
+     *
+     * @param reportingTaskId The reporting task id
+     * @return The reporting task
+     */
+    @Override
+    public ReportingTaskNode getReportingTask(final String reportingTaskId) {
+        return locateReportingTask(reportingTaskId);
+    }
+
+    /**
+     * Determines if the specified reporting task exists.
+     *
+     * @param reportingTaskId
+     * @return
+     */
+    @Override
+    public boolean hasReportingTask(final String reportingTaskId) {
+        return reportingTaskProvider.getReportingTaskNode(reportingTaskId) != null;
+    }
+
+    /**
+     * Gets all of the reporting tasks.
+     *
+     * @return The reporting tasks
+     */
+    @Override
+    public Set<ReportingTaskNode> getReportingTasks() {
+        return reportingTaskProvider.getAllReportingTasks();
+    }
+
+    /**
+     * Updates the specified reporting task.
+     *
+     * @param reportingTaskDTO The reporting task DTO
+     * @return The reporting task
+     */
+    @Override
+    public ReportingTaskNode updateReportingTask(final ReportingTaskDTO reportingTaskDTO) {
+        // get the reporting task
+        final ReportingTaskNode reportingTask = locateReportingTask(reportingTaskDTO.getId());
+
+        // ensure we can perform the update 
+        verifyUpdate(reportingTask, reportingTaskDTO);
+
+        // perform the update
+        configureReportingTask(reportingTask, reportingTaskDTO);
+
+        // configure scheduled state
+        // see if an update is necessary
+        if (isNotNull(reportingTaskDTO.getState())) {
+            final ScheduledState purposedScheduledState = ScheduledState.valueOf(reportingTaskDTO.getState());
+
+            // only attempt an action if it is changing
+            if (!purposedScheduledState.equals(reportingTask.getScheduledState())) {
+                try {
+                    // perform the appropriate action
+                    switch (purposedScheduledState) {
+                        case RUNNING:
+                            reportingTaskProvider.startReportingTask(reportingTask);
+                            break;
+                        case STOPPED:
+                            switch (reportingTask.getScheduledState()) {
+                                case RUNNING:
+                                    reportingTaskProvider.stopReportingTask(reportingTask);
+                                    break;
+                                case DISABLED:
+                                    reportingTaskProvider.enableReportingTask(reportingTask);
+                                    break;
+                            }
+                            break;
+                        case DISABLED:
+                            reportingTaskProvider.disableReportingTask(reportingTask);
+                            break;
+                    }
+                } catch (IllegalStateException | ProcessorLifeCycleException ise) {
+                    throw new NiFiCoreException(ise.getMessage(), ise);
+                } catch (RejectedExecutionException ree) {
+                    throw new NiFiCoreException("Unable to schedule all tasks for the specified reporting task.", ree);
+                } catch (NullPointerException npe) {
+                    throw new NiFiCoreException("Unable to update reporting task run state.", npe);
+                } catch (Exception e) {
+                    throw new NiFiCoreException("Unable to update reporting task run state: " + e, e);
+                }
+            }
+        }
+
+        return reportingTask;
+    }
+
+    /**
+     * Validates the specified configuration for the specified reporting task.
+     *
+     * @param reportingTask
+     * @param reportingTaskDTO
+     * @return
+     */
+    private List<String> validateProposedConfiguration(final ReportingTaskNode reportingTask, final ReportingTaskDTO reportingTaskDTO) {
+        final List<String> validationErrors = new ArrayList<>();
+
+        // get the current scheduling strategy
+        SchedulingStrategy schedulingStrategy = reportingTask.getSchedulingStrategy();
+
+        // validate the new scheduling strategy if appropriate
+        if (isNotNull(reportingTaskDTO.getSchedulingStrategy())) {
+            try {
+                // this will be the new scheduling strategy so use it
+                schedulingStrategy = SchedulingStrategy.valueOf(reportingTaskDTO.getSchedulingStrategy());
+            } catch (IllegalArgumentException iae) {
+                validationErrors.add(String.format("Scheduling strategy: Value must be one of [%s]", StringUtils.join(SchedulingStrategy.values(), ", ")));
+            }
+        }
+
+        // validate the scheduling period based on the scheduling strategy
+        if (isNotNull(reportingTaskDTO.getSchedulingPeriod())) {
+            switch (schedulingStrategy) {
+                case TIMER_DRIVEN:
+                    final Matcher schedulingMatcher = FormatUtils.TIME_DURATION_PATTERN.matcher(reportingTaskDTO.getSchedulingPeriod());
+                    if (!schedulingMatcher.matches()) {
+                        validationErrors.add("Scheduling period is not a valid time duration (ie 30 sec, 5 min)");
+                    }
+                    break;
+                case CRON_DRIVEN:
+                    try {
+                        new CronExpression(reportingTaskDTO.getSchedulingPeriod());
+                    } catch (final ParseException pe) {
+                        throw new IllegalArgumentException(String.format("Scheduling Period '%s' is not a valid cron expression: %s", reportingTaskDTO.getSchedulingPeriod(), pe.getMessage()));
+                    } catch (final Exception e) {
+                        throw new IllegalArgumentException("Scheduling Period is not a valid cron expression: " + reportingTaskDTO.getSchedulingPeriod());
+                    }
+                    break;
+            }
+        }
+
+        return validationErrors;
+    }
+
+    @Override
+    public void verifyDelete(final String reportingTaskId) {
+        final ReportingTaskNode reportingTask = locateReportingTask(reportingTaskId);
+        reportingTask.verifyCanDelete();
+    }
+
+    @Override
+    public void verifyUpdate(final ReportingTaskDTO reportingTaskDTO) {
+        final ReportingTaskNode reportingTask = locateReportingTask(reportingTaskDTO.getId());
+        verifyUpdate(reportingTask, reportingTaskDTO);
+    }
+
+    /**
+     * Verifies the reporting task can be updated.
+     *
+     * @param reportingTask
+     * @param reportingTaskDTO
+     */
+    private void verifyUpdate(final ReportingTaskNode reportingTask, final ReportingTaskDTO reportingTaskDTO) {
+        // ensure the state, if specified, is valid
+        if (isNotNull(reportingTaskDTO.getState())) {
+            try {
+                final ScheduledState purposedScheduledState = ScheduledState.valueOf(reportingTaskDTO.getState());
+
+                // only attempt an action if it is changing
+                if (!purposedScheduledState.equals(reportingTask.getScheduledState())) {
+                    // perform the appropriate action
+                    switch (purposedScheduledState) {
+                        case RUNNING:
+                            reportingTask.verifyCanStart();
+                            break;
+                        case STOPPED:
+                            switch (reportingTask.getScheduledState()) {
+                                case RUNNING:
+                                    reportingTask.verifyCanStop();
+                                    break;
+                                case DISABLED:
+                                    reportingTask.verifyCanEnable();
+                                    break;
+                            }
+                            break;
+                        case DISABLED:
+                            reportingTask.verifyCanDisable();
+                            break;
+                    }
+                }
+            } catch (IllegalArgumentException iae) {
+                throw new IllegalArgumentException(String.format(
+                        "The specified reporting task state (%s) is not valid. Valid options are 'RUNNING', 'STOPPED', and 'DISABLED'.",
+                        reportingTaskDTO.getState()));
+            }
+        }
+
+        boolean modificationRequest = false;
+        if (isAnyNotNull(reportingTaskDTO.getName(),
+                reportingTaskDTO.getSchedulingStrategy(),
+                reportingTaskDTO.getSchedulingPeriod(),
+                reportingTaskDTO.getAnnotationData(),
+                reportingTaskDTO.getProperties())) {
+            modificationRequest = true;
+
+            // validate the request
+            final List<String> requestValidation = validateProposedConfiguration(reportingTask, reportingTaskDTO);
+
+            // ensure there was no validation errors
+            if (!requestValidation.isEmpty()) {
+                throw new ValidationException(requestValidation);
+            }
+        }
+
+        if (modificationRequest) {
+            reportingTask.verifyCanUpdate();
+        }
+    }
+
+    /**
+     * Configures the specified reporting task.
+     *
+     * @param reportingTask
+     * @param reportingTaskDTO
+     */
+    private void configureReportingTask(final ReportingTaskNode reportingTask, final ReportingTaskDTO reportingTaskDTO) {
+        final String name = reportingTaskDTO.getName();
+        final String schedulingStrategy = reportingTaskDTO.getSchedulingStrategy();
+        final String schedulingPeriod = reportingTaskDTO.getSchedulingPeriod();
+        final String annotationData = reportingTaskDTO.getAnnotationData();
+        final String comments = reportingTaskDTO.getComments();
+        final Map<String, String> properties = reportingTaskDTO.getProperties();
+
+        // ensure scheduling strategy is set first
+        if (isNotNull(schedulingStrategy)) {
+            reportingTask.setSchedulingStrategy(SchedulingStrategy.valueOf(schedulingStrategy));
+        }
+        
+        if (isNotNull(name)) {
+            reportingTask.setName(name);
+        }
+        if (isNotNull(schedulingPeriod)) {
+            reportingTask.setScheduldingPeriod(schedulingPeriod);
+        }
+        if (isNotNull(annotationData)) {
+            reportingTask.setAnnotationData(annotationData);
+        }
+        if (isNotNull(comments)) {
+            reportingTask.setComments(comments);
+        }
+        if (isNotNull(properties)) {
+            for (final Map.Entry<String, String> entry : properties.entrySet()) {
+                final String propName = entry.getKey();
+                final String propVal = entry.getValue();
+                if (isNotNull(propName) && propVal == null) {
+                    reportingTask.removeProperty(propName);
+                } else if (isNotNull(propName)) {
+                    reportingTask.setProperty(propName, propVal);
+                }
+            }
+        }
+    }
+
+    /**
+     * Deletes the specified reporting task.
+     *
+     * @param reportingTaskId The reporting task id
+     */
+    @Override
+    public void deleteReportingTask(String reportingTaskId) {
+        final ReportingTaskNode reportingTask = locateReportingTask(reportingTaskId);
+        reportingTaskProvider.removeReportingTask(reportingTask);
+    }
+
+    /* setters */
+    public void setReportingTaskProvider(ReportingTaskProvider reportingTaskProvider) {
+        this.reportingTaskProvider = reportingTaskProvider;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardSnippetDAO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardSnippetDAO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardSnippetDAO.java
index 92e3a8d..6447464 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardSnippetDAO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardSnippetDAO.java
@@ -26,9 +26,11 @@ import org.apache.nifi.controller.ProcessorNode;
 import org.apache.nifi.controller.Snippet;
 import org.apache.nifi.controller.StandardSnippet;
 import org.apache.nifi.controller.exception.ProcessorInstantiationException;
+import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.web.NiFiCoreException;
 import org.apache.nifi.web.ResourceNotFoundException;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
 import org.apache.nifi.web.api.dto.FlowSnippetDTO;
 import org.apache.nifi.web.api.dto.ProcessGroupDTO;
 import org.apache.nifi.web.api.dto.ProcessorConfigDTO;
@@ -36,7 +38,6 @@ import org.apache.nifi.web.api.dto.ProcessorDTO;
 import org.apache.nifi.web.api.dto.SnippetDTO;
 import org.apache.nifi.web.dao.SnippetDAO;
 import org.apache.nifi.web.util.SnippetUtils;
-
 import org.apache.commons.lang3.StringUtils;
 
 /**
@@ -285,9 +286,13 @@ public class StandardSnippetDAO implements SnippetDAO {
         if (snippet != null) {
             // go through each processor if specified
             if (snippet.getProcessors() != null) {
-                lookupSensitiveProperties(snippet.getProcessors());
+                lookupSensitiveProcessorProperties(snippet.getProcessors());
             }
 
+            if ( snippet.getControllerServices() != null ) {
+                lookupSensitiveControllerServiceProperties(snippet.getControllerServices());
+            }
+            
             // go through each process group if specified
             if (snippet.getProcessGroups() != null) {
                 for (final ProcessGroupDTO group : snippet.getProcessGroups()) {
@@ -303,7 +308,7 @@ public class StandardSnippetDAO implements SnippetDAO {
      *
      * @param snippet
      */
-    private void lookupSensitiveProperties(final Set<ProcessorDTO> processors) {
+    private void lookupSensitiveProcessorProperties(final Set<ProcessorDTO> processors) {
         final ProcessGroup rootGroup = flowController.getGroup(flowController.getRootGroupId());
 
         // go through each processor
@@ -331,6 +336,31 @@ public class StandardSnippetDAO implements SnippetDAO {
             }
         }
     }
+    
+    private void lookupSensitiveControllerServiceProperties(final Set<ControllerServiceDTO> controllerServices) {
+        // go through each service
+        for (final ControllerServiceDTO serviceDTO : controllerServices) {
+            
+            // ensure that some property configuration have been specified
+            final Map<String, String> serviceProperties = serviceDTO.getProperties();
+            if (serviceProperties != null) {
+                // find the corresponding controller service
+                final ControllerServiceNode serviceNode = flowController.getControllerServiceNode(serviceDTO.getId());
+                if (serviceNode == null) {
+                    throw new IllegalArgumentException(String.format("Unable to create snippet because Controller Service '%s' could not be found", serviceDTO.getId()));
+                }
+
+                // look for sensitive properties get the actual value
+                for (Entry<PropertyDescriptor, String> entry : serviceNode.getProperties().entrySet()) {
+                    final PropertyDescriptor descriptor = entry.getKey();
+
+                    if (descriptor.isSensitive()) {
+                        serviceProperties.put(descriptor.getName(), entry.getValue());
+                    }
+                }
+            }
+        }
+    }
 
     /* setters */
     public void setFlowController(FlowController flowController) {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/spring/ControllerServiceProviderFactoryBean.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/spring/ControllerServiceProviderFactoryBean.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/spring/ControllerServiceProviderFactoryBean.java
new file mode 100644
index 0000000..5c10de6
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/spring/ControllerServiceProviderFactoryBean.java
@@ -0,0 +1,68 @@
+/*
+ * 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.web.spring;
+
+import org.apache.nifi.cluster.manager.impl.WebClusterManager;
+import org.apache.nifi.controller.FlowController;
+import org.apache.nifi.controller.service.ControllerServiceProvider;
+import org.apache.nifi.util.NiFiProperties;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+/**
+ *
+ */
+public class ControllerServiceProviderFactoryBean implements FactoryBean, ApplicationContextAware {
+
+    private ApplicationContext context;
+    private ControllerServiceProvider controllerServiceProvider;
+    private NiFiProperties properties;
+
+    @Override
+    public Object getObject() throws Exception {
+        if (controllerServiceProvider == null) {
+            if (properties.isClusterManager()) {
+                controllerServiceProvider = context.getBean("clusterManager", WebClusterManager.class);
+            } else {
+                controllerServiceProvider = context.getBean("flowController", FlowController.class);
+            }
+        }
+
+        return controllerServiceProvider;
+    }
+
+    @Override
+    public Class getObjectType() {
+        return ControllerServiceProvider.class;
+    }
+
+    @Override
+    public boolean isSingleton() {
+        return true;
+    }
+
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext context) throws BeansException {
+        this.context = context;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/spring/OptimisticLockingManagerFactoryBean.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/spring/OptimisticLockingManagerFactoryBean.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/spring/OptimisticLockingManagerFactoryBean.java
new file mode 100644
index 0000000..8436793
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/spring/OptimisticLockingManagerFactoryBean.java
@@ -0,0 +1,67 @@
+/*
+ * 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.web.spring;
+
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.OptimisticLockingManager;
+import org.apache.nifi.web.StandardOptimisticLockingManager;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+/**
+ *
+ */
+public class OptimisticLockingManagerFactoryBean implements FactoryBean, ApplicationContextAware {
+
+    private ApplicationContext context;
+    private OptimisticLockingManager optimisticLockingManager;
+    private NiFiProperties properties;
+
+    @Override
+    public Object getObject() throws Exception {
+        if (optimisticLockingManager == null) {
+            if (properties.isClusterManager()) {
+                optimisticLockingManager = context.getBean("clusterManagerOptimisticLockingManager", OptimisticLockingManager.class);
+            } else {
+                optimisticLockingManager = new StandardOptimisticLockingManager();
+            }
+        }
+
+        return optimisticLockingManager;
+    }
+
+    @Override
+    public Class getObjectType() {
+        return OptimisticLockingManager.class;
+    }
+
+    @Override
+    public boolean isSingleton() {
+        return true;
+    }
+
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext context) throws BeansException {
+        this.context = context;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/spring/ReportingTaskProviderFactoryBean.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/spring/ReportingTaskProviderFactoryBean.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/spring/ReportingTaskProviderFactoryBean.java
new file mode 100644
index 0000000..d344fa6
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/spring/ReportingTaskProviderFactoryBean.java
@@ -0,0 +1,69 @@
+/*
+ * 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.web.spring;
+
+import org.apache.nifi.cluster.manager.impl.WebClusterManager;
+import org.apache.nifi.controller.FlowController;
+import org.apache.nifi.controller.reporting.ReportingTaskProvider;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.dao.ControllerServiceDAO;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+/**
+ *
+ */
+public class ReportingTaskProviderFactoryBean implements FactoryBean, ApplicationContextAware {
+
+    private ApplicationContext context;
+    private ReportingTaskProvider reportingTaskProvider;
+    private NiFiProperties properties;
+
+    @Override
+    public Object getObject() throws Exception {
+        if (reportingTaskProvider == null) {
+            if (properties.isClusterManager()) {
+                reportingTaskProvider = context.getBean("clusterManager", WebClusterManager.class);
+            } else {
+                reportingTaskProvider = context.getBean("flowController", FlowController.class);
+            }
+        }
+
+        return reportingTaskProvider;
+    }
+
+    @Override
+    public Class getObjectType() {
+        return ReportingTaskProvider.class;
+    }
+
+    @Override
+    public boolean isSingleton() {
+        return true;
+    }
+
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext context) throws BeansException {
+        this.context = context;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/Availability.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/Availability.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/Availability.java
new file mode 100644
index 0000000..29ba4f8
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/Availability.java
@@ -0,0 +1,34 @@
+/*
+ * 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.web.util;
+
+/**
+ * Where a given controller service or reporting task should run.
+ */
+public enum Availability {
+
+    /**
+     * Service or reporting task will run only on the NiFi Cluster Manager (NCM)
+     */
+    NCM,
+    
+    /**
+     * Service or reporting task will run only on NiFi Nodes (or standalone
+     * instance, if not clustered)
+     */
+    NODE;
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/SnippetUtils.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/SnippetUtils.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/SnippetUtils.java
index 8653094..fa9bc41 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/SnippetUtils.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/SnippetUtils.java
@@ -18,6 +18,7 @@ package org.apache.nifi.web.util;
 
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
@@ -28,6 +29,7 @@ import java.util.UUID;
 
 import org.apache.nifi.cluster.context.ClusterContext;
 import org.apache.nifi.cluster.context.ClusterContextThreadLocal;
+import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.connectable.ConnectableType;
 import org.apache.nifi.connectable.Connection;
 import org.apache.nifi.connectable.Funnel;
@@ -37,17 +39,22 @@ import org.apache.nifi.controller.ProcessorNode;
 import org.apache.nifi.controller.ScheduledState;
 import org.apache.nifi.controller.Snippet;
 import org.apache.nifi.controller.label.Label;
+import org.apache.nifi.controller.service.ControllerServiceNode;
+import org.apache.nifi.controller.service.ControllerServiceState;
 import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.groups.RemoteProcessGroup;
 import org.apache.nifi.web.api.dto.ConnectableDTO;
 import org.apache.nifi.web.api.dto.ConnectionDTO;
+import org.apache.nifi.web.api.dto.ControllerServiceDTO;
 import org.apache.nifi.web.api.dto.DtoFactory;
 import org.apache.nifi.web.api.dto.FlowSnippetDTO;
 import org.apache.nifi.web.api.dto.FunnelDTO;
 import org.apache.nifi.web.api.dto.LabelDTO;
 import org.apache.nifi.web.api.dto.PortDTO;
 import org.apache.nifi.web.api.dto.ProcessGroupDTO;
+import org.apache.nifi.web.api.dto.ProcessorConfigDTO;
 import org.apache.nifi.web.api.dto.ProcessorDTO;
+import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupContentsDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO;
 import org.apache.nifi.web.api.dto.RemoteProcessGroupPortDTO;
@@ -181,11 +188,100 @@ public final class SnippetUtils {
             snippetDto.setRemoteProcessGroups(remoteProcessGroups);
         }
 
+        addControllerServicesToSnippet(snippetDto);
+        
         return snippetDto;
     }
+    
+    private void addControllerServicesToSnippet(final FlowSnippetDTO snippetDto) {
+        for ( final ProcessorDTO processorDto : snippetDto.getProcessors() ) {
+            addControllerServicesToSnippet(snippetDto, processorDto);
+        }
+        
+        for ( final ProcessGroupDTO processGroupDto : snippetDto.getProcessGroups() ) {
+            final FlowSnippetDTO childGroupDto = processGroupDto.getContents();
+            addControllerServicesToSnippet(childGroupDto);
+        }
+    }
+    
+    private void addControllerServicesToSnippet(final FlowSnippetDTO snippet, final ProcessorDTO processorDto) {
+        final ProcessorConfigDTO configDto = processorDto.getConfig();
+        if ( configDto == null ) {
+            return;
+        }
+        
+        final Map<String, PropertyDescriptorDTO> descriptors = configDto.getDescriptors();
+        final Map<String, String> properties = configDto.getProperties();
+        
+        if ( properties != null && descriptors != null ) {
+            for ( final Map.Entry<String, String> entry : properties.entrySet() ) {
+                final String propName = entry.getKey();
+                final String propValue = entry.getValue();
+                if ( propValue == null ) {
+                    continue;
+                }
+                
+                final PropertyDescriptorDTO propertyDescriptorDto = descriptors.get(propName);
+                if ( propertyDescriptorDto != null && propertyDescriptorDto.isIdentifiesControllerService() ) {
+                    final ControllerServiceNode serviceNode = flowController.getControllerServiceNode(propValue);
+                    if ( serviceNode != null ) {
+                        addControllerServicesToSnippet(snippet, serviceNode);
+                    }
+                }
+            }
+        }
+    }
+    
+    private void addControllerServicesToSnippet(final FlowSnippetDTO snippet, final ControllerServiceNode serviceNode) {
+        if ( isServicePresent(serviceNode.getIdentifier(), snippet.getControllerServices()) ) {
+            return;
+        }
+        
+        final ControllerServiceDTO serviceNodeDto = dtoFactory.createControllerServiceDto(serviceNode);
+        Set<ControllerServiceDTO> existingServiceDtos = snippet.getControllerServices();
+        if ( existingServiceDtos == null ) {
+            existingServiceDtos = new HashSet<>();
+            snippet.setControllerServices(existingServiceDtos);
+        }
+        existingServiceDtos.add(serviceNodeDto);
+
+        for ( final Map.Entry<PropertyDescriptor, String> entry : serviceNode.getProperties().entrySet() ) {
+            final PropertyDescriptor descriptor = entry.getKey();
+            final String propertyValue = entry.getValue();
+            
+            if ( descriptor.getControllerServiceDefinition() != null ) {
+                final ControllerServiceNode referencedNode = flowController.getControllerServiceNode(propertyValue);
+                if ( referencedNode == null ) {
+                    throw new IllegalStateException("Controller Service with ID " + propertyValue + " is referenced in template but cannot be found");
+                }
+                
+                final String referencedNodeId = referencedNode.getIdentifier();
+                
+                final boolean alreadyPresent = isServicePresent(referencedNodeId, snippet.getControllerServices());
+                if ( !alreadyPresent ) {
+                    addControllerServicesToSnippet(snippet, referencedNode);
+                }
+            }
+        }
+    }
 
+    private boolean isServicePresent(final String serviceId, final Collection<ControllerServiceDTO> services) {
+        if ( services == null ) {
+            return false;
+        }
+        
+        for ( final ControllerServiceDTO existingService : services ) {
+            if ( serviceId.equals(existingService.getId()) ) {
+                return true;
+            }
+        }
+        
+        return false;
+    }
+    
+    
     public FlowSnippetDTO copy(final FlowSnippetDTO snippetContents, final ProcessGroup group) {
-        final FlowSnippetDTO snippetCopy = copyContentsForGroup(snippetContents, group.getIdentifier(), null);
+        final FlowSnippetDTO snippetCopy = copyContentsForGroup(snippetContents, group.getIdentifier(), null, null);
         resolveNameConflicts(snippetCopy, group);
         return snippetCopy;
     }
@@ -240,10 +336,49 @@ public final class SnippetUtils {
         }
     }
 
-    private FlowSnippetDTO copyContentsForGroup(final FlowSnippetDTO snippetContents, final String groupId, final Map<String, ConnectableDTO> parentConnectableMap) {
+    private FlowSnippetDTO copyContentsForGroup(final FlowSnippetDTO snippetContents, final String groupId, final Map<String, ConnectableDTO> parentConnectableMap, Map<String, String> serviceIdMap) {
         final FlowSnippetDTO snippetContentsCopy = new FlowSnippetDTO();
 
         //
+        // Copy the Controller Services
+        //
+        if ( serviceIdMap == null ) {
+            serviceIdMap = new HashMap<>();
+            final Set<ControllerServiceDTO> services = new HashSet<>();
+            if ( snippetContents.getControllerServices() != null ) {
+                for (final ControllerServiceDTO serviceDTO : snippetContents.getControllerServices() ) {
+                    final ControllerServiceDTO service = dtoFactory.copy(serviceDTO);
+                    service.setId(generateId(serviceDTO.getId()));
+                    service.setState(ControllerServiceState.DISABLED.name());
+                    services.add(service);
+                    
+                    // Map old service ID to new service ID so that we can make sure that we reference the new ones.
+                    serviceIdMap.put(serviceDTO.getId(), service.getId());
+                }
+            }
+            
+            // if there is any controller service that maps to another controller service, update the id's
+            for ( final ControllerServiceDTO serviceDTO : services ) {
+                final Map<String, String> properties = serviceDTO.getProperties();
+                final Map<String, PropertyDescriptorDTO> descriptors = serviceDTO.getDescriptors();
+                if ( properties != null && descriptors != null ) {
+                    for ( final PropertyDescriptorDTO descriptor : descriptors.values() ) {
+                        if ( descriptor.isIdentifiesControllerService() ) {
+                            final String currentServiceId = properties.get(descriptor.getName());
+                            if ( currentServiceId == null ) {
+                                continue;
+                            }
+                            
+                            final String newServiceId = serviceIdMap.get(currentServiceId);
+                            properties.put(descriptor.getName(), newServiceId);
+                        }
+                    }
+                }
+            }
+            snippetContentsCopy.setControllerServices(services);
+        }
+        
+        //
         // Copy the labels
         //
         final Set<LabelDTO> labels = new HashSet<>();
@@ -332,6 +467,9 @@ public final class SnippetUtils {
         }
         snippetContentsCopy.setProcessors(processors);
 
+        // if there is any controller service that maps to another controller service, update the id's
+        updateControllerServiceIdentifiers(snippetContentsCopy, serviceIdMap);
+        
         // 
         // Copy ProcessGroups
         //
@@ -344,7 +482,7 @@ public final class SnippetUtils {
                 cp.setParentGroupId(groupId);
 
                 // copy the contents of this group - we do not copy via the dto factory since we want to specify new ids
-                final FlowSnippetDTO contentsCopy = copyContentsForGroup(groupDTO.getContents(), cp.getId(), connectableMap);
+                final FlowSnippetDTO contentsCopy = copyContentsForGroup(groupDTO.getContents(), cp.getId(), connectableMap, serviceIdMap);
                 cp.setContents(contentsCopy);
                 groups.add(cp);
             }
@@ -396,6 +534,43 @@ public final class SnippetUtils {
 
         return snippetContentsCopy;
     }
+    
+    
+    private void updateControllerServiceIdentifiers(final FlowSnippetDTO snippet, final Map<String, String> serviceIdMap) {
+        final Set<ProcessorDTO> processors = snippet.getProcessors();
+        if ( processors != null ) {
+            for ( final ProcessorDTO processor : processors ) {
+                updateControllerServiceIdentifiers(processor.getConfig(), serviceIdMap);
+            }
+        }
+        
+        for ( final ProcessGroupDTO processGroupDto : snippet.getProcessGroups() ) {
+            updateControllerServiceIdentifiers(processGroupDto.getContents(), serviceIdMap);
+        }
+    }
+    
+    private void updateControllerServiceIdentifiers(final ProcessorConfigDTO configDto, final Map<String, String> serviceIdMap) {
+        if ( configDto == null ) {
+            return;
+        }
+        
+        final Map<String, String> properties = configDto.getProperties();
+        final Map<String, PropertyDescriptorDTO> descriptors = configDto.getDescriptors();
+        if ( properties != null && descriptors != null ) {
+            for ( final PropertyDescriptorDTO descriptor : descriptors.values() ) {
+                if ( descriptor.isIdentifiesControllerService() ) {
+                    final String currentServiceId = properties.get(descriptor.getName());
+                    if ( currentServiceId == null ) {
+                        continue;
+                    }
+                    
+                    final String newServiceId = serviceIdMap.get(currentServiceId);
+                    properties.put(descriptor.getName(), newServiceId);
+                }
+            }
+        }
+    }
+    
 
     /**
      * Generates a new id for the current id that is specified. If no seed is

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
index a822442..bf4f245 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
@@ -24,11 +24,17 @@
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
 
+    <!-- controller service / reporting task -->
+    <bean id="controllerServiceProvider" class="org.apache.nifi.web.spring.ControllerServiceProviderFactoryBean" depends-on="clusterManager flowController">
+        <property name="properties" ref="nifiProperties"/>
+    </bean>
+    <bean id="reportingTaskProvider" class="org.apache.nifi.web.spring.ReportingTaskProviderFactoryBean" depends-on="clusterManager flowController">
+        <property name="properties" ref="nifiProperties"/>
+    </bean>
+    
     <!-- optimistic locking manager -->
-    <bean id="optimisticLockingManager" class="org.apache.nifi.web.ClusterAwareOptimisticLockingManager">
-        <constructor-arg>
-            <bean class="org.apache.nifi.web.StandardOptimisticLockingManager" />
-        </constructor-arg>
+    <bean id="webOptimisticLockingManager" class="org.apache.nifi.web.spring.OptimisticLockingManagerFactoryBean" depends-on="clusterManagerOptimisticLockingManager">
+        <property name="properties" ref="nifiProperties"/>
     </bean>
     
     <!-- content access -->
@@ -40,8 +46,7 @@
 
     <!-- dto factory -->
     <bean id="dtoFactory" class="org.apache.nifi.web.api.dto.DtoFactory">
-        <property name="properties" ref="nifiProperties"/>
-        <property name="controllerServiceLookup" ref="flowController" />
+        <property name="controllerServiceLookup" ref="controllerServiceProvider" />
     </bean>
 
     <!-- snippet utils -->
@@ -75,6 +80,12 @@
     <bean id="processorDAO" class="org.apache.nifi.web.dao.impl.StandardProcessorDAO">
         <property name="flowController" ref="flowController"/>
     </bean>
+    <bean id="controllerServiceDAO" class="org.apache.nifi.web.dao.impl.StandardControllerServiceDAO">
+        <property name="serviceProvider" ref="controllerServiceProvider"/>
+    </bean>
+    <bean id="reportingTaskDAO" class="org.apache.nifi.web.dao.impl.StandardReportingTaskDAO">
+        <property name="reportingTaskProvider" ref="reportingTaskProvider"/>
+    </bean>
     <bean id="templateDAO" class="org.apache.nifi.web.dao.impl.StandardTemplateDAO">
         <property name="flowController" ref="flowController"/>
         <property name="snippetUtils" ref="snippetUtils"/>
@@ -101,22 +112,35 @@
         <property name="labelDAO" ref="labelDAO"/>
         <property name="funnelDAO" ref="funnelDAO"/>
         <property name="connectionDAO" ref="connectionDAO"/>
+        <property name="controllerServiceDAO" ref="controllerServiceDAO"/>
+        <property name="reportingTaskDAO" ref="reportingTaskDAO"/>
         <property name="templateDAO" ref="templateDAO"/>
         <property name="snippetDAO" ref="snippetDAO"/>
         <property name="auditService" ref="auditService"/>
         <property name="userService" ref="userService"/>
         <property name="snippetUtils" ref="snippetUtils"/>
-        <property name="optimisticLockingManager" ref="optimisticLockingManager"/>
+        <property name="optimisticLockingManager" ref="webOptimisticLockingManager"/>
         <property name="dtoFactory" ref="dtoFactory"/>
         <property name="clusterManager" ref="clusterManager"/>
     </bean>
 
+    <!-- depecrated -->
     <bean id="nifiWebContext" class="org.apache.nifi.web.StandardNiFiWebContext">
         <property name="serviceFacade" ref="serviceFacade"/>
-        <property name="controllerFacade" ref="controllerFacade"/>
         <property name="properties" ref="nifiProperties"/>
         <property name="clusterManager" ref="clusterManager"/>
         <property name="auditService" ref="auditService"/>
+        <property name="controllerServiceLookup" ref="controllerServiceProvider"/>
+    </bean>
+    
+    <!-- component ui extension configuration context -->
+    <bean id="nifiWebConfigurationContext" class="org.apache.nifi.web.StandardNiFiWebConfigurationContext">
+        <property name="serviceFacade" ref="serviceFacade"/>
+        <property name="properties" ref="nifiProperties"/>
+        <property name="clusterManager" ref="clusterManager"/>
+        <property name="auditService" ref="auditService"/>
+        <property name="controllerServiceLookup" ref="controllerServiceProvider"/>
+        <property name="reportingTaskProvider" ref="reportingTaskProvider"/>
     </bean>
 
     <!-- rest endpoints -->
@@ -133,6 +157,16 @@
         <property name="properties" ref="nifiProperties"/>
         <property name="clusterManager" ref="clusterManager"/>
     </bean>
+    <bean id="controllerServiceResource" class="org.apache.nifi.web.api.ControllerServiceResource" scope="singleton">
+        <property name="serviceFacade" ref="serviceFacade"/>
+        <property name="properties" ref="nifiProperties"/>
+        <property name="clusterManager" ref="clusterManager"/>
+    </bean>
+    <bean id="reportingTaskResource" class="org.apache.nifi.web.api.ReportingTaskResource" scope="singleton">
+        <property name="serviceFacade" ref="serviceFacade"/>
+        <property name="properties" ref="nifiProperties"/>
+        <property name="clusterManager" ref="clusterManager"/>
+    </bean>
     <bean id="processGroupResource" class="org.apache.nifi.web.api.ProcessGroupResource" scope="prototype">
         <property name="serviceFacade" ref="serviceFacade"/>
         <property name="properties" ref="nifiProperties"/>
@@ -303,6 +337,16 @@
         <property name="processorAuditor" ref="processorAuditor"/>
         <property name="relationshipAuditor" ref="relationshipAuditor"/>
     </bean>
+    <bean id="controllerServiceAuditor" class="org.apache.nifi.audit.ControllerServiceAuditor">
+        <property name="serviceFacade" ref="serviceFacade"/>
+        <property name="auditService" ref="auditService"/>
+        <property name="processGroupDAO" ref="processGroupDAO"/>
+    </bean>
+    <bean id="reportingTaskAuditor" class="org.apache.nifi.audit.ReportingTaskAuditor">
+        <property name="serviceFacade" ref="serviceFacade"/>
+        <property name="auditService" ref="auditService"/>
+        <property name="processGroupDAO" ref="processGroupDAO"/>
+    </bean>
     
     <!-- NiFi locking -->
     <bean id="serviceFacadeLock" class="org.apache.nifi.web.NiFiServiceFacadeLock"/>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java
index dd9bb73..1f28609 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/integration/util/NiFiTestServer.java
@@ -18,12 +18,14 @@ package org.apache.nifi.integration.util;
 
 import com.sun.jersey.api.client.Client;
 import java.io.File;
+import java.util.Collections;
 import javax.servlet.ServletContext;
 import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.util.WebUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.framework.security.util.SslContextFactory;
 import org.apache.nifi.services.FlowService;
+import org.apache.nifi.ui.extension.UiExtensionMapping;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnectionFactory;
 import org.eclipse.jetty.server.SecureRequestCustomizer;
@@ -160,6 +162,9 @@ public class NiFiTestServer {
      */
     public void startServer() throws Exception {
         jetty.start();
+        
+        // ensure the ui extensions are set
+        webappContext.getServletContext().setAttribute("nifi-ui-extensions", new UiExtensionMapping(Collections.EMPTY_MAP));
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/pom.xml
index e9781f8..a9f0c3e 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/pom.xml
@@ -27,5 +27,17 @@
             <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-administration</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-web-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-framework-cluster-web</artifactId>
+        </dependency>
     </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/ConfigurationRequest.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/ConfigurationRequest.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/ConfigurationRequest.java
new file mode 100644
index 0000000..939c3f0
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/ConfigurationRequest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.web;
+
+/**
+ * Represents a request to configure. The implementations execute method will
+ * perform the configuration action. It will return type T which will be
+ * encapsulated in a ConfigurationSnapshot.
+ * 
+ * @param <T>
+ */
+public interface ConfigurationRequest<T> {
+
+    /**
+     * Executes a configuration action and returns the updated resulting configuration.
+     * 
+     * @return The resulting configuration
+     */
+    T execute();
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/ConfigurationSnapshot.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/ConfigurationSnapshot.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/ConfigurationSnapshot.java
index 6ad683c..8817d69 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/ConfigurationSnapshot.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/ConfigurationSnapshot.java
@@ -22,36 +22,36 @@ package org.apache.nifi.web;
  */
 public class ConfigurationSnapshot<T> {
 
-    private Long revision;
+    private Long version;
     private T configuration;
 
     /**
      * Creates a new ConfigurationSnapshot.
      *
-     * @param revision The model revision
+     * @param version The revision version
      */
-    public ConfigurationSnapshot(Long revision) {
-        this(revision, null);
+    public ConfigurationSnapshot(Long version) {
+        this(version, null);
     }
 
     /**
      * Creates a new ConfigurationSnapshot.
      *
-     * @param revision The model revision
+     * @param version The revision version
      * @param configuration The configuration
      */
-    public ConfigurationSnapshot(Long revision, T configuration) {
-        this.revision = revision;
+    public ConfigurationSnapshot(Long version, T configuration) {
+        this.version = version;
         this.configuration = configuration;
     }
 
     /**
-     * Get the new model revision.
+     * Get the revision version.
      *
-     * @return The model revision
+     * @return The revision version
      */
-    public Long getRevision() {
-        return revision;
+    public Long getVersion() {
+        return version;
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/FlowModification.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/FlowModification.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/FlowModification.java
new file mode 100644
index 0000000..f6bccb1
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/FlowModification.java
@@ -0,0 +1,57 @@
+/*
+ * 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.web;
+
+/**
+ * Records a flow modification. This includes the resulting revision and the
+ * user that performed the modification.
+ */
+public class FlowModification {
+
+    private final Revision revision;
+    private final String lastModifier;
+
+    /**
+     * Creates a new FlowModification.
+     * 
+     * @param revision
+     * @param lastModifier 
+     */
+    public FlowModification(Revision revision, String lastModifier) {
+        this.revision = revision;
+        this.lastModifier = lastModifier;
+    }
+
+    /**
+     * Get the revision.
+     * 
+     * @return 
+     */
+    public Revision getRevision() {
+        return revision;
+    }
+
+    /**
+     * Get the last modifier.
+     * 
+     * @return 
+     */
+    public String getLastModifier() {
+        return lastModifier;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/e9647717/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/OptimisticLockingManager.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/OptimisticLockingManager.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/OptimisticLockingManager.java
index b045247..4c54b7c 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/OptimisticLockingManager.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-optimistic-locking/src/main/java/org/apache/nifi/web/OptimisticLockingManager.java
@@ -26,70 +26,28 @@ package org.apache.nifi.web;
 public interface OptimisticLockingManager {
 
     /**
-     * Checks the specified revision against the current revision. If the check
-     * succeeds, then the current revision's version is incremented and the
-     * current revision's client ID is set to the given revision's client ID.
-     *
-     * If the given revision's version is null, then the revision's client ID
-     * must match for the current revision's client ID for the check to succeed.
-     *
-     * If the versions and the clientIds do not match, then an
-     * InvalidRevisionException.
-     *
-     * @param revision the revision to check
-     *
-     * @return the current revision
-     *
-     * @throws InvalidRevisionException if the given revision does not match the
-     * current revision
+     * Attempts to execute the specified configuration request using the specified revision within a lock.
+     * 
+     * @param <T>
+     * @param revision
+     * @param configurationRequest
+     * @return 
      */
-    Revision checkRevision(Revision revision) throws InvalidRevisionException;
-
-    /**
-     * Returns true if the given revision matches the current revision.
-     *
-     * @param revision a revision
-     * @return true if given revision is current; false otherwise.
-     */
-    boolean isCurrent(Revision revision);
-
+    <T> ConfigurationSnapshot<T> configureFlow(Revision revision, ConfigurationRequest<T> configurationRequest);
+    
     /**
-     * @return the current revision
+     * Updates the revision using the specified revision within a lock.
+     * 
+     * @param updateRevision 
      */
-    Revision getRevision();
+    void setRevision(UpdateRevision updateRevision);
 
     /**
-     * Sets the current revision.
-     *
-     * @param revision a revision
+     * Returns the last flow modification. This is a combination of the revision and the user
+     * who performed the modification.
+     * 
+     * @return the last modification
      */
-    void setRevision(Revision revision);
+    FlowModification getLastModification();
 
-    /**
-     * Increments the current revision's version.
-     *
-     * @return the current revision
-     */
-    Revision incrementRevision();
-
-    /**
-     * Increments the current revision's version and sets the current revision's
-     * client ID to the given client ID.
-     *
-     * @param clientId a client ID
-     * @return the current revision
-     */
-    Revision incrementRevision(String clientId);
-
-    /**
-     * @return the last modifier.
-     */
-    String getLastModifier();
-
-    /**
-     * Sets the last modifier.
-     *
-     * @param lastModifier the last modifier
-     */
-    void setLastModifier(String lastModifier);
 }


[38/62] [abbrv] incubator-nifi git commit: NIFI-490: If IO error when creating Site-to-Site transaction, cleanup connection

Posted by ma...@apache.org.
NIFI-490: If IO error when creating Site-to-Site transaction, cleanup connection


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/a7862a19
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/a7862a19
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/a7862a19

Branch: refs/heads/NIFI-25
Commit: a7862a19ba56f4ce8350d34b38d7d4edc6002a66
Parents: 5098693
Author: Mark Payne <ma...@hotmail.com>
Authored: Tue Apr 7 12:36:35 2015 -0400
Committer: Mark Payne <ma...@hotmail.com>
Committed: Tue Apr 7 12:36:35 2015 -0400

----------------------------------------------------------------------
 .../org/apache/nifi/remote/client/socket/SocketClient.java   | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/a7862a19/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/SocketClient.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/SocketClient.java b/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/SocketClient.java
index bd9319f..ed54ccb 100644
--- a/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/SocketClient.java
+++ b/nifi/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/SocketClient.java
@@ -130,8 +130,14 @@ public class SocketClient implements SiteToSiteClient {
 		    return null;
 		}
 		
-		final Transaction transaction = connectionState.getSocketClientProtocol().startTransaction(
+		final Transaction transaction;
+		try {
+			transaction = connectionState.getSocketClientProtocol().startTransaction(
 				connectionState.getPeer(), connectionState.getCodec(), direction);
+		} catch (final Throwable t) {
+			pool.terminate(connectionState);
+			throw t;
+		}
 		
 		// Wrap the transaction in a new one that will return the EndpointConnectionState back to the pool whenever
 		// the transaction is either completed or canceled.