0
  • java 19
  • vavr (latest)

I would like to create a processing flow of steps (processing XML file) that can split the flow to sub-processing steps. Each step can fail so I wrap it in an Either (vavr) to control the flow.

  1. load XML file (from cloud storage)
  2. parse XML -> List< Obj >
  3. LIST< Obj > -> List< JSON >
  4. LIST< JSON > -> LIST< File >
  5. FILE -> store each one on cloud storage (network)
  • Each processing step can fail.
  • Splitting the flow from one file to many people
  • Using Either looks like the right way to go as I would like to all succeed or fail.

** This is a mockup code

public class Main {

  private Cloud cloud;

  public static void main(String[] args) {
    Main main = new Main();
    main.retrieve("path/filename.xml")
        .flatMap(file -> main.parse(file))
        .flatMap(people -> main.toUsers(people))
        // I would like to break the inner Either to a Seq and process each one of the users
        // to a file and store it separately
        .flatMap(users -> main.store(users))
        // if one file store ops fail I would like to stop and retry the operation.
        .peekLeft(error -> System.out.println(error.formatted()));
  }

  /**
   * Store each user in cloud storage Can fail per file
   *
   * @param users
   * @return io.vavr.control.Either
   */
  private Either<Error, String> store(List<User> users) {
    return Try.of(
            () -> {
              for (User user: users) {
                store(user);
              }
              return "success";
            })
        .toEither()
        .mapLeft(throwable -> new FIleStoreError(throwable.getMessage()));
  }

  /**
   * network file storage use cloud API to store the file
   *
   * @param user
   * @throws RuntimeException
   */
  private void store(User user) throws RuntimeException {
    cloud.store(user);
  }

  private Either<Error, List<User>> toUsers(List<Person> people) {
    return Try.of(() -> people.map(person -> new User(person.name())).toList())
        .toEither()
        .mapLeft(throwable -> new ParseError(throwable.getMessage()));
  }

  /**
   * Read a list of Person from a file
   *
   * @param file
   * @return io.vavr.control.Either
   */
  private Either<Error, List<Person>> parse(File file) {
    return Try.of(() -> poeple(file))
        .toEither()
        .mapLeft(throwable -> new ParseError(throwable.getMessage()));
  }

  /**
   * Load list from {@link Person} objects from file
   *
   * <p>Can fail!
   *
   * @param file input with people data
   * @return list of {@link Person}
   * @throws Exception may fail with file operations
   */
  private List<Person> poeple(File file) throws Exception {
    return List.of(new Person("A"), new Person("B"));
  }

  /**
   * load a file from cloud
   *
   * @param name
   * @return io.vavr.control.Either
   */
  public Either<Error, File> retrieve(String name) {
    return Try.of(() -> loadFile(name))
        .toEither()
        .mapLeft(throwable -> new FileLoadingError(throwable.getMessage()));
  }

  /**
   * Load file from network
   *
   * @param name file name to load
   * @return the file
   * @throws Exception can fail
   */
  private File loadFile(String name) throws Exception {
    return new File(name);
  }
}

How to convert an Either<Error, List< User >> to a Seq of Either<Error, User> so it can be processed one by one?

Gadi
  • 1,539
  • 22
  • 37

1 Answers1

0

Hope this helps:

class Main {
    public static void main(String[] args) {
        List<User> ul = Arrays.asList(new User("a"), new User("b"));
        Error e = new Error("error msg");
        Either<Error, List<User>> a = new Either<>(e, ul);
        List<Either<Error, User>> collect = a.u.stream().map(u -> new Either<>(a.t, u)).collect(Collectors.toList());
    }
}

class Either<T, U> {

    T t;
    U u;

    public Either(T t, U u) {
        this.t = t;
        this.u = u;
    }
}

Hi computer
  • 946
  • 4
  • 8
  • 19