container/src/main/java/com/streamsets/datacollector/validation/ValidationUtil.java
public static boolean validateStageConfiguration(
    StageLibraryTask stageLibrary,
    boolean shouldBeSource,
    StageConfiguration stageConf,
    boolean notOnMainCanvas,
    IssueCreator issueCreator,
    boolean isPipelineFragment,
    Map<String, Object> constants,
    List<Issue> issues
  ) {
    boolean preview = true;
    StageDefinition stageDef = stageLibrary.getStage(
        stageConf.getLibrary(),
        stageConf.getStageName(),
        false
    );
    if (stageDef == null) {
      // stage configuration refers to an undefined stage definition
      issues.add(
          issueCreator.create(
              stageConf.getInstanceName(),
              ValidationError.VALIDATION_0006,
              stageConf.getLibrary(),
              stageConf.getStageName(),
              stageConf.getStageVersion()
          )
      );
      preview = false;
    } else {
      if (shouldBeSource) {
        if (stageDef.getType() != StageType.SOURCE && !isPipelineFragment) {
          // first stage must be a Source
          issues.add(issueCreator.create(stageConf.getInstanceName(), ValidationError.VALIDATION_0003));
          preview = false;
        }
      } else {
        if (!stageLibrary.isMultipleOriginSupported() && stageDef.getType() == StageType.SOURCE) {
          // no stage other than first stage can be a Source
          issues.add(issueCreator.create(stageConf.getInstanceName(), ValidationError.VALIDATION_0004));
          preview = false;
        }
      }
      if (!stageConf.isSystemGenerated() && !TextUtils.isValidName(stageConf.getInstanceName())) {
        // stage instance name has an invalid name (it must match '[0-9A-Za-z_]+')
        issues.add(
            issueCreator.create(
                stageConf.getInstanceName(),
                ValidationError.VALIDATION_0016,
                stageConf.getInstanceName(),
                TextUtils.VALID_NAME
            )
        );
        preview = false;
      }

      // Hidden stages can't appear on the main canvas
      if(!notOnMainCanvas && !stageDef.getHideStage().isEmpty()) {
        issues.add(issueCreator.create(stageConf.getInstanceName(), ValidationError.VALIDATION_0037));
        preview = false;
      }

      for (String lane : stageConf.getInputLanes()) {
        if (!TextUtils.isValidName(lane)) {
          // stage instance input lane has an invalid name (it must match '[0-9A-Za-z_]+')
          issues.add(
              issueCreator.create(
                  stageConf.getInstanceName(),
                  ValidationError.VALIDATION_0017,
                  lane,
                  TextUtils.VALID_NAME
              )
          );
          preview = false;
        }
      }
      for (String lane : stageConf.getOutputLanes()) {
        if (!TextUtils.isValidName(lane)) {
          // stage instance output lane has an invalid name (it must match '[0-9A-Za-z_]+')
          issues.add(
              issueCreator.create(
                  stageConf.getInstanceName(),
                  ValidationError.VALIDATION_0018,
                  lane,
                  TextUtils.VALID_NAME
              )
          );
          preview = false;
        }
      }
      for (String lane : stageConf.getEventLanes()) {
        if (!TextUtils.isValidName(lane)) {
          // stage instance output lane has an invalid name (it must match '[0-9A-Za-z_]+')
          issues.add(
              issueCreator.create(
                  stageConf.getInstanceName(),
                  ValidationError.VALIDATION_0100,
                  lane,
                  TextUtils.VALID_NAME
              )
          );
          preview = false;
        }
      }

      // Special validation for stage exposed limit of input lanes
      // -1: Means that framework is fully in control on how many input lanes should be present
      //  0: Means that the stage supports unlimited number of input lanes
      // >0: Means that stage needs exactly that amount of lanes which we will validate here
      if(stageDef.getInputStreams() > 0) {
        if(stageDef.getInputStreams() != stageConf.getInputLanes().size()) {
           issues.add(
              issueCreator.create(
                  stageConf.getInstanceName(),
                  ValidationError.VALIDATION_0094,
                  stageDef.getInputStreams(),
                  stageConf.getInputLanes().size()
              )
          );
        }
      }

      // Validate proper input/output lane configuration
      switch (stageDef.getType()) {
        case SOURCE:
          if (!stageConf.getInputLanes().isEmpty()) {
            // source stage cannot have input lanes
            issues.add(
                issueCreator.create(
                    stageConf.getInstanceName(),
                    ValidationError.VALIDATION_0012,
                    stageDef.getType(),
                    stageConf.getInputLanes()
                )
            );
            preview = false;
          }
          if (!notOnMainCanvas && !stageDef.isVariableOutputStreams()) {
            // source stage must match the output stream defined in StageDef
            if (stageDef.getOutputStreams() != stageConf.getOutputLanes().size()) {
              issues.add(
                  issueCreator.create(
                      stageConf.getInstanceName(),
                      ValidationError.VALIDATION_0015,
                      stageDef.getOutputStreams(),
                      stageConf.getOutputLanes().size()
                  )
              );
            }
          } else if (!notOnMainCanvas && stageConf.getOutputLanes().isEmpty()) {
            // source stage must have at least one output lane
            issues.add(issueCreator.create(stageConf.getInstanceName(), ValidationError.VALIDATION_0032));
          }
          break;
        case PROCESSOR:
          if (!notOnMainCanvas && stageConf.getInputLanes().isEmpty() && !isPipelineFragment) {
            // processor stage must have at least one input lane
            issues.add(
                issueCreator.create(
                    stageConf.getInstanceName(),
                    ValidationError.VALIDATION_0014,
                    "Processor"
                )
            );
            preview = false;
          }
          if(!notOnMainCanvas) {
            if (!stageDef.isVariableOutputStreams()) {
              // processor stage must match the output stream defined in StageDef
              if (stageDef.getOutputStreams() != stageConf.getOutputLanes().size()) {
                issues.add(
                  issueCreator.create(
                    stageConf.getInstanceName(),
                    ValidationError.VALIDATION_0015,
                    stageDef.getOutputStreams(),
                    stageConf.getOutputLanes().size()
                  )
                );
              }
            } else if (stageConf.getOutputLanes().isEmpty()) {
              // processor stage must have at least one output lane
              issues.add(issueCreator.create(stageConf.getInstanceName(), ValidationError.VALIDATION_0032));
            }
          }
          break;
        case EXECUTOR:
        case TARGET:
          // Normal target stage must have at least one input lane
          if (!notOnMainCanvas && stageConf.getInputLanes().isEmpty() && !isPipelineFragment) {
            issues.add(
                issueCreator.create(
                    stageConf.getInstanceName(),
                    ValidationError.VALIDATION_0014,
                    "Target"
                )
            );
            preview = false;
          }
          // Error/Stats/Pipeline lifecycle must not have an input lane
          if (notOnMainCanvas && !stageConf.getInputLanes().isEmpty()) {
            issues.add(
                issueCreator.create(
                    stageConf.getInstanceName(),
                    ValidationError.VALIDATION_0012,
                    "Error/Stats/Lifecycle",
                    stageConf.getInputLanes()
                )
            );
            preview = false;
          }
          if (!stageConf.getOutputLanes().isEmpty()) {
            // target stage cannot have output lanes
            issues.add(
                issueCreator.create(
                    stageConf.getInstanceName(),
                    ValidationError.VALIDATION_0013,
                    stageDef.getType(),
                    stageConf.getOutputLanes()
                )
            );
            preview = false;
          }
          if (notOnMainCanvas && !stageConf.getEventLanes().isEmpty()) {
            issues.add(
                issueCreator.create(
                    stageConf.getInstanceName(),
                    ValidationError.VALIDATION_0036,
                    stageDef.getType(),
                    stageConf.getEventLanes()
                )
            );
            preview = false;
          }
          break;
        default:
          throw new IllegalStateException("Unexpected stage type " + stageDef.getType());
      }

      // Validate proper event configuration
      if(!notOnMainCanvas && stageConf.getEventLanes().size() > 1) {
        issues.add(
          issueCreator.create(
            stageConf.getInstanceName(),
            ValidationError.VALIDATION_0101
          )
        );
        preview = false;
      }
      if(!notOnMainCanvas && !stageDef.isProducingEvents() && stageConf.getEventLanes().size() > 0) {
        issues.add(
          issueCreator.create(
            stageConf.getInstanceName(),
            ValidationError.VALIDATION_0102
          )
        );
        preview = false;
      }

      // Validate stage owns configuration
      preview &= validateComponentConfigs(
        stageConf,
        stageDef.getConfigDefinitions(),
        stageDef.getConfigDefinitionsMap(),
        stageDef.getHideConfigs(),
        stageDef.hasPreconditions(),
        constants,
        issueCreator,
        issues
      );

      // Validate service definitions
      Set<String> expectedServices = stageDef.getServices().stream()
        .map(service -> service.getService().getName())
        .collect(Collectors.toSet());
      Set<String> configuredServices = stageConf.getServices().stream()
        .map(service -> service.getService().getName())
        .collect(Collectors.toSet());
      if(!expectedServices.equals(configuredServices)) {
        issues.add(issueCreator.create(
            stageConf.getInstanceName(),
            ValidationError.VALIDATION_0200,
            StringUtils.join(expectedServices, ","),
            StringUtils.join(configuredServices, ",")
        ));
        preview = false;
      } else {
        // Validate all services
        for (ServiceConfiguration serviceConf: stageConf.getServices()) {
          ServiceDefinition serviceDef = stageLibrary.getServiceDefinition(serviceConf.getService(), false);
          preview &= validateComponentConfigs(
            serviceConf,
            serviceDef.getConfigDefinitions(),
            serviceDef.getConfigDefinitionsMap(),
            Collections.emptySet(),
            false,
            constants,
            issueCreator.forService(serviceConf.getService().getName()),
            issues
          );
        }
      }
    }
    return preview;
  }
public static boolean validateStageConfiguration(
    StageLibraryTask stageLibrary,
    boolean shouldBeSource,
    StageConfiguration stageConf,
    boolean notOnMainCanvas,
    IssueCreator issueCreator,
    boolean isPipelineFragment,
    Map<String, Object> constants,
    List<Issue> issues
  ) {
    boolean preview = true;
    StageDefinition stageDef = stageLibrary.getStage(
        stageConf.getLibrary(),
        stageConf.getStageName(),
        false
    );
    if (stageDef == null) {
      // stage configuration refers to an undefined stage definition
      issues.add(
          issueCreator.create(
              stageConf.getInstanceName(),
              ValidationError.VALIDATION_0006,
              stageConf.getLibrary(),
              stageConf.getStageName(),
              stageConf.getStageVersion()
          )
      );
      preview = false;
    } else {
      if (shouldBeSource) {
        if (stageDef.getType() != StageType.SOURCE && !isPipelineFragment) {
          // first stage must be a Source
          issues.add(issueCreator.create(stageConf.getInstanceName(), ValidationError.VALIDATION_0003));
          preview = false;
        }
      } else {
        if (!stageLibrary.isMultipleOriginSupported() && stageDef.getType() == StageType.SOURCE) {
          // no stage other than first stage can be a Source
          issues.add(issueCreator.create(stageConf.getInstanceName(), ValidationError.VALIDATION_0004));
          preview = false;
        }
      }
      if (!stageConf.isSystemGenerated() && !TextUtils.isValidName(stageConf.getInstanceName())) {
        // stage instance name has an invalid name (it must match '[0-9A-Za-z_]+')
        issues.add(
            issueCreator.create(
                stageConf.getInstanceName(),
                ValidationError.VALIDATION_0016,
                stageConf.getInstanceName(),
                TextUtils.VALID_NAME
            )
        );
        preview = false;
      }

      // Hidden stages can't appear on the main canvas
      if(!notOnMainCanvas && !stageDef.getHideStage().isEmpty()) {
        issues.add(issueCreator.create(stageConf.getInstanceName(), ValidationError.VALIDATION_0037));
        preview = false;
      }

      for (String lane : stageConf.getInputLanes()) {
        if (!TextUtils.isValidName(lane)) {
          // stage instance input lane has an invalid name (it must match '[0-9A-Za-z_]+')
          issues.add(
              issueCreator.create(
                  stageConf.getInstanceName(),
                  ValidationError.VALIDATION_0017,
                  lane,
                  TextUtils.VALID_NAME
              )
          );
          preview = false;
        }
      }
      for (String lane : stageConf.getOutputLanes()) {
        if (!TextUtils.isValidName(lane)) {
          // stage instance output lane has an invalid name (it must match '[0-9A-Za-z_]+')
          issues.add(
              issueCreator.create(
                  stageConf.getInstanceName(),
                  ValidationError.VALIDATION_0018,
                  lane,
                  TextUtils.VALID_NAME
              )
          );
          preview = false;
        }
      }
      for (String lane : stageConf.getEventLanes()) {
        if (!TextUtils.isValidName(lane)) {
          // stage instance output lane has an invalid name (it must match '[0-9A-Za-z_]+')
          issues.add(
              issueCreator.create(
                  stageConf.getInstanceName(),
                  ValidationError.VALIDATION_0100,
                  lane,
                  TextUtils.VALID_NAME
              )
          );
          preview = false;
        }
      }

      // Special validation for stage exposed limit of input lanes
      // -1: Means that framework is fully in control on how many input lanes should be present
      //  0: Means that the stage supports unlimited number of input lanes
      // >0: Means that stage needs exactly that amount of lanes which we will validate here
      if(stageDef.getInputStreams() > 0) {
        if(stageDef.getInputStreams() != stageConf.getInputLanes().size()) {
           issues.add(
              issueCreator.create(
                  stageConf.getInstanceName(),
                  ValidationError.VALIDATION_0094,
                  stageDef.getInputStreams(),
                  stageConf.getInputLanes().size()
              )
          );
        }
      }

      // Validate proper input/output lane configuration
      switch (stageDef.getType()) {
        case SOURCE:
          if (!stageConf.getInputLanes().isEmpty()) {
            // source stage cannot have input lanes
            issues.add(
                issueCreator.create(
                    stageConf.getInstanceName(),
                    ValidationError.VALIDATION_0012,
                    stageDef.getType(),
                    stageConf.getInputLanes()
                )
            );
            preview = false;
          }
          if (!notOnMainCanvas && !stageDef.isVariableOutputStreams()) {
            // source stage must match the output stream defined in StageDef
            if (stageDef.getOutputStreams() != stageConf.getOutputLanes().size()) {
              issues.add(
                  issueCreator.create(
                      stageConf.getInstanceName(),
                      ValidationError.VALIDATION_0015,
                      stageDef.getOutputStreams(),
                      stageConf.getOutputLanes().size()
                  )
              );
            }
          } else if (!notOnMainCanvas && stageConf.getOutputLanes().isEmpty()) {
            // source stage must have at least one output lane
            issues.add(issueCreator.create(stageConf.getInstanceName(), ValidationError.VALIDATION_0032));
          }
          break;
        case PROCESSOR:
          if (!notOnMainCanvas && stageConf.getInputLanes().isEmpty() && !isPipelineFragment) {
            // processor stage must have at least one input lane
            issues.add(
                issueCreator.create(
                    stageConf.getInstanceName(),
                    ValidationError.VALIDATION_0014,
                    "Processor"
                )
            );
            preview = false;
          }
          if(!notOnMainCanvas) {
            if (!stageDef.isVariableOutputStreams()) {
              // processor stage must match the output stream defined in StageDef
              if (stageDef.getOutputStreams() != stageConf.getOutputLanes().size()) {
                issues.add(
                  issueCreator.create(
                    stageConf.getInstanceName(),
                    ValidationError.VALIDATION_0015,
                    stageDef.getOutputStreams(),
                    stageConf.getOutputLanes().size()
                  )
                );
              }
            } else if (stageConf.getOutputLanes().isEmpty()) {
              // processor stage must have at least one output lane
              issues.add(issueCreator.create(stageConf.getInstanceName(), ValidationError.VALIDATION_0032));
            }
          }
          break;
        case EXECUTOR:
        case TARGET:
          // Normal target stage must have at least one input lane
          if (!notOnMainCanvas && stageConf.getInputLanes().isEmpty() && !isPipelineFragment) {
            issues.add(
                issueCreator.create(
                    stageConf.getInstanceName(),
                    ValidationError.VALIDATION_0014,
                    "Target"
                )
            );
            preview = false;
          }
          // Error/Stats/Pipeline lifecycle must not have an input lane
          if (notOnMainCanvas && !stageConf.getInputLanes().isEmpty()) {
            issues.add(
                issueCreator.create(
                    stageConf.getInstanceName(),
                    ValidationError.VALIDATION_0012,
                    "Error/Stats/Lifecycle",
                    stageConf.getInputLanes()
                )
            );
            preview = false;
          }
          if (!stageConf.getOutputLanes().isEmpty()) {
            // target stage cannot have output lanes
            issues.add(
                issueCreator.create(
                    stageConf.getInstanceName(),
                    ValidationError.VALIDATION_0013,
                    stageDef.getType(),
                    stageConf.getOutputLanes()
                )
            );
            preview = false;
          }
          if (notOnMainCanvas && !stageConf.getEventLanes().isEmpty()) {
            issues.add(
                issueCreator.create(
                    stageConf.getInstanceName(),
                    ValidationError.VALIDATION_0036,
                    stageDef.getType(),
                    stageConf.getEventLanes()
                )
            );
            preview = false;
          }
          break;
        default:
          throw new IllegalStateException("Unexpected stage type " + stageDef.getType());
      }

      // Validate proper event configuration
      if(!notOnMainCanvas && stageConf.getEventLanes().size() > 1) {
        issues.add(
          issueCreator.create(
            stageConf.getInstanceName(),
            ValidationError.VALIDATION_0101
          )
        );
        preview = false;
      }
      if(!notOnMainCanvas && !stageDef.isProducingEvents() && stageConf.getEventLanes().size() > 0) {
        issues.add(
          issueCreator.create(
            stageConf.getInstanceName(),
            ValidationError.VALIDATION_0102
          )
        );
        preview = false;
      }

      // Validate stage owns configuration
      preview &= validateComponentConfigs(
        stageConf,
        stageDef.getConfigDefinitions(),
        stageDef.getConfigDefinitionsMap(),
        stageDef.getHideConfigs(),
        stageDef.hasPreconditions(),
        constants,
        issueCreator,
        issues
      );

      // Validate service definitions
      Set<String> expectedServices = stageDef.getServices().stream()
        .map(service -> service.getService().getName())
        .collect(Collectors.toSet());
      Set<String> configuredServices = stageConf.getServices().stream()
        .map(service -> service.getService().getName())
        .collect(Collectors.toSet());
      if(!expectedServices.equals(configuredServices)) {
        issues.add(issueCreator.create(
            stageConf.getInstanceName(),
            ValidationError.VALIDATION_0200,
            StringUtils.join(expectedServices, ","),
            StringUtils.join(configuredServices, ",")
        ));
        preview = false;
      } else {
        // Validate all services
        for (ServiceConfiguration serviceConf: stageConf.getServices()) {
          ServiceDefinition serviceDef = stageLibrary.getServiceDefinition(serviceConf.getService(), false);
          preview &= validateComponentConfigs(
            serviceConf,
            serviceDef.getConfigDefinitions(),
            serviceDef.getConfigDefinitionsMap(),
            Collections.emptySet(),
            false,
            constants,
            issueCreator.forService(serviceConf.getService().getName()),
            issues
          );
        }
      }
    }
    return preview;
  }
Validate given stage configuration.