All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.swoval.files.apple.FileEventsApi Maven / Gradle / Ivy

The newest version!
package com.swoval.files.apple;

import com.swoval.concurrent.ThreadFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

public class FileEventsApi implements AutoCloseable {

  public interface Consumer {
    void accept(T t);
  }

  private long handle;
  private final ExecutorService executor = Executors.newSingleThreadExecutor(
      new ThreadFactory("com.swoval.files.apple.FileEventsApi.run-loop-thread"));

  private FileEventsApi(final Consumer c, final Consumer pc)
      throws InterruptedException {
    final CountDownLatch latch = new CountDownLatch(1);
    executor.submit(new Runnable() {
      @Override
      public void run() {
        FileEventsApi.this.handle = FileEventsApi.init(c, pc);
        latch.countDown();
        loop();
      }
    });
    latch.await();
  }

  private AtomicBoolean closed = new AtomicBoolean(false);

  @Override
  public void close() {
    if (closed.compareAndSet(false, true)) {
      stopLoop();
      executor.shutdownNow();
      try {
        executor.awaitTermination(5, TimeUnit.SECONDS);
        close(handle);
      } catch (InterruptedException e) {
      }
    }
  }

  public void loop() {
    loop(handle);
  }

  public int createStream(String path, double latency, int flags) {
    if (closed.get()) {
      String err = "Tried to create watch stream for path " + path + " on closed watch service";
      throw new IllegalStateException(err);
    }
    return createStream(path, latency, flags, handle);
  }

  public void stopLoop() {
    stopLoop(handle);
  }

  public void stopStream(int streamHandle) {
    if (!closed.get()) {
      stopStream(handle, streamHandle);
    }
  }

  private static final String NATIVE_LIBRARY = "apple-file-events0";

  private static final void exit(String msg) {
    System.err.println(msg);
    System.exit(1);
  }

  private static void loadPackaged() {
    try {
      String lib = System.mapLibraryName(NATIVE_LIBRARY);
      File temp = File.createTempFile("jni-", "");
      if (!temp.delete()) {
        throw new IOException("Couldn't remove temp file");
      }
      if (!temp.mkdir()) {
        throw new IOException("Couldn't remove temp file");
      }
      String resourcePath = "/native/x86_64-darwin/" + lib;
      InputStream resourceStream = FileEventsApi.class.getResourceAsStream(resourcePath);
      if (resourceStream == null) {
        String msg = "Native library " + lib + " (" + resourcePath + ") can't be loaded.";
        throw new UnsatisfiedLinkError(msg);
      }

      final File extractedPath = new File(temp.getAbsolutePath() + "/" + lib);
      OutputStream out = new FileOutputStream(extractedPath);
      try {
        byte[] buf = new byte[1024];
        int len = 0;
        while ((len = resourceStream.read(buf)) >= 0) {
          out.write(buf, 0, len);
        }
      } catch (Exception e) {
        throw new UnsatisfiedLinkError("Error while extracting native library: " + e);
      } finally {
        resourceStream.close();
        out.close();
      }

      Runtime.getRuntime().addShutdownHook(new Thread() {
        public void run() {
          try {
            Files.delete(extractedPath.toPath());
          } catch (IOException e) {
            System.err.println("Error deleting temporary files: " + e);
          }
        }
      });

      System.load(extractedPath.getAbsolutePath());
    } catch (Exception e) {
      exit("Couldn't load packaged library " + NATIVE_LIBRARY);
    }
  }

  static {
    loadPackaged();
  }

  public static FileEventsApi apply(Consumer consumer, Consumer pathConsumer)
      throws InterruptedException {
    return new FileEventsApi(consumer, pathConsumer);
  }

  public static native void loop(long handle);

  public static native void close(long handle);

  public static native long init(Consumer consumer, Consumer pathConsumer);

  public static native int createStream(String path, double latency, int flags, long handle);

  public static native void stopLoop(long handle);

  public static native void stopStream(long handle, int streamHandle);

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy