/*
* 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 org.apache.commons.lang3.StringUtils;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.controller.FlowController;
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.Revision;
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.ProcessorDTO;
import org.apache.nifi.web.api.dto.RevisionDTO;
import org.apache.nifi.web.api.dto.SnippetDTO;
import org.apache.nifi.web.dao.SnippetDAO;
import org.apache.nifi.web.util.SnippetUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class StandardSnippetDAO implements SnippetDAO {
private FlowController flowController;
private SnippetUtils snippetUtils;
private StandardSnippet locateSnippet(String snippetId) {
final StandardSnippet snippet = flowController.getSnippetManager().getSnippet(snippetId);
if (snippet == null) {
throw new ResourceNotFoundException(String.format("Unable to find snippet with id '%s'.", snippetId));
}
return snippet;
}
@Override
public FlowSnippetDTO copySnippet(final String groupId, final String snippetId, final Double originX, final Double originY, final String idGenerationSeed) {
try {
// ensure the parent group exist
final ProcessGroup processGroup = flowController.getGroup(groupId);
if (processGroup == null) {
throw new IllegalArgumentException("The specified parent process group could not be found");
}
// get the existing snippet
Snippet existingSnippet = getSnippet(snippetId);
// get the process group
ProcessGroup existingSnippetProcessGroup = flowController.getGroup(existingSnippet.getParentGroupId());
// ensure the group could be found
if (existingSnippetProcessGroup == null) {
throw new IllegalStateException("The parent process group for the existing snippet could not be found.");
}
// generate the snippet contents
FlowSnippetDTO snippetContents = snippetUtils.populateFlowSnippet(existingSnippet, true, false, false);
// resolve sensitive properties
lookupSensitiveProperties(snippetContents);
// copy snippet
snippetContents = snippetUtils.copy(snippetContents, processGroup, idGenerationSeed, true);
// move the snippet if necessary
if (originX != null && originY != null) {
org.apache.nifi.util.SnippetUtils.moveSnippet(snippetContents, originX, originY);
}
try {
// instantiate the snippet and return the contents
flowController.instantiateSnippet(processGroup, snippetContents);
return snippetContents;
} catch (IllegalStateException ise) {
// illegal state will be thrown from instantiateSnippet when there is an issue with the snippet _before_ any of the
// components are actually created. if we've received this exception we want to attempt to roll back any of the
// policies that we've already cloned for this request
snippetUtils.rollbackClonedPolicies(snippetContents);
// rethrow the same exception
throw ise;
}
} catch (ProcessorInstantiationException pie) {
throw new NiFiCoreException(String.format("Unable to copy snippet because processor type '%s' is unknown to this NiFi.",
StringUtils.substringAfterLast(pie.getMessage(), ".")));
}
}
private Map<String, Revision> mapDtoToRevision(final Map<String, RevisionDTO> revisionMap) {
final Map<String, Revision> revisions = new HashMap<>(revisionMap.size());
for (final Map.Entry<String, RevisionDTO> entry : revisionMap.entrySet()) {
final RevisionDTO revisionDto = entry.getValue();
final Revision revision = new Revision(revisionDto.getVersion(), revisionDto.getClientId(), entry.getKey());
revisions.put(entry.getKey(), revision);
}
return revisions;
}
@Override
public Snippet createSnippet(final SnippetDTO snippetDTO) {
// create the snippet request
final StandardSnippet snippet = new StandardSnippet();
snippet.setId(snippetDTO.getId());
snippet.setParentGroupId(snippetDTO.getParentGroupId());
snippet.addProcessors(mapDtoToRevision(snippetDTO.getProcessors()));
snippet.addProcessGroups(mapDtoToRevision(snippetDTO.getProcessGroups()));
snippet.addRemoteProcessGroups(mapDtoToRevision(snippetDTO.getRemoteProcessGroups()));
snippet.addInputPorts(mapDtoToRevision(snippetDTO.getInputPorts()));
snippet.addOutputPorts(mapDtoToRevision(snippetDTO.getOutputPorts()));
snippet.addConnections(mapDtoToRevision(snippetDTO.getConnections()));
snippet.addLabels(mapDtoToRevision(snippetDTO.getLabels()));
snippet.addFunnels(mapDtoToRevision(snippetDTO.getFunnels()));
// ensure this snippet isn't empty
if (snippet.isEmpty()) {
throw new IllegalArgumentException("Cannot create an empty snippet.");
}
// ensure the parent group exist
final ProcessGroup processGroup = flowController.getGroup(snippet.getParentGroupId());
if (processGroup == null) {
throw new IllegalArgumentException("The specified parent process group could not be found.");
}
// store the snippet
flowController.getSnippetManager().addSnippet(snippet);
return snippet;
}
@Override
public void verifyDeleteSnippetComponents(String snippetId) {
final Snippet snippet = locateSnippet(snippetId);
// ensure the parent group exist
final ProcessGroup processGroup = flowController.getGroup(snippet.getParentGroupId());
if (processGroup == null) {
throw new IllegalArgumentException("The specified parent process group could not be found.");
}
// verify the processGroup can remove the snippet
processGroup.verifyCanDelete(snippet);
}
@Override
public void deleteSnippetComponents(String snippetId) {
// verify the action
verifyDeleteSnippetComponents(snippetId);
// locate the snippet in question
final Snippet snippet = locateSnippet(snippetId);
// remove the contents
final ProcessGroup processGroup = flowController.getGroup(snippet.getParentGroupId());
if (processGroup == null) {
throw new IllegalArgumentException("The specified parent process group could not be found.");
}
// remove the underlying components
processGroup.remove(snippet);
}
@Override
public Snippet getSnippet(String snippetId) {
return locateSnippet(snippetId);
}
@Override
public boolean hasSnippet(String snippetId) {
return flowController.getSnippetManager().getSnippet(snippetId) != null;
}
@Override
public void dropSnippet(String snippetId) {
// drop the snippet itself
final StandardSnippet snippet = locateSnippet(snippetId);
flowController.getSnippetManager().removeSnippet(snippet);
}
@Override
public void verifyUpdateSnippetComponent(SnippetDTO snippetDTO) {
final Snippet snippet = locateSnippet(snippetDTO.getId());
// if the group is changing move it
if (snippetDTO.getParentGroupId() != null && !snippet.getParentGroupId().equals(snippetDTO.getParentGroupId())) {
// get the current process group
final ProcessGroup processGroup = flowController.getGroup(snippet.getParentGroupId());
if (processGroup == null) {
throw new IllegalArgumentException("The specified parent process group could not be found.");
}
// get the new process group
final ProcessGroup newProcessGroup = flowController.getGroup(snippetDTO.getParentGroupId());
if (newProcessGroup == null) {
throw new IllegalArgumentException("The new process group could not be found.");
}
// perform the verification
processGroup.verifyCanMove(snippet, newProcessGroup);
}
}
@Override
public Snippet updateSnippetComponents(final SnippetDTO snippetDTO) {
// verify the action
verifyUpdateSnippetComponent(snippetDTO);
// find the snippet in question
final StandardSnippet snippet = locateSnippet(snippetDTO.getId());
// if the group is changing move it
if (snippetDTO.getParentGroupId() != null && !snippet.getParentGroupId().equals(snippetDTO.getParentGroupId())) {
final ProcessGroup currentProcessGroup = flowController.getGroup(snippet.getParentGroupId());
if (currentProcessGroup == null) {
throw new IllegalArgumentException("The current process group could not be found.");
}
final ProcessGroup newProcessGroup = flowController.getGroup(snippetDTO.getParentGroupId());
if (newProcessGroup == null) {
throw new IllegalArgumentException("The new process group could not be found.");
}
// move the snippet
currentProcessGroup.move(snippet, newProcessGroup);
// update its parent group id
snippet.setParentGroupId(snippetDTO.getParentGroupId());
}
return snippet;
}
private void lookupSensitiveProperties(final FlowSnippetDTO snippet) {
// ensure that contents have been specified
if (snippet != null) {
// go through each processor if specified
if (snippet.getProcessors() != null) {
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()) {
lookupSensitiveProperties(group.getContents());
}
}
}
}
private void lookupSensitiveProcessorProperties(final Set<ProcessorDTO> processors) {
final ProcessGroup rootGroup = flowController.getGroup(flowController.getRootGroupId());
// go through each processor
for (final ProcessorDTO processorDTO : processors) {
final ProcessorConfigDTO processorConfig = processorDTO.getConfig();
// ensure that some property configuration have been specified
if (processorConfig != null && processorConfig.getProperties() != null) {
final Map<String, String> processorProperties = processorConfig.getProperties();
// find the corresponding processor
final ProcessorNode processorNode = rootGroup.findProcessor(processorDTO.getId());
if (processorNode == null) {
throw new IllegalArgumentException(String.format("Unable to create snippet because Processor '%s' could not be found", processorDTO.getId()));
}
// look for sensitive properties get the actual value
for (Entry<PropertyDescriptor, String> entry : processorNode.getProperties().entrySet()) {
final PropertyDescriptor descriptor = entry.getKey();
if (descriptor.isSensitive()) {
processorProperties.put(descriptor.getName(), entry.getValue());
}
}
}
}
}
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) {
this.flowController = flowController;
}
public void setSnippetUtils(SnippetUtils snippetUtils) {
this.snippetUtils = snippetUtils;
}
}