private void startChildWorkflow(
      final StartChildWorkflowExecutionParameters parameters,
      final Settable<StartChildWorkflowReply> reply,
      final Settable<String> result) {
    String workflowId = parameters.getWorkflowId();
    WorkflowType workflowType = parameters.getWorkflowType();
    WorkflowExecution childExecution = new WorkflowExecution();
    final String runId = UUID.randomUUID().toString();
    // TODO: Validate parameters against registration options to find missing timeouts or other
    // options
    try {
      DecisionContext parentDecisionContext = decisionContextProvider.getDecisionContext();
      if (workflowId == null) {
        workflowId =
            decisionContextProvider.getDecisionContext().getWorkflowClient().generateUniqueId();
      }
      childExecution.setWorkflowId(workflowId);
      childExecution.setRunId(runId);

      final GenericActivityClient activityClient = parentDecisionContext.getActivityClient();
      final WorkflowClock workflowClock = parentDecisionContext.getWorkflowClock();
      WorkflowDefinitionFactory factory;
      if (factoryFactory == null) {
        throw new IllegalStateException("required property factoryFactory is null");
      }
      factory = factoryFactory.getWorkflowDefinitionFactory(workflowType);
      if (factory == null) {
        String cause =
            StartChildWorkflowExecutionFailedCause.WORKFLOW_TYPE_DOES_NOT_EXIST.toString();
        throw new StartChildWorkflowFailedException(0, childExecution, workflowType, cause);
      }
      TestWorkflowContext workflowContext = new TestWorkflowContext();
      workflowContext.setWorkflowExecution(childExecution);
      workflowContext.setWorkflowType(parameters.getWorkflowType());
      workflowContext.setParentWorkflowExecution(
          parentDecisionContext.getWorkflowContext().getWorkflowExecution());
      workflowContext.setTagList(parameters.getTagList());
      workflowContext.setTaskList(parameters.getTaskList());
      DecisionContext context =
          new TestDecisionContext(
              activityClient, TestGenericWorkflowClient.this, workflowClock, workflowContext);
      // this, parameters, childExecution, workflowClock, activityClient);
      final WorkflowDefinition childWorkflowDefinition = factory.getWorkflowDefinition(context);
      final ChildWorkflowTryCatchFinally tryCatch =
          new ChildWorkflowTryCatchFinally(
              parameters, childExecution, childWorkflowDefinition, context, result);
      workflowContext.setRootTryCatch(tryCatch);
      ChildWorkflowTryCatchFinally currentRun = workflowExecutions.get(workflowId);
      if (currentRun != null) {
        String cause = StartChildWorkflowExecutionFailedCause.WORKFLOW_ALREADY_RUNNING.toString();
        throw new StartChildWorkflowFailedException(0, childExecution, workflowType, cause);
      }
      workflowExecutions.put(workflowId, tryCatch);
      continueAsNewWorkflowExecution(tryCatch, result);
    } catch (StartChildWorkflowFailedException e) {
      throw e;
    } catch (Throwable e) {
      // This cause is chosen to represent internal error for sub-workflow creation
      String cause = StartChildWorkflowExecutionFailedCause.OPEN_CHILDREN_LIMIT_EXCEEDED.toString();
      StartChildWorkflowFailedException failure =
          new StartChildWorkflowFailedException(0, childExecution, workflowType, cause);
      failure.initCause(e);
      throw failure;
    } finally {
      reply.set(new StartChildWorkflowReplyImpl(result, runId));
    }
  }
  private void addWorkflowType(
      Class<?> interfaze,
      Class<?> workflowImplementationType,
      DataConverter converterOverride,
      Object[] constructorArgs)
      throws InstantiationException, IllegalAccessException {
    Workflow workflowAnnotation = interfaze.getAnnotation(Workflow.class);
    String interfaceName = interfaze.getSimpleName();
    MethodConverterPair workflowImplementationMethod = null;
    MethodConverterPair getStateMethod = null;
    WorkflowType workflowType = null;
    WorkflowTypeRegistrationOptions registrationOptions = null;
    Map<String, MethodConverterPair> signals = new HashMap<String, MethodConverterPair>();
    for (Method method : interfaze.getMethods()) {
      if (method.getDeclaringClass().getAnnotation(Workflow.class) == null) {
        continue;
      }
      Execute executeAnnotation = method.getAnnotation(Execute.class);
      Signal signalAnnotation = method.getAnnotation(Signal.class);
      GetState getStateAnnotation = method.getAnnotation(GetState.class);
      checkAnnotationUniqueness(method, executeAnnotation, signalAnnotation, getStateAnnotation);
      if (executeAnnotation != null) {
        if (workflowImplementationMethod != null) {
          throw new IllegalArgumentException(
              "Interface annotated with @Workflow is allowed to have only one method annotated with @Execute. Found "
                  + getMethodFullName(workflowImplementationMethod.getMethod())
                  + " and "
                  + getMethodFullName(method));
        }
        if (!method.getReturnType().equals(void.class)
            && !(Promise.class.isAssignableFrom(method.getReturnType()))) {
          throw new IllegalArgumentException(
              "Workflow implementation method annotated with @Execute can return only Promise or void: "
                  + getMethodFullName(method));
        }
        if (!method.getDeclaringClass().equals(interfaze)) {
          throw new IllegalArgumentException(
              "Interface "
                  + interfaze.getName()
                  + " cannot inherit workflow implementation method annotated with @Execute: "
                  + getMethodFullName(method));
        }
        DataConverter converter =
            createConverter(workflowAnnotation.dataConverter(), converterOverride);
        workflowImplementationMethod = new MethodConverterPair(method, converter);
        workflowType = getWorkflowType(interfaceName, method, executeAnnotation);

        WorkflowRegistrationOptions registrationOptionsAnnotation =
            interfaze.getAnnotation(WorkflowRegistrationOptions.class);
        SkipTypeRegistration skipRegistrationAnnotation =
            interfaze.getAnnotation(SkipTypeRegistration.class);
        if (skipRegistrationAnnotation == null) {
          if (registrationOptionsAnnotation == null) {
            throw new IllegalArgumentException(
                "@WorkflowRegistrationOptions is required for the interface that contains method annotated with @Execute");
          }
          registrationOptions = createRegistrationOptions(registrationOptionsAnnotation);
        } else {
          if (registrationOptionsAnnotation != null) {
            throw new IllegalArgumentException(
                "@WorkflowRegistrationOptions is not allowed for the interface annotated with @SkipTypeRegistration.");
          }
        }
      }
      if (signalAnnotation != null) {
        String signalName = signalAnnotation.name();
        if (signalName == null || signalName.isEmpty()) {
          signalName = method.getName();
        }
        DataConverter signalConverter =
            createConverter(workflowAnnotation.dataConverter(), converterOverride);
        signals.put(signalName, new MethodConverterPair(method, signalConverter));
      }
      if (getStateAnnotation != null) {
        if (getStateMethod != null) {
          throw new IllegalArgumentException(
              "Interface annotated with @Workflow is allowed to have only one method annotated with @GetState. Found "
                  + getMethodFullName(getStateMethod.getMethod())
                  + " and "
                  + getMethodFullName(method));
        }
        if (method.getReturnType().equals(void.class)
            || (Promise.class.isAssignableFrom(method.getReturnType()))) {
          throw new IllegalArgumentException(
              "Workflow method annotated with @GetState cannot have void or Promise return type: "
                  + getMethodFullName(method));
        }
        DataConverter converter =
            createConverter(workflowAnnotation.dataConverter(), converterOverride);
        getStateMethod = new MethodConverterPair(method, converter);
      }
    }
    if (workflowImplementationMethod == null) {
      throw new IllegalArgumentException(
          "Workflow definition does not implement any method annotated with @Execute. "
              + workflowImplementationType);
    }
    POJOWorkflowImplementationFactory implementationFactory =
        getImplementationFactory(workflowImplementationType, interfaze, workflowType);
    WorkflowDefinitionFactory factory =
        new POJOWorkflowDefinitionFactory(
            implementationFactory,
            workflowType,
            registrationOptions,
            workflowImplementationMethod,
            signals,
            getStateMethod,
            constructorArgs);
    factories.put(workflowType, factory);
    workflowImplementationTypes.add(workflowImplementationType);
    if (factory.getWorkflowRegistrationOptions() != null) {
      workflowTypesToRegister.add(workflowType);
    }
  }