package com.thinkbiganalytics.nifi.rest.visitor;
/*-
* #%L
* thinkbig-nifi-rest-client-api
* %%
* Copyright (C) 2017 ThinkBig Analytics
* %%
* Licensed 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.
* #L%
*/
import com.thinkbiganalytics.nifi.rest.client.NiFiRestClient;
import com.thinkbiganalytics.nifi.rest.client.NifiComponentNotFoundException;
import com.thinkbiganalytics.nifi.rest.model.visitor.NifiFlowVisitor;
import com.thinkbiganalytics.nifi.rest.model.visitor.NifiVisitableConnection;
import com.thinkbiganalytics.nifi.rest.model.visitor.NifiVisitableProcessGroup;
import com.thinkbiganalytics.nifi.rest.model.visitor.NifiVisitableProcessor;
import com.thinkbiganalytics.nifi.rest.support.NifiConnectionUtil;
import com.thinkbiganalytics.nifi.rest.support.NifiProcessUtil;
import org.apache.nifi.web.api.dto.ConnectableDTO;
import org.apache.nifi.web.api.dto.ConnectionDTO;
import org.apache.nifi.web.api.dto.ProcessGroupDTO;
import org.apache.nifi.web.api.dto.ProcessorDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*/
public class NifiConnectionOrderVisitor implements NifiFlowVisitor {
private static final Logger log = LoggerFactory.getLogger(NifiConnectionOrderVisitor.class);
protected NifiVisitableProcessGroup currentProcessGroup;
private NifiVisitableProcessGroup processGroup;
private Map<String, ProcessorDTO> processorsMap = new HashMap<>();
private Map<String, NifiVisitableProcessor> visitedProcessors = new HashMap<>();
private Map<String, NifiVisitableProcessGroup> visitedProcessGroups = new HashMap<>();
private Set<NifiVisitableConnection> allConnections = new HashSet<>();
private NiFiRestClient restClient;
private NifiConnectionOrderVisitorCache cache;
public NifiConnectionOrderVisitor(NiFiRestClient restClient, NifiVisitableProcessGroup processGroup, NifiConnectionOrderVisitorCache cache) {
this.restClient = restClient;
this.processGroup = processGroup;
this.currentProcessGroup = processGroup;
this.processorsMap = NifiProcessUtil.getProcessorsMap(processGroup.getDto());
this.cache = cache;
if (cache == null) {
cache = new NifiConnectionOrderVisitorCache();
}
}
@Override
public void visitProcessor(NifiVisitableProcessor processor) {
visitedProcessors.put(processor.getDto().getId(), processor);
//add the pointer to the ProcessGroup
currentProcessGroup.addProcessor(processor);
}
@Override
public NifiVisitableProcessor getProcessor(String id) {
return visitedProcessors.get(id);
}
@Override
public NifiVisitableProcessGroup getProcessGroup(String id) {
return visitedProcessGroups.get(id);
}
@Override
public void visitConnection(NifiVisitableConnection connection) {
Set<String> relationships = connection.getDto().getSelectedRelationships();
String sourceType = connection.getDto().getSource().getType();
String destType = connection.getDto().getDestination().getType();
List<NifiVisitableProcessor> destinationProcessors = getDestinationProcessors(connection.getDto(), true);
List<NifiVisitableProcessor> sourceProcessors = getSourceProcessors(connection.getDto());
if (destinationProcessors != null) {
destinationProcessors.forEach(destinationProcessor -> destinationProcessor.addSourceConnectionIdentifier(connection.getDto()));
}
if (destinationProcessors != null && sourceProcessors != null) {
for (NifiVisitableProcessor destination : destinationProcessors) {
for (NifiVisitableProcessor source : sourceProcessors) {
destination.addSource(source);
source.addDestination(destination);
}
}
}
for (NifiVisitableProcessor sourceProcessor : sourceProcessors) {
sourceProcessor.addDestinationConnectionIdentifier(connection.getDto());
}
allConnections.add(connection);
}
@Override
public void visitProcessGroup(NifiVisitableProcessGroup processGroup) {
log.debug(" Visit Process Group: {}, ({}) ", processGroup.getDto().getName(), processGroup.getDto().getId());
NifiVisitableProcessGroup group = visitedProcessGroups.get(processGroup.getDto().getId());
if (group == null) {
group = processGroup;
}
this.currentProcessGroup = group;
if (processGroup.getParentProcessGroup() == null) {
try {
ProcessGroupDTO parent = fetchProcessGroupForNameAndIdentifier(processGroup.getDto().getParentGroupId());
if (parent != null) {
group.setParentProcessGroup(parent);
}
} catch (NifiComponentNotFoundException e) {
//cant find the parent
}
}
group.accept(this);
this.visitedProcessGroups.put(group.getDto().getId(), group);
}
public List<NifiVisitableProcessor> getDestinationProcessors(ConnectionDTO connection, boolean getSource) {
ConnectableDTO dest = connection.getDestination();
List<NifiVisitableProcessor> destinationProcessors = new ArrayList<>();
if (dest != null) {
if ("INPUT_PORT".equalsIgnoreCase(dest.getType())) {
boolean isNew = false;
NifiVisitableProcessGroup group = visitedProcessGroups.get(dest.getGroupId());
if (group == null) {
group = fetchProcessGroup(dest.getGroupId());
}
ConnectionDTO conn = group.getConnectionMatchingSourceId(dest.getId());
if (conn != null) {
destinationProcessors = getDestinationProcessors(conn, getSource);
if (getSource) {
List<NifiVisitableProcessor> outputProcessors = getSourceProcessors(connection);
if (outputProcessors != null) {
for (NifiVisitableProcessor outputProcessor : outputProcessors) {
//outputProcessor.setOutputPortId(dest.getId());
currentProcessGroup.addOutputPortProcessor(dest.getId(), outputProcessor);
}
}
}
}
} else if ("OUTPUT_PORT".equals(dest.getType())) {
boolean isNew = false;
//get parent processgroup connection to input port
NifiVisitableProcessGroup group = visitedProcessGroups.get(dest.getGroupId());
if (group == null) {
group = fetchProcessGroup(dest.getGroupId());
}
ConnectionDTO conn = group.getConnectionMatchingSourceId(dest.getId());
if (conn == null) {
conn = searchConnectionMatchingSource(group.getDto().getParentGroupId(), dest.getId());
}
if (conn != null) {
//get the processor whos source matches this connection Id
List<NifiVisitableProcessor> destinations = getDestinationProcessors(conn, getSource);
if (destinations != null) {
destinationProcessors.addAll(destinations);
}
if (getSource) {
List<NifiVisitableProcessor> outputProcessors = getSourceProcessors(connection);
if (outputProcessors != null) {
for (NifiVisitableProcessor outputProcessor : outputProcessors) {
currentProcessGroup.addOutputPortProcessor(dest.getId(), outputProcessor);
}
}
}
}
} else if ("FUNNEL".equals(dest.getType())) {
List<ConnectionDTO>
passThroughConnections =
NifiConnectionUtil.findConnectionsMatchingSourceId(currentProcessGroup.getDto().getContents().getConnections(), connection.getDestination().getId());
if (passThroughConnections != null) {
for (ConnectionDTO dto : passThroughConnections) {
ConnectionDTO newConnection = new ConnectionDTO();
newConnection.setId(connection.getSource().getId());
newConnection.setSource(connection.getSource());
newConnection.setDestination(dto.getDestination());
List<NifiVisitableProcessor> destinations = getDestinationProcessors(newConnection, getSource);
if (destinations != null) {
// destinationProcessor.setOutputPortId(dest.getId());
destinationProcessors.addAll(destinations);
}
}
}
} else if ("PROCESSOR".equals(dest.getType())) {
NifiVisitableProcessor destinationProcessor = getConnectionProcessor(dest.getGroupId(), dest.getId());
destinationProcessors.add(destinationProcessor);
}
}
for (NifiVisitableProcessor destinationProcessor : destinationProcessors) {
destinationProcessor.addSourceConnectionIdentifier(connection);
}
return destinationProcessors;
}
public List<NifiVisitableProcessor> getSourceProcessors(ConnectionDTO connection) {
ConnectableDTO source = connection.getSource();
List<NifiVisitableProcessor> sourceProcessors = new ArrayList<>();
if (source != null) {
if ("INPUT_PORT".equalsIgnoreCase(source.getType())) {
NifiVisitableProcessGroup group = visitedProcessGroups.get(source.getGroupId());
if (group == null) {
group = processGroup;
}
NifiVisitableProcessGroup parent = visitedProcessGroups.get(group.getDto().getParentGroupId());
//if the parent is null the parent is the starting process group
if (parent == null) {
parent = processGroup;
}
ConnectionDTO conn = parent.getConnectionMatchingDestinationId(source.getId());
if (conn == null) {
//if its null get it from the cache and process with that.
conn =
cache.getProcessGroupCache().values().stream().flatMap(g -> g.getContents().getConnections().stream())
.filter(connectionDTO -> connectionDTO.getDestination().getId().equalsIgnoreCase(source.getId())).findFirst().orElse(null);
}
if (conn != null && conn != connection) {
//get the processor whos source matches this connection Id
sourceProcessors = getSourceProcessors(conn);
//assign the inputPortProcessor == the the destination of this connection
}
List<NifiVisitableProcessor> inputProcessors = getDestinationProcessors(connection, false);
if (inputProcessors != null) {
for (NifiVisitableProcessor inputProcessor : inputProcessors) {
currentProcessGroup.addInputPortProcessor(source.getId(), inputProcessor);
}
}
} else if ("OUTPUT_PORT".equals(source.getType())) {
//get the sources group id then get the ending processor for that group
NifiVisitableProcessGroup group = visitedProcessGroups.get(source.getGroupId());
if (group != null) {
Set<NifiVisitableProcessor> sources = group.getOutputPortProcessors(source.getId());
if (sourceProcessors != null && sources != null) {
sourceProcessors.addAll(sources);
}
/*
If a process group is connected to another process group without any processors there will be no source processors
having sourceProcessors as null here is ok.
*/
}
} else if ("FUNNEL".equalsIgnoreCase(source.getType())) {
List<ConnectionDTO> passThroughConnections = NifiConnectionUtil.findConnectionsMatchingDestinationId(currentProcessGroup.getDto().getContents().getConnections(),
connection.getSource().getId());
if (passThroughConnections != null) {
for (ConnectionDTO dto : passThroughConnections) {
ConnectionDTO newConnection = new ConnectionDTO();
newConnection.setSource(dto.getSource());
newConnection.setId(connection.getSource().getId());
newConnection.setDestination(connection.getDestination());
visitConnection(new NifiVisitableConnection(currentProcessGroup, newConnection));
}
}
} else if ("PROCESSOR".equals(source.getType())) {
NifiVisitableProcessor sourceProcessor = getConnectionProcessor(source.getGroupId(), source.getId());
sourceProcessors.add(sourceProcessor);
}
for (NifiVisitableProcessor sourceProcessor : sourceProcessors) {
sourceProcessor.addDestinationConnectionIdentifier(connection);
}
}
return sourceProcessors;
}
private ProcessGroupDTO fetchProcessGroupForNameAndIdentifier(String groupId) {
//fetch it
ProcessGroupDTO processGroupEntity = null;
try {
try {
log.debug("fetchProcessGroup {} ", groupId);
processGroupEntity = getGroup(groupId);
} catch (NifiComponentNotFoundException e) {
log.debug("Unable to find the process group " + groupId);
}
} catch (Exception e) {
log.error("Exception fetching the process group " + groupId);
}
return processGroupEntity;
}
private NifiVisitableProcessGroup fetchProcessGroup(String groupId) {
NifiVisitableProcessGroup group = processGroup;
//fetch it
ProcessGroupDTO processGroupEntity = null;
try {
try {
log.debug("fetchProcessGroup {} ", groupId);
processGroupEntity = getGroup(groupId);
} catch (NifiComponentNotFoundException e) {
log.debug("Unable to find the process group " + groupId);
}
//if the parent is null the parent is the starting process group
if (processGroupEntity != null) {
group = new NifiVisitableProcessGroup(processGroupEntity);
}
} catch (Exception e) {
log.error("Exception fetching the process group " + groupId);
}
return group;
}
private ConnectionDTO searchConnectionMatchingSource(String parentGroupId, String destinationId) {
//search up to find the connection that matches this dest id
try {
ProcessGroupDTO parent = null;
try {
log.debug("fetch ProcessGroup for searchConnectionMatchingSource {} ", parentGroupId);
parent = getGroup(parentGroupId);
} catch (NifiComponentNotFoundException e) {
log.debug("Exception searching Connection matching the source. Parent Group ID: " + parentGroupId + ", and destinationId of " + destinationId);
}
if (parent != null) {
//processGroup.getDto().setParent(parentParent.getProcessGroup());
//get Contents of this parent
NifiVisitableProcessGroup visitableProcessGroup = new NifiVisitableProcessGroup(parent);
ConnectionDTO conn = visitableProcessGroup.getConnectionMatchingSourceId(destinationId);
if (conn != null) {
return conn;
}
if (conn == null && parent.getParentGroupId() != null) {
return searchConnectionMatchingSource(parent.getParentGroupId(), destinationId);
}
}
} catch (Exception e) {
log.error("Exception searching Connection matching the source. Parent Group ID: " + parentGroupId + ", and destinationId of " + destinationId);
}
return null;
}
private ConnectionDTO searchConnectionMatchingDestination(String parentGroupId, String sourceId) {
//search up to find the connectoin that matches this dest id
try {
ProcessGroupDTO parent = null;
try {
parent = getGroup(parentGroupId);
} catch (NifiComponentNotFoundException e) {
log.debug("Exception searching Connection matching the destination. Parent Group ID: " + parentGroupId + ", and destinationId of " + sourceId);
}
if (parent != null) {
//get Contents of this parent
NifiVisitableProcessGroup visitableProcessGroup = new NifiVisitableProcessGroup(parent);
ConnectionDTO conn = visitableProcessGroup.getConnectionMatchingDestinationId(sourceId);
if (conn != null) {
return conn;
}
if (conn == null && parent.getParentGroupId() != null) {
return searchConnectionMatchingSource(parent.getParentGroupId(), sourceId);
}
}
} catch (Exception e) {
log.error("Exception searching Connection matching the destination. Parent Group ID: " + parentGroupId + ", and source of " + sourceId);
}
return null;
}
private NifiVisitableProcessor getConnectionProcessor(String groupId, String id) {
NifiVisitableProcessor processor = visitedProcessors.get(id);
if (processor == null) {
if (!this.processorsMap.containsKey(id)) {
//if the current group is not related to this processgroup then attempt to walk this processors processgroup
try {
log.debug("fetch ProcessGroup for getConnectionProcessor {} ", groupId);
ProcessGroupDTO processGroupEntity = getGroup(groupId);
if (processGroupEntity != null) {
ProcessorDTO processorDTO = NifiProcessUtil.findFirstProcessorsById(
processGroupEntity.getContents().getProcessors(), id);
if (processorDTO != null) {
this.processorsMap.put(id, processorDTO);
}
if (processGroup.getDto().getId() != groupId && !visitedProcessGroups.containsKey(processGroupEntity.getId())) {
visitProcessGroup(new NifiVisitableProcessGroup(processGroupEntity));
}
} else {
log.error("getConnectionProcessor error. Unable to find Process Group for process group id: {}, and processor: {} {} ", groupId, id);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//
processor = visitedProcessors.get(id);
if (processor == null) {
processor = new NifiVisitableProcessor(this.processorsMap.get(id));
//visit the group?
processor.accept(this);
}
}
return processor;
}
public NifiVisitableProcessGroup getProcessGroup() {
return processGroup;
}
public Map<String, ProcessorDTO> getProcessorsMap() {
return processorsMap;
}
public Map<String, NifiVisitableProcessor> getVisitedProcessors() {
return visitedProcessors;
}
public Map<String, NifiVisitableProcessGroup> getVisitedProcessGroups() {
return visitedProcessGroups;
}
public Set<NifiVisitableConnection> getAllConnections() {
return allConnections;
}
public NifiConnectionOrderVisitorCachedItem toCachedItem() {
return new NifiConnectionOrderVisitorCachedItem(this);
}
public Integer getNumberOfSplits() {
int count = 0;
for (NifiVisitableProcessor processor : visitedProcessors.values()) {
Set<NifiVisitableProcessor> destinations = processor.getDestinations();
if (destinations != null && !destinations.isEmpty()) {
count += (destinations.size() - 1);
}
}
return count;
}
/**
* inspect the current status and determine if it has data in queue
*/
public boolean isProcessingData() {
return false;
}
public void printOrder() {
for (NifiVisitableProcessor processor : processGroup.getStartingProcessors()) {
processor.print();
}
}
private ProcessGroupDTO getGroup(String processGroupId) {
ProcessGroupDTO group = null;
if (cache.getProcessGroup(processGroupId).isPresent()) {
group = cache.getProcessGroup(processGroupId).get();
} else {
group = getRestClient().processGroups().findById(processGroupId, true, true).orElse(null);
if (group != null) {
cache.add(group);
NifiProcessUtil.getProcessGroups(group).stream().forEach(processGroupDTO -> cache.add(processGroupDTO));
}
}
return group;
}
private NiFiRestClient getRestClient() {
return restClient;
}
}