Repository: eclipse-vertx/vert.x

Problem statement:
Allow AsyncFile's to be opened in append mode
Appending to a file requires three steps:
1. Open the file with `FileSystem.open()` with an `OpenOptions` object on which `setTruncateExisting(false)` and `setWrite(true)` have been called.
2. Get the file properties with `FileSystem.props()`
3. Call `setWritePos()` on the file with the length according to the properties.

It seems like a simpler and less error-prone way would be to add `setAppend()` and `getAppend()` to `OpenOptions` and make `AsyncFileImpl`'s constructor translate it to `StandardOpenOption.APPEND`.



Hints:

Patch:
diff --git a/src/main/generated/io/vertx/core/file/OpenOptionsConverter.java b/src/main/generated/io/vertx/core/file/OpenOptionsConverter.java
--- a/src/main/generated/io/vertx/core/file/OpenOptionsConverter.java
+++ b/src/main/generated/io/vertx/core/file/OpenOptionsConverter.java
@@ -27,6 +27,9 @@
 public class OpenOptionsConverter {
 
   public static void fromJson(JsonObject json, OpenOptions obj) {
+    if (json.getValue("append") instanceof Boolean) {
+      obj.setAppend((Boolean)json.getValue("append"));
+    }
     if (json.getValue("create") instanceof Boolean) {
       obj.setCreate((Boolean)json.getValue("create"));
     }
@@ -60,6 +63,7 @@ public static void fromJson(JsonObject json, OpenOptions obj) {
   }
 
   public static void toJson(OpenOptions obj, JsonObject json) {
+    json.put("append", obj.isAppend());
     json.put("create", obj.isCreate());
     json.put("createNew", obj.isCreateNew());
     json.put("deleteOnClose", obj.isDeleteOnClose());
diff --git a/src/main/java/io/vertx/core/file/OpenOptions.java b/src/main/java/io/vertx/core/file/OpenOptions.java
--- a/src/main/java/io/vertx/core/file/OpenOptions.java
+++ b/src/main/java/io/vertx/core/file/OpenOptions.java
@@ -21,7 +21,7 @@
 
 /**
  * Describes how an {@link io.vertx.core.file.AsyncFile} should be opened.
- * 
+ *
  * @author <a href="http://tfox.org">Tim Fox</a>
  */
 @DataObject(generateConverter = true)
@@ -38,6 +38,11 @@ public class OpenOptions {
   public static final boolean DEFAULT_TRUNCATEEXISTING = false;
   public static final boolean DEFAULT_SPARSE = false;
 
+  /**
+   * Whether the file should be opened in append mode by default = false.
+   */
+  public static final boolean DEFAULT_APPEND = false;
+
   private String perms = DEFAULT_PERMS;
   private boolean read = DEFAULT_READ;
   private boolean write = DEFAULT_WRITE;
@@ -48,6 +53,7 @@ public class OpenOptions {
   private boolean deleteOnClose = DEFAULT_DELETEONCLOSE;
   private boolean truncateExisting = DEFAULT_TRUNCATEEXISTING;
   private boolean sparse = DEFAULT_SPARSE;
+  private boolean append = DEFAULT_APPEND;
 
   /**
    * Default constructor
@@ -72,6 +78,7 @@ public OpenOptions(OpenOptions other) {
     this.deleteOnClose = other.deleteOnClose;
     this.truncateExisting = other.truncateExisting;
     this.sparse = other.sparse;
+    this.append = other.append;
   }
 
   /**
@@ -280,4 +287,22 @@ public OpenOptions setDsync(boolean dsync) {
     this.dsync = dsync;
     return this;
   }
+
+  /**
+   * @return true if the file should be opened in append mode, false otherwise
+   */
+  public boolean isAppend() {
+    return append;
+  }
+
+  /**
+   * Whether the file should be opened in append mode. Defaults to {@code false}.
+   *
+   * @param append true to open file in append mode, false otherwise
+   * @return a reference to this, so the API can be used fluently
+   */
+  public OpenOptions setAppend(boolean append) {
+    this.append = append;
+    return this;
+  }
 }
diff --git a/src/main/java/io/vertx/core/file/impl/AsyncFileImpl.java b/src/main/java/io/vertx/core/file/impl/AsyncFileImpl.java
--- a/src/main/java/io/vertx/core/file/impl/AsyncFileImpl.java
+++ b/src/main/java/io/vertx/core/file/impl/AsyncFileImpl.java
@@ -101,6 +101,7 @@ public class AsyncFileImpl implements AsyncFile {
       } else {
         ch = AsynchronousFileChannel.open(file, opts, vertx.getWorkerPool());
       }
+      if (options.isAppend()) writePos = ch.size();
     } catch (IOException e) {
       throw new FileSystemException(e);
     }


Test patch:
diff --git a/src/test/java/io/vertx/test/core/FileSystemTest.java b/src/test/java/io/vertx/test/core/FileSystemTest.java
--- a/src/test/java/io/vertx/test/core/FileSystemTest.java
+++ b/src/test/java/io/vertx/test/core/FileSystemTest.java
@@ -1162,11 +1162,9 @@ public void testWriteStreamAppend() throws Exception {
     createFile(fileName, existing);
     byte[] content = TestUtils.randomByteArray(chunkSize * chunks);
     Buffer buff = Buffer.buffer(content);
-    vertx.fileSystem().open(testDir + pathSep + fileName, new OpenOptions(), ar -> {
+    vertx.fileSystem().open(testDir + pathSep + fileName, new OpenOptions().setAppend(true), ar -> {
       if (ar.succeeded()) {
         AsyncFile ws = ar.result();
-        long size = vertx.fileSystem().propsBlocking(testDir + pathSep + fileName).size();
-        ws.setWritePos(size);
         ws.exceptionHandler(t -> fail(t.getMessage()));
         for (int i = 0; i < chunks; i++) {
           Buffer chunk = buff.getBuffer(i * chunkSize, (i + 1) * chunkSize);