JavaFX」タグアーカイブ

JavaFXとWebsocketを連携してみる(On Glassfish4.0) その2

前回からスレッド周りの制約事項にかかってうまくいっていないけど状況の整理。

やろうとしていることは、前回は表の中にWebsockeで取得したデータを反映しようとしていたものを、Labelに変更して表示しようというもの。

1

起きているエラーはJavaFXのコンポーネントをJavaFX配下以外のスレッドで呼んではいけないというもの。

スタックトレースは以下。

java.lang.IllegalStateException: Not on FX application thread; currentThread = Grizzly-worker(1)
エラーです:e9e8fea3-96b6-46be-ad8e-a393e1499784
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:237)
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:397)
    at javafx.scene.Parent$1.onProposedChange(Parent.java:245)
    at com.sun.javafx.collections.VetoableObservableList.setAll(VetoableObservableList.java:90)
    at com.sun.javafx.collections.ObservableListWrapper.setAll(ObservableListWrapper.java:314)
    at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:602)
    at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(LabeledSkinBase.java:209)
    at com.sun.javafx.scene.control.skin.SkinBase$3.changed(SkinBase.java:282)
    at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:107)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:196)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
    at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:121)
    at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:128)
    at javafx.beans.property.StringPropertyBase.access$100(StringPropertyBase.java:67)
    at javafx.beans.property.StringPropertyBase$Listener.invalidated(StringPropertyBase.java:236)
    at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:155)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
    at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:121)
    at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:128)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:161)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:67)
    at jp.co.epea.firstfxclient.ObjData.setDataValue(ObjData.java:26)
    at jp.co.epea.firstfxclient.WSClient.onMessage(WSClient.java:30)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.glassfish.tyrus.core.AnnotatedEndpoint.callMethod(AnnotatedEndpoint.java:431)
    at org.glassfish.tyrus.core.AnnotatedEndpoint.access$100(AnnotatedEndpoint.java:83)
    at org.glassfish.tyrus.core.AnnotatedEndpoint$WholeHandler$1.onMessage(AnnotatedEndpoint.java:518)
    at org.glassfish.tyrus.core.SessionImpl.notifyMessageHandlers(SessionImpl.java:391)
    at org.glassfish.tyrus.core.EndpointWrapper.onMessage(EndpointWrapper.java:498)
    at org.glassfish.tyrus.container.grizzly.GrizzlyClientSocket.onMessage(GrizzlyClientSocket.java:393)
    at org.glassfish.tyrus.websockets.frametypes.TextFrameType.respond(TextFrameType.java:66)
    at org.glassfish.tyrus.websockets.DataFrame.respond(DataFrame.java:102)
    at org.glassfish.tyrus.container.grizzly.WebSocketFilter.handleRead(WebSocketFilter.java:300)
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:837)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:565)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:545)
    at java.lang.Thread.run(Thread.java:724)

 

現在の書き方(ソース)でかかっている場所は、ClientManager#connectToServerでGrizzlyのスレッドがたっていて、

        @Override
        protected Task createTask() {
            Task<Void> task = new Task<Void>() {
                @Override
                protected Void call() throws Exception {

                    try {
                        URI clientURI = new URI("ws://localhost:8080/first/hello/");
                        ClientManager cliContainer = org.glassfish.tyrus.client.ClientManager.createClient();
                        cliContainer.connectToServer(new WSClient(data), clientURI);

                    } catch (DeploymentException  ex) {
                        Logger.getLogger(SampleController.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    return null;
                }
            };
            return task;
        }

その中で動いているOnmessegeで値を書き換えようとしていること

    ObjData data;

    @OnMessage
    public void onMessage(String message) {
        if (message.length() == 0) {
            return;
        }
        System.out.println(data.getDataValue());
        data.setDataValue(message);//下の自分で作ったメソッド
        System.out.println(data.getDataValue());
    }

// ObjDataの中
    private SimpleStringProperty dataValue;
    public void setDataValue(String dataValue) {
        dataValueProperty().set(dataValue);
    }

改めて前回サンプルにしたソースを見直すとObservableListの変更をトリガーにした通知機能をうまく使っているみたい。

なお、自前でスレッドを管理している場合のドキュメントはこちら

JavaFXソース、JavaDoc添付(Eclipse 4.3 Kepler)

JavaFXのソースとJavaDocのアタッチのをしていなく何かと不便なので添付する。

そもそもPleiadesに入っていたJava 7にくっついていたもの程度の意識なのでまずはこちらのソースを使ってバージョンを調べる。

javafx.runtime.version: 2.2.25-b15

だそう。

まずJavaDocから入れようとダウンロードサイトを開くとjavafx-2_2_25-apidocs.zipとbuild番号が付いていない。ダウンロードしてみて、レポジトリのチェンジログと比べるとb19の変更が反映されているので時系列に差がある模様。とはいえ、リリースノートが下な感じで、大きな修正は言ってもいないのでそのまま突き進む。

The full version string for this update release is 1.7.0_25-b15 (where "b" means "build") except for Windows on which it is 1.7.0_25-b17. The version number is 7u25.

 

疲れたので、以下作業予定で今日は挫折。 (7/22 再開)

ソースダウンロード(そもそもMercurial入れなければいけないので入れる) -> リンク先の右上にダウンロード用リンクあったので、zipでダウンロード。

ソースとJavaDocを適宜フォルダを作って配備。(C:\dev\attachment\javafx2.2.25)

pomの修正(Localレポジトリを作成しないときれいに添付できないかもしれない。参照先)

プロパティー->Javaのビルドパス->ライブラリー->maven依存関係->jafxt.jarのJavadoc添付、ソース添付から設定することで妥協。

 

JavaFX&Websocket連携(エラーパターン)

対向のサーバが落ちているとき。

素直にハンドシェイクできないという。まぁ、そのまんま。

7 20, 2013 5:43:29 午後 jp.co.epea.firstfxclient.SampleController$TwitterCheckService$1 call
SEVERE: null
javax.websocket.DeploymentException: Handshake response not received.
    at org.glassfish.tyrus.client.ClientManager.connectToServer(ClientManager.java:300)
    at org.glassfish.tyrus.client.ClientManager.connectToServer(ClientManager.java:172)
    at jp.co.epea.firstfxclient.SampleController$TwitterCheckService$1.call(SampleController.java:65)
    at jp.co.epea.firstfxclient.SampleController$TwitterCheckService$1.call(SampleController.java:1)
    at javafx.concurrent.Task$TaskCallable.call(Task.java:1259)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
    at java.util.concurrent.FutureTask.run(FutureTask.java:166)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:724)

 

URI clientURI = new URI(“ws://localhost:8080/first/hellohoge/”);

みたいにURLが間違っている場合。

Handshake errorというやつとスタックとレースにレスポンスコードが返ってくる。

この例だと存在しないパスなので404が返っているけど、WebsocketじゃないただのHTMLだとUpdate Protocolの時に失敗しているので(?)200が返ってくる。

なお、パスの指定時は最後の”/(スラッシュ)”も厳密に見る模様。

7 20, 2013 8:46:51 午後 jp.co.epea.firstfxclient.SampleController$TwitterCheckService$1 call
SEVERE: null
javax.websocket.DeploymentException: Handshake error.
    at org.glassfish.tyrus.client.ClientManager.connectToServer(ClientManager.java:285)
    at org.glassfish.tyrus.client.ClientManager.connectToServer(ClientManager.java:172)
    at jp.co.epea.firstfxclient.SampleController$TwitterCheckService$1.call(SampleController.java:65)
    at jp.co.epea.firstfxclient.SampleController$TwitterCheckService$1.call(SampleController.java:1)
    at javafx.concurrent.Task$TaskCallable.call(Task.java:1259)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
    at java.util.concurrent.FutureTask.run(FutureTask.java:166)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:724)
Caused by: org.glassfish.tyrus.websockets.HandshakeException: Response code was not 101: 404
    at org.glassfish.tyrus.websockets.HandShake.validateServerResponse(HandShake.java:314)
    at org.glassfish.tyrus.websockets.draft06.HandShake06.validateServerResponse(HandShake06.java:98)
    at org.glassfish.tyrus.container.grizzly.WebSocketFilter.handleClientHandShake(WebSocketFilter.java:368)
    at org.glassfish.tyrus.container.grizzly.WebSocketFilter.handleHandshake(WebSocketFilter.java:353)
    at org.glassfish.tyrus.container.grizzly.WebSocketFilter.handleRead(WebSocketFilter.java:274)
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:837)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:565)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:545)
    ... 1 more

Connection had closed.

 

ちなみに、エラーログをはいているのはSampleController#createTaskの中にあるキャッチ節。

        @Override
        protected Task createTask() {
            Task<Void> task = new Task<Void>() {
                @Override
                protected Void call() throws Exception {
                    messageLatch = new CountDownLatch(1);
                    try {
                        URI clientURI = new URI("ws://localhost:8080/first/hellohoge/");
                        ClientManager cliContainer = org.glassfish.tyrus.client.ClientManager.createClient();
                        cliContainer.connectToServer(new TwitterClient(table), clientURI);
                        messageLatch.await(1, TimeUnit.SECONDS);
                    } catch (DeploymentException | URISyntaxException | InterruptedException ex) {
                        Logger.getLogger(SampleController.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    return null;
                }
            };
            return task;
        }

下の@OnErrorは通らずに、@OnClose(前のログだと緑字部分)で終わるので注意。

    @OnClose
    public void closeConnection(Session session) {
        System.out.println("Connection had closed.");
    }

    @OnError
    public void onError(Session session,Throwable t) {
        System.out.println("error");
        t.printStackTrace();
    }

javafx.concurrent.Taskの使い方次第でもっとハンドリングできそうな気がする。
とりあえず参考になりそうなサイト

JavaFXとWebsocketを連携してみる(On Glassfish4.0) その1

サーバサイドはいつものようにGlassfish 4.0。まずは寺田さんのブログを見つつメッセージ送信用のページで入れたメッセージが表示されるようにするとこまで持っていく。(元のブログのようにツイッターを見に行きはしないで、index.htmlで入れたものが表示されるようにする。)

サーバ用のエンドポイント(ws://localhost:8080/first/hello/)

package jp.co.epea.first;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/hello/")
public class HelloWorld {

    static Set<Session> sessions = Collections
            .synchronizedSet(new HashSet<Session>());

    @OnMessage
    public void onMessage(String message) {
        System.out.println("message[" + message + "]");

        for (Session s : sessions) {
            s.getAsyncRemote().sendText(message);
        }
    }

    @OnOpen
    public void open(Session sess) {
        System.out.println("開始します:" + sess.getId());
        sessions.add(sess);
    }

    @OnClose
    public void close(Session sess) {
        System.out.println("終了します:" + sess.getId());
        sessions.remove(sess);
    }

    @OnError
    public void error(Session sess,Throwable t) {
        System.out.println("エラーです:" + sess.getId());
        t.printStackTrace();
    }
}

メッセージ送信用のページ(http://localhost:8080/first/index.html)

<!DOCTYPE html>
<html>
    <head>
        <title>WebSocketテスト</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
        <script type="text/javascript">
            var socket;
            $(document).ready(function(){
                var host="ws://localhost:8080/first/hello/";
                socket = new WebSocket(host);

                socket.onmessage = function(message){
                    $('#log').append(message.data + "<br/>");
                }

                $('#send').click(function(){
                    var text = $('#msg').val();
                    socket.send(text);
                    $('#msg').val('');
                })

            });
        </script>        
    </head>
    <body>
        <h1>WebSocketテスト</h1>
        <div id="log">
        </div>
        <input id="msg" type="text"/>
        <button id="send">送信</button>
    </body>
</html>

 

クライアントサイドの作成の流れは

  1. MavanプロジェクトとしてJavaFXを作成(前回ブログ)
  2. 足りない依存関係を追加
  3. 寺田さんのブログのクライアントサイドプログラムをコピペ
  4. とりあえず動くようになるために最低限の修正を加える

(手順1と3は省略)

2.足りない依存関係を追加

tyrus-clientとtyrus-container-grizzlyはこちらのブログを参考にしたときに「Grizzly 関連の jar」の今版として使えそうなので試したところいけたっぽいので持ってきた。(ユーザガイド)

    <dependencies>
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>javafx</artifactId>
            <version>2.0</version>
            <scope>system</scope>
            <systemPath>C:\dev\pleiades43\java\7\lib/jfxrt.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>org.glassfish.tyrus</groupId>
            <artifactId>tyrus-client</artifactId>
            <version>1.1</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.glassfish.tyrus</groupId>
            <artifactId>tyrus-container-grizzly</artifactId>
            <version>1.1</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

 

4.とりあえず動くようになるために最低限の修正を加える

TwitterClientのアノテーションを@ClientEndpoint、@OnOpen、@OnMessage、@OnCloseに変更。

SampleController.javaのURLを修正。

ClientEndpointConfigurationの行をコメントアウト。

URI clientURI = new URI("ws://localhost:8080/first/hello/");
//ClientEndpointConfiguration clientConfig = new DefaultClientConfiguration();

 

動作確認

サーバを起動する。

JavaFXを起動する。

a

start timelineボタンをクリックしてサーバと接続

(コンソールメッセージ)
Connection had opened.

 

index.htmlからメッセージを送信

b

 

JavaFX側に表示される

c

 

一応これで動作しているようなのでこいつを基点に触っていくことにする。

(JavaFX側をXボタンで閉じたときにOncloseが呼ばれてないっぽいけど、実装的にそういう動きになるのか環境がおかしいのか。。。。。)

2013/11/03追記

子の実装ではJavaFXのTableViewを使用することで、WebSocket側のスレッドによる変更がJavaFXのスレッドに通知されている。(オブザーバ)

桜庭さんに伺ったところ、「(GUIはそっちのスレッドから動かすのが基本なので)きちんと動かないことあるかもよ」とのことでした。

他の日記ではLabelで同期しようとした場合にPlatform.runLaterでJavaFXのスレッドに処理を戻しているけど、そちらの方法が基本とのことでした。

 

JavaFXのMaven(M2E)設定(Eclipse 4.3 Kepler)

Eclipse4.3(実際に使っているのはPleiades all in one)にてM2Eを使ってJavaFXをMavenプロジェクトとして動かすまでのメモ。

新規->その他->Maven->Mavenプロジェクトから次へ次へといってアーキタイプの選択を開く

以前入れたリモートレポジトリに入っているorg.codehaus.mojo.archetypesのjavafxを選択

注:JavaFXのアーキタイプは何種類かあるようでディレクトリ構造は、JavaFX用アークテクとタイプの中でも結構ばらついている。なのでスタンダードな構造はきちんと調べた方がいいかも。

a

 

任意の名前をつけてプロジェクトを作成。

そのままだとJavaFX用のJarをMavenが見えていないので「FXML を型に解決できません」とか「インポートされた javafx は見つかりません」とかで怒られる。

pom.xml(プロジェクト直下ある)にjarのパスを依存関係に追加してやって「Maven->プロジェクトの更新」とやってやると動くようになる。(今のとこ無難に動いている模様。)

追加した内容

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>jp.co.epea</groupId>
    <artifactId>firstfxclient</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>firstfxclient</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <mainClass>jp.co.epea.firstfxclient.MainApp</mainClass>
    </properties>

    <organization>
        <!-- Used as the 'Vendor' for JNLP generation -->
        <name>Your Organisation</name>
    </organization>

    <dependencies> <-アーキタイプによってはすでにあるので注意(dependencyタグだけでよい)
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>javafx</artifactId>
            <version>2.0</version>
            <scope>system</scope>
            <systemPath>C:\dev\pleiades43\java\7\lib/jfxrt.jar</systemPath> <-パスは実際にある場所
        </dependency>
    </dependencies>
    <build>

(8/5追記)

パスの外部化と、参考サイトへのリンク記事追加

(12/15追記)

実行可能Jarの作り方リンク

 

JDK8とScene Builder 1.0の併用不可?

(7/25追記)

この件はJDK 8インストール時にPublic JREのインストールをはずしておけば大丈夫だった。

a

 

他にもちょこちょこ触っていたので濡れ衣かもしれないけどもWindwos7の64bitではJDK 8(EA)とScene Builder 1.0の併用は単純には不可っぽい。

64bit版Windows7にてJDK 8(Early AccessのBuild b98)をインストールした翌日に、Scene Builder 1.0を起動しようとしたところ起動できなくなっていた。

java -versionで1.8を呼んでいるので

JAVA_HOME=C:\Program Files\Java\jdk1.7.0_25
Path=C:\Program Files\Java\jdk1.7.0_25\bin;~

な感じに環境変数をセットして1.7にパスを通しなおしてみたけどだめ。

レジストリのHKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime EnvironmentをみるとCurrentVersionに1.8とかレジストリはいろいろ書いてある。

scenebuilder.exeなのでそっちを見てうまくいかないのかな??

ということで、JDK 8(とアプリ的には別にインストールされていたJRE 8)をアンインストールしたら無事に起動できた。

濡れ衣かも知れないけどそういうことと思っておこう。。。。

 

Eclipse 4.3(Kepler)ベースでJavaFXの開発環境構築

ベースはPleiades All in OneのEclipse4.3 ultimate。

JavaFX開発用プラグインのe(fx)clipseを以下のupdateサイトからインストール。

http://www.efxclipse.org/p2-repos/releases/latest/

何をインストールするか聞かれたけど、とりあえず全部選択した。

OracleのサイトからScene Builder(バージョンは1.0)のインストーラを落としてきてインストール。

 

Eclipse上からScene Builderを開けるようにするため、「Eclipseのウィンドウ->設定->JavaFX」にてシーンビルダーのインストールパスを指定。

自分の場合はC:\Program Files\Oracle\JavaFX Scene Builder 1.0\bin\scenebuilder.exe

 

この段階でエクリプスを再起動したら

新規->JavaFXから作成できるようになっているはず。

(New FX DocumentがfxmlでJavafx main classがメインクラス。)

 

JavaFX 2で始めるGUI開発の第2回までのソースを軽く追った範囲では大体問題なく動く模様。

ただし、Scene Builder上でイベント処理(リンク先の図10)を指定しようとしたときに、候補選択が効かなかったため手動で打ち込んだ。(Scene Builderのバージョンとかそちら側の問題かもしれないが調べられていない。 -> 他のものを触っているときにはScene Builderの1.0でも候補選択が有効だった。少なくともバージョンの問題ではない。)

a

(本来なら赤枠のエリアに選択肢が出てくるとのこと。)

JavaFXエラーパターン(FXMLLoaderのファイル名誤り)

FXMLLoader.load(getClass().getResource(“間違ったファイル名“));

としたときのエラーメッセージはロケーションが必須と若干意味が違うものが表示される。

Exception in Application start method
Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:403)
    at com.sun.javafx.application.LauncherImpl.access$000(LauncherImpl.java:47)
    at com.sun.javafx.application.LauncherImpl$1.run(LauncherImpl.java:115)
    at java.lang.Thread.run(Thread.java:724)
Caused by: java.lang.NullPointerException: Location is required. <-ぱっと見わからない
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2739)

(中略)
  at jp.co.epea.firstclient.test3.TestLoginFXML.start(TestLoginFXML.java:15)

 

FXMLLoaderの中身見るとクラスローダーで探せないからURLがnullで渡されいってこのメッセージになっているらしい。

public static <T> T load(URL paramURL)
    throws IOException
  {
    return load(paramURL, null);
  }

->
if (paramURL == null) {
      throw new NullPointerException("Location is required.");
    }

JavaFXエラーパターン(Stage#setSceneの漏れ)

ITProのJavaFXサンプルを写経しつつ少しずつエラーを起こしてみる。

LoginDemoクラスにあるstage.setScene(scene);をコメントアウトしてみる。

        public void start(Stage stage) {
            stage.setTitle("Login Demo");

            AnchorPane root = new AnchorPane();
            Scene scene = new Scene(root);
            //stage.setScene(scene);  <-ここのコメントアウト

 

外枠部分だけ表示されて中が透明(背景がそのまま見える)な状態で表示されて枠ごと動かすとメモリがいっぱいいっぱいの時のような表示になる。

(枠の中に背景=Eclipseの画面が表示されている状態)

a

 

(ドラッグして表示が乱れている状態)

b

sceneに対して他のメソッドもコールしていなければEclipse上で”ローカル変数 scene の値は使用されていません”といわれるから多分気づく。ただ、メソッド呼んでいると警告出ないのでその時に事象を知らなかったら環境回りを疑いそう。

 

ちなみに、VBoxとかのコメントアウトはそのコンポーネント部分が表示されないだけなので切り分けは多分付く。

VBox vbox = new VBox();
//root.getChildren().add(vbox);

(コンポーネントが表示されていない状態)
c

 

 

 

JavaFXエラーパターン(fxmlのimport漏れ)

JavaFXのimport文がもれていると以下のようなエラー

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Label?> <-これがあるべきなのに書かれていない
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane prefWidth="100" prefHeight="40" xmlns:fx="http://javafx.com/fxml">
    <children>
        <Label text="Hello, World!" />
    </children>
</AnchorPane>

クラスを実行しようとすると項目名が不正な旨のエラーが発生する。

エラーになった行(ハローワールド)と項目名がでるのでそのインポート文を見直せばOK。

エラーログ

Label is not a valid type.
/C:/dev/pleiades43/workspace/firstclient/bin/jp/co/epea/firstclient/Hello.fxml:7
  at javafx.fxml.FXMLLoader.createElement(FXMLLoader.java:2381)
  at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2311)
  at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2131)

ちなみにScene Builderだと下のようにラベルが表示されない。コンポーネント1種類だけのっていない場合は実行するまで気付かないかもしれない。

a

 

 

スペルミスの場合(クラスパスが通っていない場合も同じだと思う)

エラーログ

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Label2?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane prefWidth="100" prefHeight="40" xmlns:fx="http://javafx.com/fxml">
    <children>
        <Label2 text="Hello, World!" />
    </children>
</AnchorPane>

java.lang.ClassNotFoundException: javafx.scene.control.Label2
/C:/dev/pleiades43/workspace/firstclient/bin/jp/co/epea/firstclient/Hello.fxml
  at javafx.fxml.FXMLLoader.importClass(FXMLLoader.java:2455)
  at javafx.fxml.FXMLLoader.processImport(FXMLLoader.java:2299)

Scene Builderで開こうとするとClassNotFoundExceptionで怒られるのでこっちは多分気づく。

b

ファイル'C:\dev\pleiades43\workspace\firstclient\src\jp\co\epea\firstclient\Hello.fxml'のロード中にエラーが発生しました。
C:\dev\pleiades43\workspace\firstclient\src\jp\co\epea\firstclient\Hello.fxml:0: error: 
    java.lang.ClassNotFoundException: javafx.scene.layout.Label2