DropwizardのManaged Objects

Environmentsを確認。
Health Checkは最初にやったのでManaged Objectsを試す。
動作確認用に作ったのでResource、Health Checkは適当。

Dropwizardのバージョンは0.6.2。

# Managedインターフェース

Managedインターフェースを実装する。
ここではファイル監視をNIOで書いた。

```java:NioFileWatcher.java
package example.core.managed;

import com.yammer.dropwizard.lifecycle.Managed;

import java.io.IOException;
import java.nio.file.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NioFileWatcher implements Managed, Runnable {
    private final Path path;
    private final WatchService watcher;

    public NioFileWatcher(String path) throws IOException {
        FileSystem fileSystem = FileSystems.getDefault();
        this.path = fileSystem.getPath(path);
        this.watcher = fileSystem.newWatchService();
        this.path.register(watcher, StandardWatchEventKinds.ENTRY_CREATE);
    }

    public void watch() {
        while(true) {
            WatchKey watchKey = null;
            try {
                watchKey = watcher.take();
            } catch(ClosedWatchServiceException | InterruptedException e) {
                break;
            }
            for (WatchEvent<?> event : watchKey.pollEvents()) {
                AddedFiles.add(String.valueOf(event.context()));
            }
            watchKey.reset();
        }
    }

    @Override
    public void start() {
        ExecutorService exec = Executors.newSingleThreadExecutor();
        exec.execute(new Runnable() {
            @Override
            public void run() {
                watch();
            }
        });
    }

    @Override
    public void stop() {
        try {
            watcher.close();
        } catch (IOException e) {
            //
        }
    }

    @Override
    public void run() {
        watch();
    }
}
```

追加されたファイルを保持しておくクラス。

```java:AddedFiles.java
package example.core.managed;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class AddedFiles {
    private static List<String> addedFiles = new CopyOnWriteArrayList<>();

    public static void add(String file) {
        if (addedFiles.contains(file)) return;
        addedFiles.add(file);
    }

    public static List<String> getAll() {
        return addedFiles;
    }
}
```

# Environment#managedExecutorService

NioFileWatcher.javaにRunnableインターフェースを実装しておく。

# Environment#managedScheduledExecutorService

こちらは指定パス配下のファイルを列挙して追加してくようにした。

```java:IoFileWatcher.java
package example.core.managed;

import java.io.File;
import java.util.Arrays;
import java.util.List;

public class IoFileWatcher implements Runnable {
    private final File path;
    private final List<String> files;

    public IoFileWatcher(String path) {
        this.path = new File(path);
        this.files = Arrays.asList(this.path.list());
    }

    @Override
    public void run() {
        for (String name : path.list()) {
            if (files.contains(name)) continue;
            AddedFiles.add(name);
        }
    }
}
```

# Environmentに追加

Managedインターフェースの場合はそのまま追加。
Environment#managedExecutorServiceはスレッド数とか指定したあとにExecutor#executeで実行。
Environment#managedScheduledExecutorServiceはScheduledExecutorServiceが返却されるのでそのままスケジュールを指定して実行する。

それぞれEnvironmentで処理の開始、終了が管理されるのでリソースの解放とかをManaged#stopなんかに書いておけばResource側でいろいろ考えなくて良さそう。・・・今回書いた例ではResourceは全く考えずに書いてしまったけども。

```java:FileWatchService.java
package example.core.managed;

import com.yammer.dropwizard.Service;
import com.yammer.dropwizard.config.Bootstrap;
import com.yammer.dropwizard.config.Environment;

import java.util.concurrent.TimeUnit;

public class FileWatchService extends Service<FileWatchConfiguration> {
    @Override
    public void initialize(Bootstrap<FileWatchConfiguration> bootstrap) {
        bootstrap.setName("file watch");
    }

    @Override
    public void run(FileWatchConfiguration configuration, Environment environment) throws Exception {
        final String path = configuration.getPath();
        final NioFileWatcher nioFileWatcher = new NioFileWatcher(path);
        final IoFileWatcher ioFileWatcher = new IoFileWatcher(path);

        environment.addResource(new FileWatchResource());
        environment.addHealthCheck(new FilePathHealthCheck(path));

        if (configuration.isNioFileWatch1()) {
            // Managed
            environment.manage(nioFileWatcher);
        }
        if (configuration.isNioFileWatch2()) {
            // Environment#managedExecutorService
            environment.managedExecutorService("nio file watch", 1, 1, 1, TimeUnit.MILLISECONDS)
                    .execute(nioFileWatcher);
        }
        if (configuration.isIoFileWatch()) {
            // Environment#managedScheduledExecutorService
            environment.managedScheduledExecutorService("io file watch", 1)
                    .scheduleWithFixedDelay(ioFileWatcher, 10, 10, TimeUnit.SECONDS);
        }
    }

    public static void main(String[] args) throws Exception {
        new FileWatchService().run(args);
    }
}
```

その他今回書いたもの

```yaml:file-watch.yml
path: /temp
nioFileWatch1: false
nioFileWatch2: false
ioFileWatch: true
```

```java:FileWatchConfiguration.java
package example.core.managed;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.yammer.dropwizard.config.Configuration;
import org.hibernate.validator.constraints.NotEmpty;

public class FileWatchConfiguration extends Configuration {

    @NotEmpty
    @JsonProperty
    private String path;

    @JsonProperty
    private boolean nioFileWatch1 = false;

    @JsonProperty
    private boolean nioFileWatch2 = false;

    @JsonProperty
    private boolean ioFileWatch = false;

    public String getPath() {
        return path;
    }

    public boolean isNioFileWatch1() {
        return nioFileWatch1;
    }

    public boolean isNioFileWatch2() {
        return nioFileWatch2;
    }

    public boolean isIoFileWatch() {
        return ioFileWatch;
    }
}
```

```java:FileWatchResource.java
package example.core.managed;

import com.yammer.metrics.annotation.Timed;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.List;

@Path("/file-watch")
@Produces(MediaType.APPLICATION_JSON)
public class FileWatchResource {

    @GET
    @Timed
    public List<String> getAll() {
        return AddedFiles.getAll();
    }
}
```

```java:FilePathHealthCheck.java
package example.core.managed;

import com.yammer.metrics.core.HealthCheck;

import java.io.File;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;

public class FilePathHealthCheck extends HealthCheck {

    private final String path;

    public FilePathHealthCheck(String path) {
        super("path");
        this.path = path;
    }

    @Override
    protected Result check() throws Exception {
        FileSystem fileSystem = FileSystems.getDefault();
        final File file = fileSystem.getPath(path).toFile();
        if (!file.exists()) {
            return Result.unhealthy("file not found");
        }
        return Result.healthy();
    }
}
```