Repository: incubator-nifi Updated Branches: refs/heads/develop 22596080c -> bda9985d6
NIFI-115: - Code clean up. - Showing an expiration icon on the connection label when appropriate. - Allowing the user to search for connections that have expiration and back pressure configured. Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/bda9985d Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/bda9985d Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/bda9985d Branch: refs/heads/develop Commit: bda9985d6a3f7ecb2f20829eb2f084a8bfdd56fe Parents: 2259608 Author: Matt Gilman <[email protected]> Authored: Thu Jan 8 11:02:25 2015 -0500 Committer: Matt Gilman <[email protected]> Committed: Thu Jan 8 11:02:25 2015 -0500 ---------------------------------------------------------------------- .../nifi/web/controller/ControllerFacade.java | 23 ++ .../canvas/connection-configuration.jsp | 2 +- .../WEB-INF/partials/connection-details.jsp | 2 +- .../src/main/webapp/js/nf/canvas/nf-canvas.js | 275 +++++++++++-------- .../main/webapp/js/nf/canvas/nf-connection.js | 107 ++++++-- 5 files changed, 269 insertions(+), 140 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/bda9985d/nar-bundles/framework-bundle/framework/web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java ---------------------------------------------------------------------- diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java b/nar-bundles/framework-bundle/framework/web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java index 008295c..dbc4b3c 100644 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java @@ -113,6 +113,7 @@ import org.apache.commons.collections4.CollectionUtils; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.access.AccessDeniedException; @@ -1254,6 +1255,28 @@ public class ControllerFacade implements ControllerServiceProvider { for (final FlowFilePrioritizer comparator : queue.getPriorities()) { addIfAppropriate(searchStr, comparator.getClass().getName(), "Prioritizer", matches); } + + // search expiration + if (StringUtils.containsIgnoreCase("expires", searchStr) || StringUtils.containsIgnoreCase("expiration", searchStr)) { + final int expirationMillis = connection.getFlowFileQueue().getFlowFileExpiration(TimeUnit.MILLISECONDS); + if (expirationMillis > 0) { + matches.add("FlowFile expiration: " + connection.getFlowFileQueue().getFlowFileExpiration()); + } + } + + // search back pressure + if (StringUtils.containsIgnoreCase("back pressure", searchStr) || StringUtils.containsIgnoreCase("pressure", searchStr)) { + final String backPressureDataSize = connection.getFlowFileQueue().getBackPressureDataSizeThreshold(); + final Double backPressureBytes = DataUnit.parseDataSize(backPressureDataSize, DataUnit.B); + if (backPressureBytes > 0) { + matches.add("Back pressure data size: " + backPressureDataSize); + } + + final long backPressureCount = connection.getFlowFileQueue().getBackPressureObjectThreshold(); + if (backPressureCount > 0) { + matches.add("Back pressure count: " + backPressureCount); + } + } // search the source final Connectable source = connection.getSource(); http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/bda9985d/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/connection-configuration.jsp ---------------------------------------------------------------------- diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/connection-configuration.jsp b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/connection-configuration.jsp index eaea170..045b9e9 100644 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/connection-configuration.jsp +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/connection-configuration.jsp @@ -35,7 +35,7 @@ </div> <div class="setting"> <div class="setting-name"> - File expiration + FlowFile expiration <img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="The maximum amount of time an object may be in the flow before it will be automatically aged out of the flow."/> </div> <div class="setting-field"> http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/bda9985d/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/partials/connection-details.jsp ---------------------------------------------------------------------- diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/partials/connection-details.jsp b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/partials/connection-details.jsp index 5e39020..d985de1 100644 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/partials/connection-details.jsp +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/WEB-INF/partials/connection-details.jsp @@ -75,7 +75,7 @@ </div> <div class="setting"> <div class="setting-name"> - File expiration + FlowFile expiration <img class="setting-icon icon-info" src="images/iconInfo.png" alt="Info" title="The maximum amount of time an object may be in the flow before it will be automatically aged out of the flow."/> </div> <div class="setting-field"> http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/bda9985d/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js ---------------------------------------------------------------------- diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js index eb16ad9..42c7f45 100644 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js @@ -311,6 +311,28 @@ nf.Canvas = (function () { 'offset': '100%', 'stop-color': '#ffffff' }); + + // define the gradient for the expiration icon + var expirationBackground = defs.append('linearGradient') + .attr({ + 'id': 'expiration', + 'x1': '0%', + 'y1': '0%', + 'x2': '0%', + 'y2': '100%' + }); + + expirationBackground.append('stop') + .attr({ + 'offset': '0%', + 'stop-color': '#aeafb1' + }); + + expirationBackground.append('stop') + .attr({ + 'offset': '100%', + 'stop-color': '#87888a' + }); // create the canvas element canvas = svg.append('g') @@ -322,135 +344,135 @@ nf.Canvas = (function () { // handle canvas events svg.on('mousedown.selection', function () { - canvasClicked = true; + canvasClicked = true; - if (d3.event.button !== 0) { - // prevent further propagation (to parents and others handlers - // on the same element to prevent zoom behavior) - d3.event.stopImmediatePropagation(); - return; + if (d3.event.button !== 0) { + // prevent further propagation (to parents and others handlers + // on the same element to prevent zoom behavior) + d3.event.stopImmediatePropagation(); + return; + } + + // show selection box if shift is held down + if (d3.event.shiftKey) { + var position = d3.mouse(canvas.node()); + canvas.append('rect') + .attr('rx', 6) + .attr('ry', 6) + .attr('x', position[0]) + .attr('y', position[1]) + .attr('class', 'selection') + .attr('width', 0) + .attr('height', 0) + .attr('stroke-width', function () { + return 1 / nf.Canvas.View.scale(); + }) + .attr('stroke-dasharray', function () { + return 4 / nf.Canvas.View.scale(); + }) + .datum(position); + + // prevent further propagation (to parents) + d3.event.stopPropagation(); + } + }) + .on('mousemove.selection', function () { + // update selection box if shift is held down + if (d3.event.shiftKey) { + // get the selection box + var selectionBox = d3.select('rect.selection'); + if (!selectionBox.empty()) { + // get the original position + var originalPosition = selectionBox.datum(); + var position = d3.mouse(canvas.node()); + + var d = {}; + if (originalPosition[0] < position[0]) { + d.x = originalPosition[0]; + d.width = position[0] - originalPosition[0]; + } else { + d.x = position[0]; + d.width = originalPosition[0] - position[0]; } - // show selection box if shift is held down - if (d3.event.shiftKey) { - var position = d3.mouse(canvas.node()); - canvas.append('rect') - .attr('rx', 6) - .attr('ry', 6) - .attr('x', position[0]) - .attr('y', position[1]) - .attr('class', 'selection') - .attr('width', 0) - .attr('height', 0) - .attr('stroke-width', function () { - return 1 / nf.Canvas.View.scale(); - }) - .attr('stroke-dasharray', function () { - return 4 / nf.Canvas.View.scale(); - }) - .datum(position); - - // prevent further propagation (to parents) - d3.event.stopPropagation(); + if (originalPosition[1] < position[1]) { + d.y = originalPosition[1]; + d.height = position[1] - originalPosition[1]; + } else { + d.y = position[1]; + d.height = originalPosition[1] - position[1]; } - }) - .on('mousemove.selection', function () { - // update selection box if shift is held down - if (d3.event.shiftKey) { - // get the selection box - var selectionBox = d3.select('rect.selection'); - if (!selectionBox.empty()) { - // get the original position - var originalPosition = selectionBox.datum(); - var position = d3.mouse(canvas.node()); - - var d = {}; - if (originalPosition[0] < position[0]) { - d.x = originalPosition[0]; - d.width = position[0] - originalPosition[0]; - } else { - d.x = position[0]; - d.width = originalPosition[0] - position[0]; - } - if (originalPosition[1] < position[1]) { - d.y = originalPosition[1]; - d.height = position[1] - originalPosition[1]; - } else { - d.y = position[1]; - d.height = originalPosition[1] - position[1]; - } + // update the selection box + selectionBox.attr(d); + } - // update the selection box - selectionBox.attr(d); - } + d3.event.stopPropagation(); + } + }) + .on('mouseup.selection', function () { + // ensure this originated from clicking the canvas, not a component. + // when clicking on a component, the event propagation is stopped so + // it never reaches the canvas. we cannot do this however on up events + // since the drag events break down + if (canvasClicked === false) { + return; + } - d3.event.stopPropagation(); - } - }) - .on('mouseup.selection', function () { - // ensure this originated from clicking the canvas, not a component. - // when clicking on a component, the event propagation is stopped so - // it never reaches the canvas. we cannot do this however on up events - // since the drag events break down - if (canvasClicked === false) { - return; - } + // reset the canvas click flag + canvasClicked = false; - // reset the canvas click flag - canvasClicked = false; + // hide the context menu if necessary + nf.ContextMenu.hide(); - // hide the context menu if necessary - nf.ContextMenu.hide(); - - // get the selection box - var selectionBox = d3.select('rect.selection'); - if (!selectionBox.empty()) { - var selectionBoundingBox = { - x: parseInt(selectionBox.attr('x'), 10), - y: parseInt(selectionBox.attr('y'), 10), - width: parseInt(selectionBox.attr('width'), 10), - height: parseInt(selectionBox.attr('height'), 10) - }; + // get the selection box + var selectionBox = d3.select('rect.selection'); + if (!selectionBox.empty()) { + var selectionBoundingBox = { + x: parseInt(selectionBox.attr('x'), 10), + y: parseInt(selectionBox.attr('y'), 10), + width: parseInt(selectionBox.attr('width'), 10), + height: parseInt(selectionBox.attr('height'), 10) + }; - // see if a component should be selected or not - d3.selectAll('g.component').classed('selected', function (d) { - // consider it selected if its already selected or enclosed in the bounding box - return d3.select(this).classed('selected') || - d.component.position.x >= selectionBoundingBox.x && (d.component.position.x + d.dimensions.width) <= (selectionBoundingBox.x + selectionBoundingBox.width) && - d.component.position.y >= selectionBoundingBox.y && (d.component.position.y + d.dimensions.height) <= (selectionBoundingBox.y + selectionBoundingBox.height); - }); - - // see if a connection should be selected or not - d3.selectAll('g.connection').classed('selected', function (d) { - // consider all points - var points = [d.start].concat(d.bends, [d.end]); - - // determine the bounding box - var x = d3.extent(points, function (pt) { - return pt.x; - }); - var y = d3.extent(points, function (pt) { - return pt.y; - }); + // see if a component should be selected or not + d3.selectAll('g.component').classed('selected', function (d) { + // consider it selected if its already selected or enclosed in the bounding box + return d3.select(this).classed('selected') || + d.component.position.x >= selectionBoundingBox.x && (d.component.position.x + d.dimensions.width) <= (selectionBoundingBox.x + selectionBoundingBox.width) && + d.component.position.y >= selectionBoundingBox.y && (d.component.position.y + d.dimensions.height) <= (selectionBoundingBox.y + selectionBoundingBox.height); + }); - // consider it selected if its already selected or enclosed in the bounding box - return d3.select(this).classed('selected') || - x[0] >= selectionBoundingBox.x && x[1] <= (selectionBoundingBox.x + selectionBoundingBox.width) && - y[0] >= selectionBoundingBox.y && y[1] <= (selectionBoundingBox.y + selectionBoundingBox.height); - }); - - // remove the selection box - selectionBox.remove(); - } else if (panning === false) { - // deselect as necessary if we are not panning - nf.CanvasUtils.getSelection().classed('selected', false); - } + // see if a connection should be selected or not + d3.selectAll('g.connection').classed('selected', function (d) { + // consider all points + var points = [d.start].concat(d.bends, [d.end]); - // update the toolbar - nf.CanvasToolbar.refresh(); + // determine the bounding box + var x = d3.extent(points, function (pt) { + return pt.x; + }); + var y = d3.extent(points, function (pt) { + return pt.y; + }); + + // consider it selected if its already selected or enclosed in the bounding box + return d3.select(this).classed('selected') || + x[0] >= selectionBoundingBox.x && x[1] <= (selectionBoundingBox.x + selectionBoundingBox.width) && + y[0] >= selectionBoundingBox.y && y[1] <= (selectionBoundingBox.y + selectionBoundingBox.height); }); + // remove the selection box + selectionBox.remove(); + } else if (panning === false) { + // deselect as necessary if we are not panning + nf.CanvasUtils.getSelection().classed('selected', false); + } + + // update the toolbar + nf.CanvasToolbar.refresh(); + }); + // define a function for update the graph dimensions var updateGraphSize = function () { // get the location of the bottom of the graph @@ -802,17 +824,21 @@ nf.Canvas = (function () { }; return { + CANVAS_OFFSET: 0, + /** * Determines if the current broswer supports SVG. */ SUPPORTS_SVG: !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect, + /** * Hides the splash that is displayed while the application is loading. */ hideSplash: function () { $('#splash').fadeOut(); }, + /** * Stop polling for revision. */ @@ -820,6 +846,7 @@ nf.Canvas = (function () { // set polling flag revisionPolling = false; }, + /** * Remove the status poller. */ @@ -827,6 +854,7 @@ nf.Canvas = (function () { // set polling flag statusPolling = false; }, + /** * Reloads the flow from the server based on the currently specified group id. * To load another group, update nf.Canvas.setGroupId and call nf.Canvas.reload. @@ -865,6 +893,7 @@ nf.Canvas = (function () { }); }).promise(); }, + /** * Reloads the status. */ @@ -878,6 +907,7 @@ nf.Canvas = (function () { }); }).promise(); }, + /** * Initialize NiFi. */ @@ -999,6 +1029,7 @@ nf.Canvas = (function () { }).fail(nf.Common.handleAjaxError); }).fail(nf.Common.handleAjaxError); }, + /** * Defines the gradient colors used to render processors. * @@ -1007,6 +1038,7 @@ nf.Canvas = (function () { defineProcessorColors: function (colors) { setColors(colors, 'processor'); }, + /** * Defines the gradient colors used to render label. * @@ -1015,6 +1047,7 @@ nf.Canvas = (function () { defineLabelColors: function (colors) { setColors(colors, 'label'); }, + /** * Return whether this instance of NiFi is clustered. * @@ -1023,12 +1056,14 @@ nf.Canvas = (function () { isClustered: function () { return clustered === true; }, + /** * Returns whether site to site communications is secure. */ isSecureSiteToSite: function () { return secureSiteToSite; }, + /** * Set the group id. * @@ -1043,6 +1078,7 @@ nf.Canvas = (function () { getGroupId: function () { return groupId; }, + /** * Set the group name. * @@ -1051,12 +1087,14 @@ nf.Canvas = (function () { setGroupName: function (gn) { groupName = gn; }, + /** * Get the group name. */ getGroupName: function () { return groupName; }, + /** * Set the parent group id. * @@ -1065,13 +1103,16 @@ nf.Canvas = (function () { setParentGroupId: function (pgi) { parentGroupId = pgi; }, + /** * Get the parent group id. */ getParentGroupId: function () { return parentGroupId; }, + View: (function () { + /** * Updates component visibility based on their proximity to the screen's viewport. */ @@ -1138,8 +1179,8 @@ nf.Canvas = (function () { .classed('entering', function () { return visible && !wasVisible; }).classed('leaving', function () { - return !visible && wasVisible; - }); + return !visible && wasVisible; + }); }; // get the all components http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/bda9985d/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js ---------------------------------------------------------------------- diff --git a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js index 0b0c40a..8da9f6a 100644 --- a/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js +++ b/nar-bundles/framework-bundle/framework/web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js @@ -186,6 +186,24 @@ nf.Connection = (function () { var isGroup = function (terminal) { return terminal.groupId !== nf.Canvas.getGroupId() && (isInputPortType(terminal.type) || isOutputPortType(terminal.type)); }; + + /** + * Determines whether expiration is configured for the specified connection. + * + * @param {object} connection + * @return {boolean} Whether expiration is configured + */ + var isExpirationConfigured = function (connection) { + if (nf.Common.isDefinedAndNotNull(connection.flowFileExpiration)) { + var match = connection.flowFileExpiration.match(/^(\d+).*/); + if (match !== null && match.length > 0) { + if (parseInt(match[0], 10) > 0) { + return true; + } + } + } + return false; + }; /** * Sorts the specified connections according to the z index. @@ -325,16 +343,16 @@ nf.Connection = (function () { return grouped; }) - .classed('ghost', function (d) { - var ghost = false; + .classed('ghost', function (d) { + var ghost = false; - // if the connection has a relationship that is unavailable, mark it a ghost relationship - if (hasUnavailableRelationship(d)) { - ghost = true; - } + // if the connection has a relationship that is unavailable, mark it a ghost relationship + if (hasUnavailableRelationship(d)) { + ghost = true; + } - return ghost; - }); + return ghost; + }); } updated.each(function (d) { @@ -669,7 +687,7 @@ nf.Connection = (function () { // update the label text connectionFrom.select('text.connection-from') - .each(function (d) { + .each(function () { var connectionFromLabel = d3.select(this); // reset the label name to handle any previous state @@ -677,9 +695,9 @@ nf.Connection = (function () { // apply ellipsis to the label as necessary nf.CanvasUtils.ellipsis(connectionFromLabel, d.component.source.name); - }).append('title').text(function (d) { - return d.component.source.name; - }); + }).append('title').text(function () { + return d.component.source.name; + }); // update the label run status connectionFrom.select('image.connection-from-run-status').attr('xlink:href', function () { @@ -758,8 +776,8 @@ nf.Connection = (function () { // apply ellipsis to the label as necessary nf.CanvasUtils.ellipsis(connectionToLabel, d.component.destination.name); }).append('title').text(function (d) { - return d.component.destination.name; - }); + return d.component.destination.name; + }); // update the label run status connectionTo.select('image.connection-to-run-status').attr('xlink:href', function () { @@ -821,7 +839,7 @@ nf.Connection = (function () { // update the connection name connectionName.select('text.connection-name') - .each(function (d) { + .each(function () { var connectionToLabel = d3.select(this); // reset the label name to handle any previous state @@ -829,9 +847,9 @@ nf.Connection = (function () { // apply ellipsis to the label as necessary nf.CanvasUtils.ellipsis(connectionToLabel, connectionNameValue); - }).append('title').text(function (d) { - return connectionNameValue; - }); + }).append('title').text(function () { + return connectionNameValue; + }); } else { // there is no connection name, but check if the name was previous // rendered so it can be removed @@ -861,11 +879,49 @@ nf.Connection = (function () { .text('Queued'); queued.append('text') + .attr({ + 'class': 'connection-stats-value queued', + 'x': 46, + 'y': 10 + }); + + var expiration = queued.append('g') .attr({ - 'class': 'connection-stats-value queued', - 'x': 46, - 'y': 10 + 'class': 'expiration-icon', + 'transform': 'translate(167, 2)' }); + + expiration.append('circle') + .attr({ + 'cx': 5, + 'cy': 5, + 'r': 4.75, + 'stroke-width': 0.5, + 'stroke': '#87888a', + 'fill': 'url(#expiration)' + }); + + expiration.append('line') + .attr({ + 'x1': 6, + 'y1': 5, + 'x2': 3, + 'y2': 4, + 'stroke': '#fff', + 'stroke-width': 1 + }); + + expiration.append('line') + .attr({ + 'x1': 6, + 'y1': 5, + 'x2': 3, + 'y2': 7, + 'stroke': '#fff', + 'stroke-width': 1 + }); + + expiration.append('title'); } // update the queued vertical positioning as necessary @@ -879,6 +935,15 @@ nf.Connection = (function () { .attr('height', function () { return 5 + (15 * labelCount) + 3; }); + + // determine whether or not to show the expiration icon + connectionLabelContainer.select('g.expiration-icon') + .classed('hidden', function () { + return !isExpirationConfigured(d.component); + }) + .select('title').text(function () { + return 'Expires FlowFiles older than ' + d.component.flowFileExpiration; + }); if (nf.Common.isDFM()) { // only support dragging the label when appropriate
