JSON Processing(JSR-353)に寄り道1

Websocketで複数のデータを送ろうとした際に、JavaEE7でJSON Processingが出てたのを思い出したので触ってみた。

ArunさんのブログにあるようにJSONとObject間のデータバインディングまではやってくれない。

(Note, binding JSON to Java objects and vice versa is not part of the scope of this JSR.)

想定用途(?)を探して見つけたブログによるとStaxでDom操作する感覚で使えるよとのこと。

->JCPにしっかり書いてあった。(2.1)

 

利用設定(Maven)

pomに依存関係を追加

        <!-- JSON Processing (JSR-353) -->
        <dependency>
            <groupId>javax.json</groupId>
            <artifactId>javax.json-api</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish</groupId>
            <artifactId>javax.json</artifactId>
            <version>1.0.2 </version>
        </dependency>

試したソース

import javax.json.Json;
import javax.json.JsonObject;
import javax.json.stream.JsonParser;

public class FirstJson01 {
    public static void main(String[] args) {
        System.out.println("生成");
        JsonObject model = Json.createObjectBuilder()
                   .add("firstName", "Duke")
                   .add("lastName", "Java")
                   .add("age", 18)
                   .add("streetAddress", "100 Internet Dr")
                   .add("city", "JavaTown")
                   .add("state", "JA")
                   .add("postalCode", "12345")
                   .add("phoneNumbers", Json.createArrayBuilder()
                      .add(Json.createObjectBuilder()
                         .add("type", "mobile")
                         .add("number", "111-111-1111"))
                      .add(Json.createObjectBuilder()
                         .add("type", "home")
                         .add("number", "222-222-2222")))
                   .build();

        String text = model.toString();
        System.out.println(text);

        System.out.println("解析");
        StringReader sr = new StringReader(text);
        JsonParser parser = Json.createParser(sr);
        while(parser.hasNext()){
            JsonParser.Event event = parser.next();
            System.out.println(event);
        }
    }
}

Eventの種類はSTART_OBJECT/END_OBJECT、START_ARRAY/END_ARRAY、KEY_NAME、VALUE_STRING、VALUE_NUMBER、VALUE_TRUE、VALUE_FALSE、VALUE_NULL。

 

 

 

M2E(Maven)のproperty外部化

以前JavaFXのMaven設定を行った際にjfxrt.jarへのパスはpomに直接記載していた。別端末(別のパス)で使用しようとしたときに外部化した際の手順。

 

設定を記載するsetting.xmlを作成。(proxyを使う際等に既に作っていることも多い。)

Defaultは下。(自分はEclipseの設定->Maven->ユーザ設定から変更)

    The Maven install: $M2_HOME/conf/settings.xml
    A user's install: ${user.home}/.m2/settings.xml

作成した内容は以下。

profile -> propertiesの下に、キー、値を指定する。

(参考サイト

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <localRepository/>
  <interactiveMode/>
  <usePluginRegistry/>
  <offline/>
  <pluginGroups/>
  <servers/>
  <mirrors/>
  <proxies/>
    <profiles>
        <profile>
            <id>javafx2</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
                <javafx2.home>C:\develop\pleiades43\pleiades\java\7\lib\jfxrt.jar</javafx2.home>
            </properties>
        </profile>
    </profiles>
  <activeProfiles/>
</settings>

pomで直接記載していた個所が変数で指定できるようになっている。

 

     <dependencies>
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>javafx</artifactId>
            <version>2.0</version>
            <scope>system</scope>
            <systemPath>${javafx2.home}</systemPath>
        </dependency>

 

今回参考にさせてもらったビルドまで記載されているブログ

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

WebSocketで受け取ったデータをJavaFXのラベルに設定する方法とりあえず動いた。

大まかなフローは、

  1. WebSocket起動
  2. Websocketの@OnMessageでデータ受信
  3. @OnMessageの中でPlatform.runLaterを使ってJavaFXのスレッドを起動(コマンドの依頼??)
  4. Platform.runLaterの中でLabel#setTextを呼び出して値を更新

(ボタンイベントからWebsocketの起動周り)

public class SampleController {

    @FXML
    private Label testLabel;

    @FXML
    private void handleButtonAction(ActionEvent event) {
        kickWebSocket();
    }

    private void kickWebSocket() {
        try {
            CountDownLatch messageLatch = new CountDownLatch(1);
            URI clientURI = new URI("ws://localhost:8080/first/hello/");
            ClientManager cliContainer = org.glassfish.tyrus.client.ClientManager
                    .createClient();
            cliContainer.connectToServer(new WSClient(testLabel), clientURI);
            messageLatch.await(1, TimeUnit.SECONDS);
        } catch (DeploymentException | URISyntaxException
                | InterruptedException ex) {
            Logger.getLogger(SampleController.class.getName()).log(
                    Level.SEVERE, null, ex);
        }
    }
}

(OnMeessageから値の設定)

@ClientEndpoint
public class WSClient {

    private Label testLabel;

    public WSClient(Label testLabel) {
        this.testLabel = testLabel;
    }

    @OnOpen
    public void onOpen(Session session) {
        System.out.println("Connection had opened.");
    }

    @OnMessage
    public void onMessage(String message) {
        if (message.length() == 0) {
            return;
        }
        final String channeled = message;
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                testLabel.setText(channeled);
            };
        });
    }

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

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

(一応サーバ側)

@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();
    }
}

まだ、確認できていないのがこの実装で順序の保障できているかどうか。

(実装的にはWebSocketボタン押した回数立ち上がったりと適当な部分はありますが。)

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

時系列のままに書いている間が漂ってきましたが。。。。

最初はボタンイベント(handleButtonAction)からjavafx.concurrent.Serviceをキックしていた。ただGrizzlyの中でWebsocket用のスレッドが立てられているので、「Websoketスレッド起動の処理」をスレッド化しても多分意味がないことに気づいた。少なくとも今問題になっている「Not on FX application thread」というやつの対策にはならなそう。

ボタンイベントから直接Websocket用のスレッド起動を行うように変更。

public class SampleController implements Initializable {

    @FXML
    private TableView<RowData> table;

    @FXML
    private TableColumn<RowData, String> column;

    @FXML
    private void handleButtonAction(ActionEvent event) {
        try {
            CountDownLatch messageLatch = new CountDownLatch(1);
            URI clientURI = new URI("ws://localhost:8080/first/hello/");
            ClientManager cliContainer = org.glassfish.tyrus.client.ClientManager
                    .createClient();
            cliContainer.connectToServer(new WSClient(table), clientURI);
            messageLatch.await(1, TimeUnit.SECONDS);
        } catch (DeploymentException | URISyntaxException
                | InterruptedException ex) {
            Logger.getLogger(SampleController.class.getName()).log(
                    Level.SEVERE, null, ex);
        }
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        table.setEditable(true);
        column.setResizable(true);
        column.setCellValueFactory(new PropertyValueFactory<RowData, String>(
                "message"));
    }
}

予定通りWebsocket(のスレッド)はキックされている。

a

 

@OnMessageでLabelの値に反映させる方法は調査中。 -> 動いた

それを皮切りに”ObservableList経由で反映しているTableView”以外もやっつけるつもりだったけどそもそもできるか不安になってきた。。。

 

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の変更をトリガーにした通知機能をうまく使っているみたい。

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

spam対策

ここ数日198.245.49.39と137.175.15.131から数時間おきにspamコメントが寄せられるうになってきた。

ipは決まっているのでとりあえずiptablesではじくように設定。

iptables -I INPUT -s 198.245.49.39 -j DROP
iptables -I INPUT -s 137.175.15.131 -j DROP
/etc/rc.d/init.d/iptables save
(各IPからのパケットをはじく設定&保存。)

今のところは、他のIPからのスパムはないから出てきたベースで拒否していけばいいけど頻度増えてきたらAkismetとかのプラグインではじいた方がいいのかな。

スタックトレースを頼りに海外から来てくれている人もいるので国でばっさりと絞りたくはないし。

(2013/8/6までにスパムが来たIP)

216.244.85.234
205.164.24.90
58.22.70.108
137.175.15.134
185.25.48.24
198.2.200.9
137.175.105.39
137.175.118.101
137.175.15.133
137.175.68.241
137.175.105.38
114.251.150.133
123.184.133.84
163.125.181.151
137.175.15.132
137.175.118.100
137.175.68.178
71.61.84.216
117.27.138.148
137.175.118.99
198.27.82.114
137.175.15.131
198.245.49.39
198.100.144.195

Eclipse(Luna)のスケジュール感

Eclipse(Luna)のJDK 8対応状況をみるとJava Model とかもぼちぼち手がつけられそうな状況になってきた。いまのところMLにあった感じに秋(original GA in September)にはぼちぼち触れるようになりそうかなぁ。

ラムダ式周りをPMDかFindbugsのカスタムルール作りつつ様子見ようと思ったけど、自分が手を出すには技術不足感に満ち溢れているので挫折。

なおJDTのgitレポジトリは以下。

  • JDT Core repository – git://git.eclipse.org/gitroot/jdt/eclipse.jdt.core.git
  • JDT UI repository – git://git.eclipse.org/gitroot/jdt/eclipse.jdt.ui.git

詳細情報は

http://wiki.eclipse.org/Platform-releng/Git_Workflows

 

 

 

Effectively final

先日のJJUGナイトセミナー(前半資料/後半資料)でfianlつけていなくてもfinalとみなされるEffectively finalというものがJava 8から導入されるという話を聞いた。(前半資料16pに出てくる「実質的final」)

public class JavaApplication1 {
    public static void main(String[] args) {
        String hoge = "こんにちは世界。";
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                System.out.println(hoge);
            }
        };
        r1.run();

        Runnable r2 = () -> {
            System.out.println(hoge);
        };
        r2.run();
    }
}

上みたいなfinalの付いていない変数(String hoge)も代入していなければfinalとみなして匿名クラス、ラムダ式の中で使えるようになったとのこと。

 

ちなみにJava 7までだと下みたいにエラー。

a

 

final付いていないけどfinal扱いなので再代入したらだめ。

String hoge = "こんにちは世界。";
        hoge ="こんばんわ";
        Runnable r1 = new Runnable() {
            @Override

とか

            public void run() {
                hoge ="こんばんわ";
                System.out.println(hoge);
            }

とか

        Runnable r2 = () -> {
            hoge ="こんばんわ";
            System.out.println(hoge);
        };

とか。

エラーがある状態で(Netbeansから?)実行すると

Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - 内部クラスから参照されるローカル変数は、finalまたは事実上のfinalである必要があります

とか

Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - ラムダ式から参照されるローカル変数は、finalまたは事実上のfinalである必要があります

わかりやすいメッセージが表示される。

軽く触った感じだときれいに動いている。

(講演してくれた方がこのfinalまわりでよくはまったといっていたので、まだトラップありそうな気がする)

JDK8バグ?

先日のJDK 8をWindowsにインストールするとレジストリに書き込まれるっぽい問題を避けるために、NetbeansとJDK 8をCentOS(6.4 64bit)にインストールした。起動しようとしたところ下のようなエラーが発生。

java.lang.IllegalArgumentException: committed = 52752384 should be < max = 50331648
    at java.lang.management.MemoryUsage.<init>(MemoryUsage.java:162)
    at sun.management.MemoryImpl.getMemoryUsage0(Native Method)
    at sun.management.MemoryImpl.getNonHeapMemoryUsage(MemoryImpl.java:75)
    at org.netbeans.core.ui.warmup.DiagnosticTask.logParams(DiagnosticTask.java:193)
    at org.netbeans.core.ui.warmup.DiagnosticTask.run(DiagnosticTask.java:82)
[catch] at org.netbeans.core.startup.WarmUpSupport.run(WarmUpSupport.java:98)
    at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1432)
    at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:2042)

JDKのバグ??

WarmUpSupportとかいうのが死んだっぽいけどcommittedがmax越えるって設定ミスじゃなさそうだし、まぁ多分JDKもNetbeansも両方EAだし。ハローワールドのレベルだと動いたのでとりあえず放置。

 

 

 

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添付、ソース添付から設定することで妥協。