docs: synchronized void initChainInternal(ChainData inputChainData) throws ChannelException, ChainException {
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
              Tr.entry(tc, "initChainInternal");
          }
          String inputChainName = inputChainData.getName();
          Chain chain = null;
          ChainData newChainData = null;
          ChannelData inputChannelDataArray[] = inputChainData.getChannelList();
          ChannelData newChannelDataArray[] = new ChannelData[inputChannelDataArray.length];
          ChannelDataImpl parent = null;
          ChildChannelDataImpl child = null;
          // Track whether new children are created (or simply reused) in case we need
          // to rollback from an excepction
          boolean childrenNew[] = new boolean[inputChannelDataArray.length];
          // Initialize array to false.
          for (int i = 0; i < childrenNew.length; i++) {
              childrenNew[i] = false;
          }
  
          try {
              if (FlowType.INBOUND.equals(inputChainData.getType())) {
  
                  // Construct an inbound chain - note nothing is inited yet
                  // Use parents to create new channel data array of children (reuse
                  // existing children where possible)
                  newChannelDataArray = generateChildDataArray(inputChannelDataArray, childrenNew);
                  // Create new chain data with child channel data objects.
                  newChainData = new ChainDataImpl((ChainDataImpl) inputChainData, newChannelDataArray);
                  try {
                      chain = new InboundChain(newChainData, this);
                  } catch (ChannelException e) {
                      // The channel exception may specify that FFDC should be suppressed
                      if (!e.suppressFFDC()) {
                          FFDCFilter.processException(e, getClass().getName() + ".initChainInternal", "2206", this, new Object[] { newChainData });
                          Tr.error(tc, "chain.initialization.error", new Object[] { inputChainData.getName(), e.toString() });
                      } else if (TraceComponent.isAnyTracingEnabled() && tc.isEventEnabled()) {
                          Tr.event(tc, "An exception occurred when initializing chain", new Object[] { inputChainData.getName(), e.toString() });
                      }
                      // Clean up the already created child refs in the parent.
                      cleanChildRefsInParent(newChannelDataArray, childrenNew);
                      throw e;
                  } catch (IncoherentChainException e) {
                      // No FFDC Needed as it is called in the constructor above.
                      // Clean up the already created child refs in the parent.
                      cleanChildRefsInParent(newChannelDataArray, childrenNew);
                      // Throw exception to caller.
                      throw e;
                  }
              } else {
  
                  // Construct an outbound chain - note nothing is inited yet
                  // Get children for each outbound parent channel data.
                  // Loop through each channel of this new chain - note, these are parent
                  // names
                  for (int i = 0; i < inputChannelDataArray.length; i++) {
                      // Get the parent channel data object.
                      parent = (ChannelDataImpl) channelDataMap.get(inputChannelDataArray[i].getName());
                      // Get an existing one from the runtime.
                      newChannelDataArray[i] = parent.getOutboundChild();
                      if (null == newChannelDataArray[i]) {
                          // None available in runtime. Create one.
                          newChannelDataArray[i] = parent.createChild();
                          childrenNew[i] = true;
                      } else {
                          childrenNew[i] = false;
                      }
                  }
                  // Create new outbound chain data with child channel data objects.
                  newChainData = new ChainDataImpl(inputChainName, inputChainData.getType(), newChannelDataArray, null);
                  try {
                      chain = new OutboundChain(newChainData, this);
                  } catch (ChannelException e) {
                      // The channel exception may specify that FFDC should be suppressed
                      if (!e.suppressFFDC()) {
                          FFDCFilter.processException(e, getClass().getName() + ".initChainInternal", "2241", this, new Object[] { newChainData });
                          Tr.error(tc, "chain.initialization.error", new Object[] { inputChainData.getName(), e.toString() });
                      } else if (TraceComponent.isAnyTracingEnabled() && tc.isEventEnabled()) {
                          Tr.event(tc, "An exception occurred when initializing chain", new Object[] { inputChainData.getName(), e.toString() });
                      }
                      // Clean up the already created child refs in the parent.
                      cleanChildRefsInParent(newChannelDataArray, childrenNew);
                      throw e;
                  } catch (IncoherentChainException e) {
                      // No FFDC Needed as it is called in the constructor called above.
                      // Clean up the already created child refs in the parent.
                      cleanChildRefsInParent(newChannelDataArray, childrenNew);
                      // Continue to throw exception to caller.
                      throw e;
                  }
              }
  
              // Initialize the chain and its channels
              Channel[] chainChannels = chain.getChannels();
              List<Channel> chainsDone = new ArrayList<Channel>();
              Channel channelX = null; // may need this in the exception clause
              try {
                  for (int i = 0; i < chainChannels.length; ++i) {
                      channelX = chainChannels[i];
                      initChannelInChain(channelX, chain);
                      chainsDone.add(channelX);
                      channelX = null;
                  }
                  // Update the chain state and its event listeners.
                  chain.init();
              } catch (ChannelException e) {
                  if (e instanceof RetryableChannelException) {
                      if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                          Tr.debug(tc, "Caught RetryableChannelException indicating the port is busy");
                      }
                  } else {
                      Tr.error(tc, "chain.initialization.error", new Object[] { chain.getName(), e.toString() });
                      // The channel exception may specify that FFDC should be suppressed
                      if (!e.suppressFFDC()) {
                          FFDCFilter.processException(e, getClass().getName() + ".initChainInternal", "2266", this, new Object[] { chainChannels, chainsDone, chain });
                      }
                  }
  
                  // Handle partially created chain. Undo any inits that have been done
                  // thus far.
                  for (int i = 0; i < newChannelDataArray.length; i++) {
                      if (childrenNew[i]) {
                          child = (ChildChannelDataImpl) newChannelDataArray[i];
                          child.getParent().removeChild(child);
                      }
                  }
  
                  // try to destroy a channel that failed but was not add to the "done" list.
                  if (channelX != null) {
                      if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                          Tr.debug(tc, "calling destory on channel outside of destroyChannelInChain: " + channelX);
                      }
                      channelX.destroy();
                  }
  
                  for (int i = 0; i < chainsDone.size(); i++) {
                      try {
                          destroyChannelInChain(chainsDone.get(i), chain, newChannelDataArray[i]);
                          // Clean up the already created child refs in the parent.
                      } catch (Exception e1) {
                          // No FFDC needed.
                      }
                  }
                  // Throw the exception up to the caller.
                  throw e;
              }
  
              // Add the new chain to the framework.
              this.chainRunningMap.put(inputChainName, chain);
          } catch (IncoherentChainException e) {
              FFDCFilter.processException(e, getClass().getName() + ".initChainInternal", "2290", this, new Object[] { inputChainData, chain });
              if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                  Tr.debug(tc, "Caught exception during chain init: " + e);
              }
              // Can only happen in cases where the factory has multiple dev side
              // interfaces.
              // And the channel being created uses one that isn't compatible in the
              // chain.
              throw e;
          } catch (InvalidChannelNameException e) {
              FFDCFilter.processException(e, getClass().getName() + ".initChainInternal", "2298", this, new Object[] { inputChainData, chain });
              if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                  Tr.debug(tc, "Caught exception during chain init: " + e);
              }
              // This should never happen. If the chain config exists, then the channel
              // configs must exist.
              throw e;
          }
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
              Tr.exit(tc, "initChainInternal");
          }
      }