/**
   * Talks to the topology module - this top level function just sets the classloader up and creates
   * the module, then calls talkToStream to do the talking
   *
   * @param bucket
   * @param libs
   * @param harvest_tech_only
   * @param m
   * @param source
   * @return
   */
  protected static Validation<BasicMessageBean, IEnrichmentStreamingTopology> getStreamingTopology(
      final DataBucketBean bucket,
      final BucketActionMessage m,
      final String source,
      final Validation<BasicMessageBean, Map<String, Tuple2<SharedLibraryBean, String>>>
          err_or_libs // "pipeline element"
      ) {
    try {

      return err_or_libs.<Validation<BasicMessageBean, IEnrichmentStreamingTopology>>validation(
          // Error:
          error -> Validation.fail(error),
          // Normal
          libs -> {
            // Easy case, if streaming is turned off, just pass data through this layer
            if (!Optional.ofNullable(bucket.streaming_enrichment_topology().enabled()).orElse(true))
              return Validation.success(new PassthroughTopology());
            // Easy case, if libs is empty then use the default streaming topology
            if (libs.isEmpty()) {
              return Validation.success(new PassthroughTopology());
            }

            final Tuple2<SharedLibraryBean, String> libbean_path =
                libs.values()
                    .stream()
                    .filter(
                        t2 ->
                            (null != t2._1())
                                && (null
                                    != Optional.ofNullable(
                                            t2._1().streaming_enrichment_entry_point())
                                        .orElse(t2._1().misc_entry_point())))
                    .findFirst()
                    .orElse(null);

            if ((null == libbean_path)
                || (null
                    == libbean_path._2())) { // Nice easy error case, probably can't ever happen
              return Validation.fail(
                  SharedErrorUtils.buildErrorMessage(
                      source,
                      m,
                      SharedErrorUtils.SHARED_LIBRARY_NAME_NOT_FOUND,
                      bucket.full_name(),
                      "(unknown)"));
            }

            final Validation<BasicMessageBean, IEnrichmentStreamingTopology> ret_val =
                ClassloaderUtils.getFromCustomClasspath(
                    IEnrichmentStreamingTopology.class,
                    Optional.ofNullable(libbean_path._1().streaming_enrichment_entry_point())
                        .orElse(libbean_path._1().misc_entry_point()),
                    Optional.of(libbean_path._2()),
                    libs.values().stream().map(lp -> lp._2()).collect(Collectors.toList()),
                    source,
                    m);

            return ret_val;
          });
    } catch (Throwable t) {
      return Validation.fail(
          SharedErrorUtils.buildErrorMessage(
              source,
              m,
              ErrorUtils.getLongForm(
                  SharedErrorUtils.ERROR_LOADING_CLASS,
                  t,
                  bucket.harvest_technology_name_or_id())));
    }
  }