JavaFXのListCellで非同期処理して失敗した話

Javaビーム工房のタイムラインにListView使っているんだけど、画像の表示やリプライ先の表示とかで非同期処理して表示するセルがずれる問題があった。(というか今も現在進行形で発生してる
その原因がListCellの中で非同期処理してるのがまずい。
どんなことが起きているかというと、リプライ先表示で説明するとこうなる。
リプライ先取得開始(非同期処理開始)→新しいツイートが来て表示するべきオブジェクトが変わる→新しいツイートのリプライ先はキャッシュされていたので先に終了→遅れて最初のリプライ取得終了→上書きされてずれる

解決方法としては、現在ListViewのItemsに入れている型がStringとかのフィールドを持つクラスなんだけど、これとは別にPropertyのフィールドだけを持つクラスを作成して、それをItemsに入れるのが良いと思う。これならプロパティが変更された時にListCellで変更を検知して表示内容を変えれる。きっとこれで解決のはず!

簡単なサンプルとしてプログレスバーを表示するListViewのサンプルを作った。
間違いや他の解決方法アレば教えて下さい。

Minecraft1.8サーバープラグイン開発 データベース編

イベント編の続きです。
今回はデータベースを使ってプレイヤーのログインのログをとってみます。
データベースはMySQLを使用しています。

データベースといえばJPAを使用したかったんですが、試行錯誤してもダメでした。
理由としてはMETA-INF/services/の中にあるファイルを読み込めないのが原因のようでした。
というわけでBukkitで用意されているEbeanを使用していきます。

まずMySQLを使用するために依存関係にcompile ‘mysql:mysql-connector-java:5.1.34’を追加します。
次にplugin.ymlにdatabase: trueを追加。これを行うことでデータベースを使用できるようになります。

Ebeanで使用するためのエンティティを作成します。
JPAと同じでjavax.persistence.Entityアノテーションをクラスにつける感じで。
また、デフォルトコンストラクタとゲッタ/セッタが必要です。

@Entity
@Table(name = "PLAYER_JOIN_LOG")
public class PlayerJoinLog {
    @Id
    @GeneratedValue
    private Long id;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(nullable = false)
    private Date joinDate;

    @Column(nullable = false)
    private String playerName;

    @Column(nullable = false)
    private String playerUUID;

    @Column(nullable = false)
    private String joinIP;

    //コンストラクタとゲッタセッタは省略
}

PluginクラスでgetDatabaseClassesメソッドをオーバーライドしてデータベースで使用するエンティティのClassクラスのリストを返します。

@Override
public List<Class<?>> getDatabaseClasses() {
    return Arrays.asList(PlayerJoinLog.class);
}

onEnableされた時にテーブルの存在を確認し、テーブルを作成します。

private static EbeanServer database;
@Override
public void onEnable() {
    setupDatabase();
}

private void setupDatabase() {
    try {
        getDatabase().beginTransaction();
        getDatabase().find(PlayerJoinLog.class).findRowCount();
    } catch (PersistenceException ex) {
        installDDL();
    } finally {
        getDatabase().commitTransaction();
    }
}

public static EbeanServer getPluginDataBase() {
    return getPlugin().getDatabase();
}
public static Plugin getPlugin() {
    return getPlugin(ServerPlugin.class);
}

ListenerでPlayerがログインした時DBに情報を保存しましょう。

@EventHandler
public void onLogin(PlayerJoinEvent event) {
    Player player = event.getPlayer();
    UUID uuid = player.getUniqueId();
    PlayerJoinLog log = new PlayerJoinLog(player.getPlayerListName(), uuid.toString(),
            player.getAddress().getHostName());

    EbeanServer dataBase = MyPlugin.getPluginDatabase();
    try {
        dataBase.beginTransaction();
        dataBase.save(log);
    } finally {
        dataBase.commitTransaction();
    }
}

後はビルドして動作を確認して終了です。
このプラグインを使うためにはBukkit側で設定が必要になります。

実際動かすjarと同じディレクトリに生成されているbukkit.ymlに以下を記述します。

database:
  username: DBのユーザー名
  isolation: SERIALIZABLE
  driver: com.mysql.jdbc.Driver
  password: DBのパスワード
  url: DBのURL

これで設定は以上です。


おまけ

IntelliJでDBの設定(Ultimateの機能だけど)

追加からMySQLを選択
DatabaseTool1

ホストとポート、データベース名とユーザー名パスワードを入力
TestConnectionで接続確認をとれたらOK
DatabaseTool2

データはこのような形で見れるようになります。
データをダブルクリックで編集できます。
DatabaseTool3

SQL文はデータベースかテーブルを右クリックしてConsoleを選ぶと入力用のタブが開くのでそこで入力して実行。
補完めっちゃきいて良い感じです。
実行結果はこんなかんじで表示。
いいですね。IntelliJ
DatabaseTool4

Minecraft1.8サーバープラグイン開発 イベント編

HelloWorld編の続きです。
今回はイベントを使用してログインしてきたプレイヤーにかぼちゃをかぶせるプラグインを作成してみましょう。

Bukkitではサーバーで発生したいろいろなイベントを受け取る仕組みが用意されています。
これを使用することで一定範囲のブロックを破壊できないようにしたり、ログインしてきたプレイヤーにアイテムをプレゼントしたりなどの処理を行えます。

イベントを受け取るリスナはorg.bukkit.event.Listenerインターフェイスを実装します。
Listenerインターフェイスはマーカーインターフェイスになっていて実装しなければいけないメソッドはありません。
イベントを受け取るメソッドはorg.bukkit.event.Eventを継承しているクラス1つを引数に取り、@EventHandlerアノテーションがなければいけません。
引数のクラスによってメソッドは適切なタイミングで呼び出されるようになります。

プレイヤーがログインした時にかぼちゃをかぶせる処理を書いてみます。

public class MyListener implements Listener {

    @EventHandler
    public void onPlayerJoin(PlayerJoinEvent event) {
        Player player = event.getPlayer();
        player.getInventory().setHelmet(new ItemStack(Material.PUMPKIN));
    }
}

非常に簡単ですね。
最後にこのMyListenerをBukkitに登録する必要があります。
registerEventsには第一引数にListener、第二引数にPluginを与えます。

public class HelloWorld extends JavaPlugin {
    @Override
    public void onEnable() {
        getServer().getPluginManager().registerEvents(new MyListener(), this);
    }
}

イベントは独自で定義することも可能です。
複雑なプラグインを作ったりする場合は使うことになると思います。
独自のイベントを作成する場合はEventを継承し、staticなHandlerListを宣言します。
親に定義されているgetHandlersメソッドをオーバーライドしてHandlerListを返すようにします。
追加でHadlerListを返すpublic static HandlerList getHandlerList()を作成します。
これを用意しないとIllegalPluginAccessExceptionが発生します。

イベントを通知する場合はBukkit.getServer().getPluginManager().callEvent(customEvent);のようにcallEventメソッドを使用します。
リスナは@EventHandlerアノテーションのつけられたメソッドで引数にCustomEventを定義するだけです。

これでイベント編終了です。

Minecraft1.8サーバープラグイン開発 HelloWorld編

前回の続きです。
とりあえずHelloWorldでもやってみる。

まずはsrc/main/resources/にplugin.ymlを作成
これはサーバーがプラグインを読み込んだ時に必要な情報をこのファイルからロードします。
必須の項目はname, version, mainの3つです。
nameにはプラグインの名前を文字列で記述します。
versionにはプラグインのバージョンを文字列で記述します。
mainにはJavaPluginクラスを継承したエントリポイントとなるクラスを完全修飾名で記述します。

その他の設定についてはWikiを参考にすると良いと思います。

エントリポイントとなるクラスはJavaPluginクラスを継承します。
プラグインが有効化された時onEnableメソッドが呼び出されるので、プラグインの初期化はonEnableで行います。
コマンドなどでプラグインが無効化された時はonDisableメソッドが呼び出されるので、イベントリスナやタイマー処理はここで終了するようにしておきましょう。

今回は有効化された時にHelloWorldするだけにしておきましょう。

public class HelloWorld extends JavaPlugin {
    @Override
    public void onEnable() {
        System.out.println("HelloWorld!");
    }
}

JPAのエンティティクラスでなぜプリミティブではなくてラッパーを使うの?

by orekyuu 0 Comments

最近学校の作品制作をするためにJavaEEを勉強し始めた。
JPAいじってて疑問に思ったんだけど、値にプリミティブとラッパーどちらでも使えるようになっていて、どちらを使えばいいのか分からなかった。サンプルとかいろいろ見てるとラッパーばかりだったので僕もそれに合わせてたけど、計算するときとかプリミティブに直すしBoxingするだけ無駄じゃないかと思って理由を調べたら結構見つかった。

Should I use Primitives or wrappers in JPA2.0?

理由としてはラッパーの場合は設定されていない値としてnullを扱える。プリミティブなら0やfalseなどの値になる。
EntityManagerの管理下出ない場合はnullになる。

ということらしいです。確かに値が0であれば値が空なのか0が保存されているか分からないですね。
他に使う理由があったり、プリミティブ使ったほうが良いという理由とかあれば教えて下さい。

Minecraft1.8サーバープラグイン開発 開発環境構築編

Bukkitの開発が止まってしまって1.8のプラグイン開発できないなーと思ってたんですけど、Spigotが1.8対応しているみたいだったのでそれで開発してみようと思います。

まずは本体やらを準備しないといけないんですが、自分でビルドを行う必要があります。
公式サイトのDownloadsからBuildTools.jarをダウンロードします。
適当なディレクトリに投げてjava -jar BuildTools.jarのようにjarを実行すればビルドしてくれます。

必要なものが用意できたら、build.gradleを用意します。

ビルドパスにbukkitとspigot-apiを入れたいのでcraftbukkit-1.8.jarとspigot-api-1.8-略.jarをlibsフォルダに入れます。
基本的にspigot-apiのクラスを使用するのですが、Databaseの機能を使いたいのでbukkitも一緒に入れています。必要なければ入れる必要はないと思います。
jarタスクは誰か添削頼みます。groovy力が足りない・・・!じゃけんあとで勉強しましょうね~

開発環境は整いました。おしまい。
気分が乗ったら続き書くと思う。