NIFI-259:

- Initial implementation of viewing and clearing state for a processor.
This commit is contained in:
Matt Gilman 2016-01-13 13:35:24 -05:00
parent d39067ede6
commit d05314c54b
18 changed files with 649 additions and 13 deletions

View File

@ -27,6 +27,7 @@ import javax.xml.bind.annotation.XmlType;
public class ComponentStateDTO {
private String componentId;
private String stateDescription;
private StateMapDTO clusterState;
private StateMapDTO localState;
@ -44,6 +45,20 @@ public class ComponentStateDTO {
this.componentId = componentId;
}
/**
* @return Description of the state this component persists.
*/
@ApiModelProperty(
value = "Description of the state this component persists."
)
public String getStateDescription() {
return stateDescription;
}
public void setStateDescription(String stateDescription) {
this.stateDescription = stateDescription;
}
/**
* @return The cluster state for this component, or null if this NiFi is a standalone instance
*/

View File

@ -33,6 +33,7 @@ public class ControllerServiceDTO extends NiFiComponentDTO {
private String comments;
private String availability;
private String state;
private Boolean persistsState;
private Map<String, String> properties;
private Map<String, PropertyDescriptorDTO> descriptors;
@ -101,6 +102,20 @@ public class ControllerServiceDTO extends NiFiComponentDTO {
this.availability = availability;
}
/**
* @return whether this controller service persists state
*/
@ApiModelProperty(
value = "Whether the controller service persists state."
)
public Boolean getPersistsState() {
return persistsState;
}
public void setPersistsState(Boolean persistsState) {
this.persistsState = persistsState;
}
/**
* @return The state of this controller service. Possible values are ENABLED, ENABLING, DISABLED, DISABLING
*/

View File

@ -37,6 +37,7 @@ public class ProcessorDTO extends NiFiComponentDTO {
private Boolean supportsParallelProcessing;
private Boolean supportsEventDriven;
private Boolean supportsBatching;
private Boolean persistsState;
private String inputRequirement;
private ProcessorConfigDTO config;
@ -122,6 +123,20 @@ public class ProcessorDTO extends NiFiComponentDTO {
this.supportsParallelProcessing = supportsParallelProcessing;
}
/**
* @return whether this processor persists state
*/
@ApiModelProperty(
value = "Whether the processor persists state."
)
public Boolean getPersistsState() {
return persistsState;
}
public void setPersistsState(Boolean persistsState) {
this.persistsState = persistsState;
}
/**
* @return the input requirement of this processor
*/

View File

@ -33,6 +33,7 @@ public class ReportingTaskDTO extends NiFiComponentDTO {
private String state;
private String availability;
private String comments;
private Boolean persistsState;
private String schedulingPeriod;
private String schedulingStrategy;
@ -105,6 +106,20 @@ public class ReportingTaskDTO extends NiFiComponentDTO {
this.schedulingPeriod = schedulingPeriod;
}
/**
* @return whether this reporting task persists state
*/
@ApiModelProperty(
value = "Whether the reporting task persists state."
)
public Boolean getPersistsState() {
return persistsState;
}
public void setPersistsState(Boolean persistsState) {
this.persistsState = persistsState;
}
/**
* @return current scheduling state of the reporting task
*/

View File

@ -18,6 +18,7 @@ package org.apache.nifi.web;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.lf5.util.Resource;
import org.apache.nifi.action.Action;
import org.apache.nifi.action.Component;
import org.apache.nifi.action.FlowChangeAction;
@ -2168,23 +2169,32 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
@Override
public ComponentStateDTO getProcessorState(String groupId, String processorId) {
final StateMap clusterState = processorDAO.getState(groupId, processorId, Scope.CLUSTER);
final StateMap clusterState = isClustered() ? processorDAO.getState(groupId, processorId, Scope.CLUSTER) : null;
final StateMap localState = processorDAO.getState(groupId, processorId, Scope.LOCAL);
return dtoFactory.createComponentStateDTO(processorId, localState, clusterState);
// processor will be non null as it was already found when getting the state
final ProcessorNode processor = processorDAO.getProcessor(groupId, processorId);
return dtoFactory.createComponentStateDTO(processorId, processor.getProcessor().getClass(), localState, clusterState);
}
@Override
public ComponentStateDTO getControllerServiceState(String controllerServiceId) {
final StateMap clusterState = controllerServiceDAO.getState(controllerServiceId, Scope.CLUSTER);
final StateMap clusterState = isClustered() ? controllerServiceDAO.getState(controllerServiceId, Scope.CLUSTER) : null;
final StateMap localState = controllerServiceDAO.getState(controllerServiceId, Scope.LOCAL);
return dtoFactory.createComponentStateDTO(controllerServiceId, localState, clusterState);
// controller service will be non null as it was already found when getting the state
final ControllerServiceNode controllerService = controllerServiceDAO.getControllerService(controllerServiceId);
return dtoFactory.createComponentStateDTO(controllerServiceId, controllerService.getControllerServiceImplementation().getClass(), localState, clusterState);
}
@Override
public ComponentStateDTO getReportingTaskState(String reportingTaskId) {
final StateMap clusterState = reportingTaskDAO.getState(reportingTaskId, Scope.CLUSTER);
final StateMap clusterState = isClustered() ? reportingTaskDAO.getState(reportingTaskId, Scope.CLUSTER) : null;
final StateMap localState = reportingTaskDAO.getState(reportingTaskId, Scope.LOCAL);
return dtoFactory.createComponentStateDTO(reportingTaskId, localState, clusterState);
// reporting task will be non null as it was already found when getting the state
final ReportingTaskNode reportingTask = reportingTaskDAO.getReportingTask(reportingTaskId);
return dtoFactory.createComponentStateDTO(reportingTaskId, reportingTask.getReportingTask().getClass(), localState, clusterState);
}
@Override

View File

@ -32,6 +32,7 @@ import org.apache.nifi.action.details.FlowChangeMoveDetails;
import org.apache.nifi.action.details.FlowChangePurgeDetails;
import org.apache.nifi.action.details.MoveDetails;
import org.apache.nifi.action.details.PurgeDetails;
import org.apache.nifi.annotation.behavior.Stateful;
import org.apache.nifi.annotation.documentation.CapabilityDescription;
import org.apache.nifi.annotation.documentation.Tags;
import org.apache.nifi.authorization.Authority;
@ -279,14 +280,30 @@ public final class DtoFactory {
* @param clusterState cluster state
* @return dto
*/
public ComponentStateDTO createComponentStateDTO(final String componentId, final StateMap localState, final StateMap clusterState) {
public ComponentStateDTO createComponentStateDTO(final String componentId, final Class<?> componentClass, final StateMap localState, final StateMap clusterState) {
final ComponentStateDTO dto = new ComponentStateDTO();
dto.setComponentId(componentId);
dto.setStateDescription(getStateDescription(componentClass));
dto.setLocalState(createStateMapDTO(Scope.LOCAL, localState));
dto.setClusterState(createStateMapDTO(Scope.CLUSTER, clusterState));
return dto;
}
/**
* Gets the description of the state this component persists.
*
* @param componentClass the component class
* @return state description
*/
private String getStateDescription(final Class<?> componentClass) {
final Stateful capabilityDesc = componentClass.getAnnotation(Stateful.class);
if (capabilityDesc != null) {
return capabilityDesc.description();
} else {
return null;
}
}
/**
* Creates a StateMapDTO for the given scope and state map.
*
@ -295,6 +312,10 @@ public final class DtoFactory {
* @return dto
*/
public StateMapDTO createStateMapDTO(final Scope scope, final StateMap stateMap) {
if (stateMap == null) {
return null;
}
final StateMapDTO dto = new StateMapDTO();
dto.setScope(scope.toString());
@ -1066,6 +1087,7 @@ public final class DtoFactory {
dto.setActiveThreadCount(reportingTaskNode.getActiveThreadCount());
dto.setAnnotationData(reportingTaskNode.getAnnotationData());
dto.setComments(reportingTaskNode.getComments());
dto.setPersistsState(reportingTaskNode.getReportingTask().getClass().isAnnotationPresent(Stateful.class));
final Map<String, String> defaultSchedulingPeriod = new HashMap<>();
defaultSchedulingPeriod.put(SchedulingStrategy.TIMER_DRIVEN.name(), SchedulingStrategy.TIMER_DRIVEN.getDefaultSchedulingPeriod());
@ -1133,6 +1155,7 @@ public final class DtoFactory {
dto.setState(controllerServiceNode.getState().name());
dto.setAnnotationData(controllerServiceNode.getAnnotationData());
dto.setComments(controllerServiceNode.getComments());
dto.setPersistsState(controllerServiceNode.getControllerServiceImplementation().getClass().isAnnotationPresent(Stateful.class));
// sort a copy of the properties
final Map<PropertyDescriptor, String> sortedProperties = new TreeMap<>(new Comparator<PropertyDescriptor>() {
@ -1610,6 +1633,7 @@ public final class DtoFactory {
dto.setStyle(node.getStyle());
dto.setParentGroupId(node.getProcessGroup().getIdentifier());
dto.setInputRequirement(node.getInputRequirement().name());
dto.setPersistsState(node.getProcessor().getClass().isAnnotationPresent(Stateful.class));
dto.setType(node.getProcessor().getClass().getCanonicalName());
dto.setName(node.getName());

View File

@ -281,6 +281,7 @@
<include>${staging.dir}/js/nf/canvas/nf-canvas-toolbox.js</include>
<include>${staging.dir}/js/nf/canvas/nf-custom-ui.js</include>
<include>${staging.dir}/js/nf/canvas/nf-queue-listing.js</include>
<include>${staging.dir}/js/nf/canvas/nf-component-state.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>

View File

@ -22,6 +22,7 @@ nf.canvas.script.tags=<script type="text/javascript" src="js/nf/nf-namespace.js?
<script type="text/javascript" src="js/nf/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-queue-listing.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-component-state.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-ui.js?${project.version}"></script>\n\
<script type="text/javascript" src="js/nf/canvas/nf-controller-service.js?${project.version}"></script>\n\

View File

@ -122,6 +122,7 @@
<jsp:include page="/WEB-INF/partials/canvas/flowfile-details-dialog.jsp"/>
<jsp:include page="/WEB-INF/partials/canvas/listing-request-status-dialog.jsp"/>
<jsp:include page="/WEB-INF/partials/canvas/queue-listing.jsp"/>
<jsp:include page="/WEB-INF/partials/canvas/component-state-dialog.jsp"/>
<jsp:include page="/WEB-INF/partials/connection-details.jsp"/>
<div id="faded-background"></div>
<div id="glass-pane"></div>

View File

@ -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.
--%>
<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
<div id="component-state-dialog">
<div class="dialog-content">
<div class="setting">
<div class="setting-name">Name</div>
<div class="setting-field">
<span id="component-state-name"></span>
</div>
</div>
<div class="setting">
<div class="setting-name">Description</div>
<div class="setting-field">
<span id="component-state-description"></span>
</div>
</div>
<div id="component-state-filter-controls">
<div id="component-state-filter-container">
<input type="text" id="component-state-filter"/>
</div>
<div id="component-state-filter-status">
Displaying&nbsp;<span id="displayed-component-state-entries"></span>&nbsp;of&nbsp;<span id="total-component-state-entries"></span>
</div>
</div>
<div id="component-state-table"></div>
<div id="clear-link-container">
<span id="clear-link" class="link">Clear state</span>
</div>
</div>
</div>

View File

@ -22,6 +22,7 @@
@import url(queue-listing.css);
@import url(remote-process-group-configuration.css);
@import url(controller-service.css);
@import url(component-state.css);
@import url(reporting-task.css);
@import url(port-configuration.css);
@import url(port-details.css);

View File

@ -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.
*/
/*
Component state
*/
#component-state-dialog {
position: absolute;
overflow: hidden;
width: 600px;
height: 500px;
font-size: 10px;
z-index: 1301;
display: none;
}
#component-state-description {
height: 50px;
}
/*
Component state filter
*/
#component-state-filter-controls {
float: right;
margin-top: 10px;
margin-right: 2px;
margin-bottom: 7px;
}
#component-state-filter-status {
font-size: 9px;
font-weight: bold;
color: #9f6000;
clear: left;
line-height: normal;
margin-left: 5px;
}
#component-state-filter {
padding: 3px 0px 1px 3px;
font-size: 12px;
height: 18px;
line-height: 20px;
width: 173px;
border: 1px solid #ccc;
float: left;
}
/*
Component state table
*/
#component-state-table {
width: 578px;
height: 235px;
border: 1px solid #666;
}
/*
Clear
*/
#clear-link-container {
margin-top: 16px;
}
#clear-link.disabled {
color: #bbb;
font-style: italic;
text-decoration: none !important;
}

View File

@ -1063,6 +1063,23 @@ nf.Actions = (function () {
nf.QueueListing.listQueue(connection);
},
/**
* Views the state for the specified processor.
*
* @param {selection} selection
*/
viewState: function (selection) {
if (selection.size() !== 1 || !nf.CanvasUtils.isProcessor(selection)) {
return;
}
// get the processor data
var processor = selection.datum();
// view the state for the selected processor
nf.ComponentState.showState(processor.component, nf.CanvasUtils.supportsModification(selection));
},
/**
* Opens the fill color dialog for the component in the specified selection.
*

View File

@ -1151,6 +1151,7 @@ nf.Canvas = (function () {
nf.Settings.init();
nf.Actions.init();
nf.QueueListing.init();
nf.ComponentState.init();
// initialize the component behaviors
nf.Draggable.init();

View File

@ -0,0 +1,358 @@
/*
* 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 */
/**
* Views state for a given component.
*/
nf.ComponentState = (function () {
var config = {
filterText: 'Filter',
styles: {
filterList: 'filter-list'
}
};
/**
* Filters the component state table.
*/
var applyFilter = function () {
// get the dataview
var componentStateTable = $('#component-state-table').data('gridInstance');
// ensure the grid has been initialized
if (nf.Common.isDefinedAndNotNull(componentStateTable)) {
var componentStateData = componentStateTable.getData();
// update the search criteria
componentStateData.setFilterArgs({
searchString: getFilterText()
});
componentStateData.refresh();
}
};
/**
* 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 filter = 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
var matchesKey = item['key'].search(filterExp) >= 0;
var matchesValue = item['value'].search(filterExp) >= 0;
// conditionally consider the scope
var matchesScope = false;
if (nf.Common.isDefinedAndNotNull(item['scope'])) {
matchesScope = item['scope'].search(filterExp) >= 0;
}
return matchesKey || matchesValue || matchesScope;
};
/**
* 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);
};
/**
* 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 getFilterText = function () {
var filterText = '';
var filterField = $('#component-state-filter');
if (!filterField.hasClass(config.styles.filterList)) {
filterText = filterField.val();
}
return filterText;
};
/**
* Clears the component state table.
*/
var clearTable = function () {
var componentStateGrid = $('#component-state-table').data('gridInstance');
var componentStateData = componentStateGrid.getData();
componentStateData.setItems([]);
};
/**
* Loads the table with the component state.
*
* @param {object} componentState
*/
var loadComponentState = function (localState, clusterState) {
var count = 0;
var componentStateGrid = $('#component-state-table').data('gridInstance');
var componentStateData = componentStateGrid.getData();
// begin the update
componentStateData.beginUpdate();
// local state
if (nf.Common.isDefinedAndNotNull(localState)) {
$.each(localState.state, function (i, stateEntry) {
componentStateData.addItem($.extend({
id: count++,
scope: stateEntry.nodeAddress
}, stateEntry));
});
}
if (nf.Common.isDefinedAndNotNull(clusterState)) {
$.each(clusterState.state, function (i, stateEntry) {
componentStateData.addItem($.extend({
id: count++,
scope: 'Cluster'
}, stateEntry));
});
}
// complete the update
componentStateData.endUpdate();
// update the total number of state entries
$('#total-component-state-entries').text(count);
};
/**
* Reset the dialog.
*/
var resetDialog = function () {
// clear the fields
$('#component-state-name').text('');
$('#component-state-description').text('');
$('#total-component-state-entries').text('');
// clear any filter strings
$('#component-state-filter').addClass(config.styles.filterList).val(config.filterText);
// reset clear link
$('#clear-link').removeClass('disabled').attr('title', '');
// clear the table
clearTable();
// clear the component
$('#component-state-table').removeData('component');
};
return {
init: function () {
// intialize the component state filter
$('#component-state-filter').on('focus', function () {
if ($(this).hasClass(config.styles.filterList)) {
$(this).removeClass(config.styles.filterList).val('');
}
}).on('blur', function () {
if ($(this).val() === '') {
$(this).addClass(config.styles.filterList).val(config.filterText);
}
}).on('keyup', function () {
applyFilter();
}).addClass(config.styles.filterList).val(config.filterText);
// initialize the processor configuration dialog
$('#component-state-dialog').modal({
headerText: 'Component State',
overlayBackground: true,
buttons: [{
buttonText: 'Ok',
handler: {
click: function () {
$(this).modal('hide');
}
}
}],
handler: {
close: function () {
resetDialog();
}
}
}).draggable({
containment: 'parent',
handle: '.dialog-header'
});
// clear state link
$('#clear-link').on('click', function () {
if ($(this).hasClass('disabled') === false) {
// clear the table
clearTable();
// clear the state
var revision = nf.Client.getRevision();
var component = $('#component-state-table').data('component');
$.ajax({
type: 'POST',
url: component.uri + '/state/clear-requests',
data: {
version: revision.version,
clientId: revision.clientId
},
dataType: 'json'
}).done(function (response) {
// update the revision
nf.Client.setRevision(response.revision);
// reload the table with no state
loadComponentState()
}).fail(nf.Common.handleAjaxError);
}
});
// initialize the queue listing table
var componentStateColumns = [
{id: 'key', field: 'key', name: 'Key', sortable: true, resizable: true},
{id: 'value', field: 'value', name: 'Value', sortable: true, resizable: true}
];
// conditionally show the cluster node identifier
if (nf.Canvas.isClustered()) {
componentStateColumns.push({id: 'scope', field: 'scope', name: 'Scope', sortable: true, resizable: true, formatter: scopeFormatter});
}
var componentStateOptions = {
forceFitColumns: true,
enableTextSelectionOnCells: true,
enableCellNavigation: false,
enableColumnReorder: false,
autoEdit: false
};
// initialize the dataview
var componentStateData = new Slick.Data.DataView({
inlineFilters: false
});
componentStateData.setItems([]);
componentStateData.setFilterArgs({
searchString: '',
property: 'key'
});
componentStateData.setFilter(filter);
// initialize the sort
sort({
columnId: 'key',
sortAsc: true
}, componentStateData);
// initialize the grid
var componentStateGrid = new Slick.Grid('#component-state-table', componentStateData, componentStateColumns, componentStateOptions);
componentStateGrid.setSelectionModel(new Slick.RowSelectionModel());
componentStateGrid.registerPlugin(new Slick.AutoTooltips());
componentStateGrid.setSortColumn('key', true);
componentStateGrid.onSort.subscribe(function (e, args) {
sort({
columnId: args.sortCol.field,
sortAsc: args.sortAsc
}, componentStateData);
});
// wire up the dataview to the grid
componentStateData.onRowCountChanged.subscribe(function (e, args) {
componentStateGrid.updateRowCount();
componentStateGrid.render();
// update the total number of displayed items
$('#displayed-component-state-entries').text(args.current);
});
componentStateData.onRowsChanged.subscribe(function (e, args) {
componentStateGrid.invalidateRows(args.rows);
componentStateGrid.render();
});
// hold onto an instance of the grid
$('#component-state-table').data('gridInstance', componentStateGrid);
// initialize the number of display items
$('#displayed-component-state-entries').text('0');
},
/**
* Shows the state for a given component.
*
* @param {object} component
* @param {boolean} canClear
*/
showState: function (component, canClear) {
return $.ajax({
type: 'GET',
url: component.uri + '/state',
dataType: 'json'
}).done(function (response) {
var componentState = response.componentState;
var componentStateTable = $('#component-state-table');
// load the table
loadComponentState(componentState.localState, componentState.clusterState);
// populate the name/description
$('#component-state-name').text(component.name);
$('#component-state-description').text(componentState.stateDescription);
// store the component
componentStateTable.data('component', component);
// show the dialog
$('#component-state-dialog').modal('show');
// only activate the link when appropriate
if (canClear === false) {
$('#clear-link').addClass('disabled').attr('title', 'Component state can only be cleared when the component is not actively running');
}
// reset the grid size
var componentStateGrid = componentStateTable.data('gridInstance');
componentStateGrid.resizeCanvas();
}).fail(nf.Common.handleAjaxError);
}
};
}());

View File

@ -231,6 +231,25 @@ nf.ContextMenu = (function () {
(nf.CanvasUtils.isInputPort(selection) && nf.Canvas.getParentGroupId() !== null);
};
/**
* Determines whether the current selection is a processor.
*
* @param {selection} selection
*/
var isStatefulProcessor = function (selection) {
// ensure the correct number of components are selected
if (selection.size() !== 1) {
return false;
}
if (nf.CanvasUtils.isProcessor(selection)) {
var processorData = selection.datum();
return processorData.component.persistsState === true;
} else {
return false;
}
};
/**
* Determines whether the current selection is a process group.
*
@ -399,6 +418,7 @@ nf.ContextMenu = (function () {
{condition: canStopTransmission, menuItem: {img: 'images/iconTransmissionInactive.png', text: 'Disable transmission', action: 'disableTransmission'}},
{condition: supportsStats, menuItem: {img: 'images/iconChart.png', text: 'Stats', action: 'showStats'}},
{condition: canAccessProvenance, menuItem: {img: 'images/iconProvenance.png', imgStyle: 'context-menu-provenance', text: 'Data provenance', action: 'openProvenance'}},
{condition: isStatefulProcessor, menuItem: {img: 'images/iconViewState.png', text: 'View state', action: 'viewState'}},
{condition: canMoveToFront, menuItem: {img: 'images/iconToFront.png', text: 'Bring to front', action: 'toFront'}},
{condition: isConnection, menuItem: {img: 'images/iconGoTo.png', text: 'Go to source', action: 'showSource'}},
{condition: isConnection, menuItem: {img: 'images/iconGoTo.png', text: 'Go to destination', action: 'showDestination'}},

View File

@ -572,24 +572,33 @@ nf.ProcessorConfiguration = (function () {
// get the processor details
var processor = selectionData.component;
var requests = [];
// reload the processor in case an property descriptors have updated
var reloadProcessor = nf.Processor.reload(processor);
requests.push(nf.Processor.reload(processor));
// get the processor history
var loadHistory = $.ajax({
requests.push($.ajax({
type: 'GET',
url: '../nifi-api/controller/history/processors/' + encodeURIComponent(processor.id),
dataType: 'json'
});
}));
// get the processor state if we're a DFM
if (nf.Common.isDFM()) {
requests.push();
}
// once everything is loaded, show the dialog
$.when(reloadProcessor, loadHistory).done(function (processorResponse, historyResponse) {
$.when.apply(window, requests).done(function (processorResponse, historyResponse, stateResponse) {
// get the updated processor
processor = processorResponse[0].processor;
// get the processor history
var processorHistory = historyResponse[0].componentHistory;
console.log(stateResponse);
// record the processor details
$('#processor-configuration').data('processorDetails', processor);