月別アーカイブ: 2013年8月

新クライミングジム

昨日15日に大田区の蒲田にオープンしたクライミング(ボルダリング)ジムのKrimp(クリンプ)行ってきました。

ホールドまだかなり付けていないのがあったのであくまで現状です。

  • 高さは高からず低からず
  • 広さはぼちぼち(6面分?)
  • グレードは若干甘めなので気持ちよく登れる
  • 課題は2級までしか触ってない(触れない)けど素直なムーブが多いと思う
  • 空調は効いていて快適
  • 値段は安め
  • 休憩スペースは広い
  • 近場の飯はうまい(餃子のニーハオや、立ち飲み屋のバルバンチョとか)
  • 平日営業時間は2300まで

都心勤務で京浜東北線で横浜方面に帰るのだとかなりいい感じだと思う。

スノーデン自殺??

twitterを眺めていたら(元のツイートながれて見つからなくなった。。。)スノーデン氏が自殺体で見つかったという記事へのリンクが。

http://www.chronicle.su/politics/snowden-dead-of-apparent-suicide/

嫁との感想。

あめりかこえ~

元ネタが半分ネタサイトらしいけど、それでもほんと思うわ

 

 

 

WebSocketにてJSON形式のデータをJavaFXとやり取りしてみる。の4

先ほどに続いて送受信部分

送信処理

  1. 画面の送信ボタンからSingleController#handleSendActionが呼ばれる。
  2. SingleController#handleSendActionから@ClientEndpointのsendメソッド(自分で作った普通のメソッド)を呼び出す。
  3. sendメソッド内でSession.getAsyncRemote().sendObject(dataObj)を呼び出す
  4. encodersに指定してあるClientEncoderクラスのencodeメソッドが呼び出される。
  5. encodeメソッドでエンコードされたデータ(JSON形式の文字列)がServerEndpointに送信される。

エコープログラムなのでServerEndpointで受信されたデータは(若干加工され)ClientEndpointに戻される。そのため、続けて受信処理が走る。

 

受信処理

  1. サーバからのデータがPushされる。
  2. @ClientEndpointのdecodersに指定してあるClientDecoderクラスのwillDecodeメソッドが呼び出され入力値がチェックされる。
  3. willDecodeの結果がtrueならば続けてdecodeメソッドが呼び出されてジェネリクスで指定した型(ClientData)にデコードされる。
  4. デコードされたデータを引数に@ClientEndpointの@OnMessageが呼び出される。ここはJavaFXのスレッド配下ではなく、WebSocketのスレッドに移っている。そのため、JavaFXのコントロールを直接操作することはできない。
  5. @OnMessageの中でPlatform.runLaterを呼び、JavaFXの画面操作(接続時処理ViewObj#write)の呼び出しを登録(?)する。
  6. JavaFXスレッドに戻り、JavaFXの画面操作が実行される。

(コントローラーの関連する部分)

public class SingleController implements Initializable {
    @FXML
    private void handleSendAction(ActionEvent event) {
        System.out.println("SingleController#handleSendAction");
        wsClient.send(msgInput.getText(), sess);
    }
}

(ClientEndpointの関連する部分)

@ClientEndpoint( 
   decoders = { ClientDecoder.class }, 
   encoders = { ClientEncoder.class })
public class WSJsonSingleClient {

    @OnMessage
    public void onMessage(ClientData dataObj) {
        System.out.println("WSJsonSingleClient#onMessage");
        final ClientData channeled = dataObj;
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                viewObj.write(
                        channeled.getSessionId(), 
                        channeled.getMessageTimeString(), 
                        channeled.getMessage());
            };
        });
    }

    public void send(String text,Session sess) {
        System.out.println("WSJsonSingleClient#send");
        ClientData dataObj = new ClientData(text);
        sess.getAsyncRemote().sendObject(dataObj);
    }
}

(ClientEncoder)

public class ClientEncoder implements Encoder.Text<ClientData> {

    @Override
    public void init(EndpointConfig paramEndpointConfig) {
        System.out.println("ClientEncoder#init");
    }

    @Override
    public void destroy() {
        System.out.println("ClientEncoder#destroy");
    }

    @Override
    public String encode(ClientData paramData) throws EncodeException {
        System.out.println("ClientEncoder#encode");
        JsonObject model = Json.createObjectBuilder()
                   .add("message", paramData.getMessage())
                   .add("messageTime",paramData.getMessageTime())
                   .build();
        return model.toString();
    }
}

(ClientDecoder)

public class ClientDecoder implements Decoder.Text<ClientData> {

    @Override
    public void destroy() {
        System.out.println("ClientDecoder#destroy");
    }

    @Override
    public void init(EndpointConfig arg0) {
        System.out.println("ClientDecoder#init");
    }

    @Override
    public ClientData decode(String inputString) throws DecodeException {
        System.out.println("ClientDecoder#decode");
        try{
            JsonObject jsonObject = Json.createReader(new StringReader(inputString)).readObject();
            return  new ClientData(jsonObject);
        } catch(Exception e){
            e.printStackTrace();
            throw new DecodeException(inputString,"ClientDecoder#decode失敗", (Throwable)e);
        }
    }

    @Override
    public boolean willDecode(String inputString) {
        try {
            System.out.println("ClientDecoder#willDecode");
            Json.createReader(new StringReader(inputString)).readObject();
            return true;
        } catch (JsonException ex) {
            ex.printStackTrace();
            return false;
        }
    }
}

github:https://github.com/epea/test01/tree/WF_JSON_FX_SINGLE

WebSocketにてJSON形式のデータをJavaFXとやり取りしてみる。の3

先ほどの続き

接続処理

  1. 画面の開始ボタンからSingleController#handleStartButtonActionが呼ばれる。
  2. handleStartButtonAction内でClientManager#connectToServerを呼びWebSocketの起動を開始する。
  3. WebSocketの@OnOpenが呼ばれる。ここはJavaFXのスレッド配下ではなく、WebSocketのスレッドに移っている。そのため、JavaFXのコントロールを直接操作することはできない。
  4. @OnOpenの中でPlatform.runLaterを呼び、JavaFXの画面操作(接続時処理ViewObj#open)の呼び出しを登録(?)する。
  5. JavaFXスレッドに戻り、JavaFXの画面操作が実行される。

切断処理1

  1. 画面の終了ボタンからSingleController#handleStoptButtonActionが呼ばれる。
  2. Session#closeを呼び出し、WebSocketの終了処理を開始する。
  3. 切断処理2に続く

 

切断処理2

  1. 「切断処理1」または「ServerEndpointからの切断」等により、WebSocketの@OnCloseが呼ばれる。ここはJavaFXのスレッド配下ではなく、WebSocketのスレッドに移っている。そのため、JavaFXのコントロールを直接操作することはできない。
  2. @OnCloseの中でPlatform.runLaterを呼び、JavaFXの画面操作(接続時処理ViewObj#close)の呼び出しを登録(?)する。
  3. JavaFXスレッドに戻り、JavaFXの画面操作が実行される。

(コントローラーの関連する部分)

public class SingleController implements Initializable {

    private WSJsonSingleClient wsClient = null;
    private Session sess = null;

    // 略

    @FXML
    private void handleStartButtonAction(ActionEvent event) {
        System.out.println("SingleController#handleStartButtonAction");
        try {
            ClientManager m = org.glassfish.tyrus.client.ClientManager
                    .createClient();
            URI clientURI = new URI("ws://localhost:8080/first/hellojson/");
            sess = m.connectToServer(wsClient, clientURI);

        } catch (DeploymentException | URISyntaxException e) {
            Logger.getLogger(SampleController.class.getName()).log(
                    Level.SEVERE, null, e);
        }
    }

    @FXML
    private void handleStoptButtonAction(ActionEvent event) {
        System.out.println("SingleController#handleStoptButtonAction");
        try {
            sess.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

   // 略
}

(ClientEndpointの関連する部分)

@ClientEndpoint( decoders = { ClientDecoder.class }, encoders = { ClientEncoder.class })
public class WSJsonSingleClient {
    // 略
    @OnOpen
    public void onOpen(Session session) {
        System.out.println("WSJsonSingleClient#onOpen");
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                viewObj.open();
            };
        });
    }

    // 略

    @OnClose
    public void closeConnection(Session session) {
        System.out.println("WSJsonSingleClient#closeConnection");
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                viewObj.close();
            };
        });
    }
    // 略
}

github:https://github.com/epea/test01/tree/WF_JSON_FX_SINGLE

JSON送受信部分へ

 

WebSocketにてJSON形式のデータをJavaFXとやり取りしてみる。の2

先ほどの続き

前提条件

  • JavaFXのコントロール(ラベルとかボタンとか)はJavaFXのスレッド配下からでないと原則(※1)操作できない(と思う)
  • WebSocketはプログラム本体とは別のスレッドでデータの送受信を行う。
  • WebSocketのスレッド内からPlatform#runLaterを呼ぶことで、JavaFXのスレッド配下で行わせたい処理を登録できる。
  • 今の実装だと、接続状況の排他制御がきちんとされていないので、「サーバからの切断」と「クライアントからの起動」のシーケンスが重なったときとかにうまく動かないケースがでてくる。(はず)

※1 こちらにあるようにコントロールに結びついたObservableListを別スレッドから操作した場合は、その操作が画面に反映される。ラベルとかボタンとかのコントロールで試した範囲ではJavaFXのスレッドかどうかチェックにかかってうまくいかなかったがやりようはあるかもしれない。

ApplicationクラスとFXML

Applicationクラスは起動するだけ。

public class WSJsonSingle extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("SingleJson.fxml"));
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

 

FXMLはfx:idふって、onActionを紐付けている程度。

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

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane prefHeight="363.0" prefWidth="502.0" xmlns:fx="http://javafx.com/fxml" fx:controller="jp.co.epea.wsclient.SingleController">
  <children>
    <Label alignment="CENTER" contentDisplay="CENTER" prefHeight="43.0" prefWidth="502.0" text="JSON送受信" />
    <VBox layoutY="43.0" prefHeight="331.0" prefWidth="502.0">
      <children>
        <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0">
          <children>
            <Button fx:id="startButton" mnemonicParsing="false" onAction="#handleStartButtonAction" text="開始" />
            <Button fx:id="stopButton" mnemonicParsing="false" onAction="#handleStoptButtonAction" text="終了" />
            <Label fx:id="statusLabel" text="ステータス" />
          </children>
        </HBox>
        <HBox prefHeight="100.0" prefWidth="200.0">
          <children>
            <TextField fx:id="msgInput" editable="false" prefWidth="200.0" />
            <Button fx:id="sendButton" mnemonicParsing="false" onAction="#handleSendAction" text="送信" />
          </children>
        </HBox>
        <VBox prefHeight="200.0" prefWidth="100.0">
          <children>
            <HBox prefHeight="100.0" prefWidth="200.0">
              <children>
                <Label text="発言者:" />
                <Label fx:id="nameLabel" text="発言者初期値(起動時に変更)" />
              </children>
            </HBox>
            <HBox prefHeight="100.0" prefWidth="200.0">
              <children>
                <Label text="発言日時:" />
                <Label fx:id="timeLabel" text="発言日時初期値(起動時に変更)" />
              </children>
            </HBox>
            <HBox prefHeight="100.0" prefWidth="200.0">
              <children>
                <Label text="発言内容:" />
                <Label fx:id="msgLabel" text="発言内容初期値(起動時に変更)" />
              </children>
            </HBox>
          </children>
        </VBox>
      </children>
    </VBox>
  </children>
</AnchorPane>

起動処理

WSJsonSingle#mainから。

画面項目の初期化と、ClientEndpointのインスタンス化(まだ接続しない)。

public class SingleController implements Initializable {

    private WSJsonSingleClient wsClient = null;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        // コントロールをまとめたクラスを作って
        ViewObj obj = new ViewObj(nameLabel, timeLabel, msgLabel, statusLabel,
                msgInput, startButton, stopButton, sendButton);
        // ClientEndpointに渡してインスタンス化
        wsClient = new WSJsonSingleClient(obj);
    }
}

public class ViewObj {
    private Label nameLabel;
   // 略
    ViewObj(Label nameLabel, Label timeLabel, Label msgLabel,
            Label statusLabel, TextField msgInput, Button startButton,
            Button stopButton, Button sendButton) {
        super();
        // 略
        // コントロール類の初期化(項目設定とか、ボタンのdisable周りとか)
        this.init();
    }

    private void init(){
        nameLabel.setText("");
        // 略
    }
}

@ClientEndpoint( decoders = { ClientDecoder.class }, encoders = { ClientEncoder.class })
public class WSJsonSingleClient {

    private ViewObj viewObj;

    public WSJsonSingleClient(ViewObj viewObj) {
        this.viewObj = viewObj;
    }
    // 略
}

github:https://github.com/epea/test01/tree/WF_JSON_FX_SINGLE

続き

WebSocketにてJSON形式のデータをJavaFXとやり取りしてみる

Glassfish4とJavaFX間でJSON形式のデータをWebsocketにてやり取りしてみたのでメモ。

何ページかに分かれているけど実際にJSONのハンドリングをしている箇所はこちら

サーバサイドはこの前HTMLとやり取りしたものとほぼ同じ。

大まかな動きは以下。なお、表示はリスト形式ではなくてラベルの中身を置換していく形式(表示以外はやっていることはHTMLの時とほぼ一緒)

  1. HTMLからJSONで入力テキストと現在日時を送信。
  2. GlassfishのWebsocketで受信(&Decode)
  3. 受け取ったデータに情報(とりあえずユーザ情報代わりのセッションID)を付与し繋がっているクライアント(JavaFX&この前作ったHTML)全体にJSON形式で返信
  4. JavaFX(またはHTML)で受け取ったデータを表示

データ形式はこの前と同じ。(なので、クライアントはこの前のHTMLとJavaFXで併用できる。)

(JavaFX->glassfishのデータ形式)
{"message":"メッセージ","messageTime":時間(ミリ秒)}

(glassfish->JavaFXのデータ形式)
{"message":"メッセージ","sessionId":"セッションID","messageTime":"HH:mm:ss"}

斜体は変数

Maven

必要な依存関係を追加。(動作確認はeclipse上のM2E)

    <dependencies>
       <!-- JavaFX 2 -->
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>javafx</artifactId>
            <version>2.2</version>
            <scope>system</scope>
            <systemPath>${javafx2.home}</systemPath>
        </dependency>
        <!-- Websocket (JSR-356) -->
        <!-- TYRUS-210の対応で1.2にあげている -->
        <dependency>
            <groupId>org.glassfish.tyrus</groupId>
            <artifactId>tyrus-client</artifactId>
            <version>1.2</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.glassfish.tyrus</groupId>
            <artifactId>tyrus-container-grizzly</artifactId>
            <version>1.2</version>
            <scope>compile</scope>
        </dependency>
        <!-- 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>
    </dependencies>

画面フロー

起動時

開始ボタンしか押せない状態。裏ではWebSocket用のClientEndPointをインスタンス化している。

initial

 

開始ボタンクリック後(の入力欄に文字入力した状態)

入力欄や送信ボタン・終了ボタンが使えるようになる。

bef_send

送信ボタンクリック後

入力メッセージと時間がサーバに送信される。そしてデータを受信したサーバにて加工(といってもセッションID文字列を付加しただけ)されたデータがクライアントに送られ、そのデータがラベルに表示される。

aft_send

 

終了ボタンクリック後

開始ボタンしか押せない状態になる。

closed

 

github:https://github.com/epea/test01/tree/WF_JSON_FX_SINGLE

続き。

TYRUS-210の対応

昨日書いたDecoder.TextのwillDecodeが2度呼ばれる問題の対応(下参照)、試したら動いた。

Tyrus 1.2は既にGlassFishに統合・組み込まれています。
Nightly Buildのダウンロードもしくは手作業で新しいTyrusにアップグレードすることができます
(手作業の場合、全てのTyrusのjarファイルを置き換えて下さい)。
  1. Nightly Buildから最新のバージョンをダウンロード(8/11時点で glassfish-4.0.1-b02-08_10_2013.zip
  2. ダウンロードしたzipのglassfish4\glassfish\modulesに入っているtruis*.jarをインストールパスの同じところに上書き(自分の環境ではC:\develop\middles\glassfish4\glassfish\modules)

以上でeclipseから動かしてみるとDecoder.TextのwillDecodeが2度呼ばれることは無くなっていた。Eclipse上でのMaven指定等は特に修正する必要はなかった。

 

他にもいくつかバグが修正されているのでそれも直っているはず。

 

Decoder.TextのwillDecodeが2度呼ばれる(TYRUS-210)

WebSocketのServerEndpointとClientEndpointでDecoder.Text<T>を使っていたらwillDecodeが2度呼ばれていた。

Tyrusのバグ(TYRUS-210)とのこと。(1.2で修正済み)

(ServerEndpoint)
情報: TestDecoder#init
情報: TestDecoder#willDecode
情報: TestDecoder#willDecode
情報: TestDecoder#decode

(ClientEndpoint)
ClientDecoder#init
ClientDecoder#willDecode
ClientDecoder#willDecode
ClientDecoder#decode
WSJsonSingleClient#onMessage

クライアントサイドは新しいバージョンをpomに指定して修正されたことを確認。

   <dependency>
   <groupId>org.glassfish.tyrus</groupId>
   <artifactId>tyrus-client</artifactId>
   <version>1.2</version>
   <scope>compile</scope>
  </dependency>
  <dependency>
   <groupId>org.glassfish.tyrus</groupId>
   <artifactId>tyrus-container-grizzly</artifactId>
   <version>1.2</version>
   <scope>compile</scope>
  </dependency>

ClientDecoder#init
ClientDecoder#willDecode
ClientDecoder#decode
WSJsonSingleClient#onMessage

サーバサイドのGlassfishも下のように対応されているとのこと。(参照元)

Tyrus 1.2は既にGlassFishに統合・組み込まれています。
Nightly Buildのダウンロードもしくは手作業で新しいTyrusにアップグレードすることができます
(手作業の場合、全てのTyrusのjarファイルを置き換えて下さい)。

サーバサイドのMavenで対応されているものはぱっとみ見つからなかった。->実装の修正なのでサーバランタイムを変えたらいい? -> 変えたら治った

WebSocketのエンコード・デコード時のエラー

先日書いたWebsocketでJSONデータをやり取りしたブログだけど、エラー出力をきちんとしていなくてはまった。

Decoder.Text<T>のdecodeメソッド内でExceptionが発生した場合に、明示的にログ・スタックトレースの出力をしておかないと特にスタックトレース等が出力されないでdecodeメソッド(とそれから呼ばれる@OnMessage)が終了してしまう。(コンソールにエラーがつらつら出るようなことは無い。)

そのため、下みたいな感じで明示的にエラーあったことを出力してやらないとわかりにくい。

    @Override
    public TestData decode(String inputString) throws DecodeException {
        try{
            JsonObject jsonObject = Json.createReader(new StringReader(inputString)).readObject();
            return  new TestData(jsonObject);
        } catch(Exception e){
            DecodeException de = 
                    new DecodeException(inputString,"decode失敗", (Throwable)e);
            Logger.getLogger(TestDecoder.class.getName()).log(
                    Level.SEVERE, null, de);
            throw de;
        }
    }

Encoder.Text<T>のencodeメソッドも似たような話がある。

    @Override
    public String encode(TestData paramData) throws EncodeException {
        try{
            JsonObject model = Json.createObjectBuilder()
                       .add("message", paramData.getMessage())
                       .add("sessionId",paramData.getSessionId())
                       .add("messageTime", formatDate(paramData.getMessageTime()))
                       .build();
            return model.toString();
        } catch( Exception e ){
            EncodeException ee =  
                    new EncodeException(paramData, "encode失敗",  (Throwable)e);
            Logger.getLogger(TestEncoder.class.getName()).log(
                    Level.SEVERE, null, ee);
            throw ee;
        }
    }

一応decodeでエラーを発生させたときのログサンプル

重大: javax.websocket.DecodeException: decode失敗
    at jp.co.epea.first.json.TestDecoder.decode(TestDecoder.java:33)
    at jp.co.epea.first.json.TestDecoder.decode(TestDecoder.java:1)
    at org.glassfish.tyrus.core.EndpointWrapper.decodeCompleteMessage(EndpointWrapper.java:278)
    at org.glassfish.tyrus.core.SessionImpl.notifyMessageHandlers(SessionImpl.java:386)
    at org.glassfish.tyrus.core.EndpointWrapper.onMessage(EndpointWrapper.java:495)
    at org.glassfish.tyrus.server.TyrusEndpoint.onMessage(TyrusEndpoint.java:174)
    at org.glassfish.tyrus.websockets.DefaultWebSocket.onMessage(DefaultWebSocket.java:156)
    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.servlet.TyrusHttpUpgradeHandler.onDataAvailable(TyrusHttpUpgradeHandler.java:113)
    at org.apache.catalina.connector.InputBuffer$ReadHandlerImpl.processDataAvailable(InputBuffer.java:488)
    at org.apache.catalina.connector.InputBuffer$ReadHandlerImpl.onDataAvailable(InputBuffer.java:453)
    at org.glassfish.grizzly.http.io.InputBuffer.append(InputBuffer.java:855)
    at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:222)
    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:838)
    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:564)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
    at java.lang.Thread.run(Thread.java:724)
Caused by: java.lang.ClassCastException: org.glassfish.json.JsonStringImpl cannot be cast to javax.json.JsonNumber
    at org.glassfish.json.JsonObjectBuilderImpl$JsonObjectImpl.getJsonNumber(JsonObjectBuilderImpl.java:171)
    at jp.co.epea.first.json.TestData.<init>(TestData.java:21)
    at jp.co.epea.first.json.TestDecoder.decode(TestDecoder.java:30)
    ... 27 more

PMDでCUSTOM RULE

昔のブログから引っ越してきた、2011/08/05当時の記事です

 

参考サイトを見ながらぺちぺちとコーディングルール作りの勉強を開始。

今時点でわかったこと
・PMDではチェック対象ソースがツリー状のオブジェクトに変換される。
・ツリー状のオブジェクトに対してXpathやJavaで条件を記載していくことで
ルールを作成できる
・ルールを継承する時の親はAbstractRuleでなくAbstractJavaRuleになっている
・javaで条件を欠く場合は、AbstractJavaRuleがVisterパターンで作られているので
そのvisitに書いていけばよい
・visitの第一引数は色々な型で用意されている。
型はクラスの宣言を表す型等色々あるみたいだが要調査。
・ルールの呼び出しはリンク先のejbrules.xmlをイメージして準備すればよいらしい。(未実行)

######## 親クラス ######
// import net.sourceforge.pmd.AbstractRule; @deprecated
import net.sourceforge.pmd.AbstractJavaRule; // 4.2.5ではこっち

######## もともとのソース ######
package jp.epea;

public class A001 {
/**
* @param args
*/
public static void main(String[] args) {
if(true){

}

}

}

######## ツリー状のソースオブジェクト(実質XML表現:><はエスケープ) ######

 <?xml version="1.0" encoding="UTF-8"?>
<CompilationUnit beginColumn="1" beginLine="1" comments="[net.sourceforge.pmd.ast.FormalComment@aa415d]" endColumn="3" endLine="15" packageDeclaration="PackageDeclaration">
<PackageDeclaration beginColumn="1" beginLine="1" endColumn="16" endLine="1" packageNameImage="jp.epea">
<Name beginColumn="9" beginLine="1" endColumn="15" endLine="1" image="jp.epea"/>
</PackageDeclaration>
<TypeDeclaration beginColumn="1" beginLine="3" endColumn="1" endLine="15">
<ClassOrInterfaceDeclaration abstract="false" beginColumn="8" beginLine="3" endColumn="1" endLine="15" final="false" image="A001" interface="false" modifiers="1" native="false" nested="false" packagePrivate="false" private="false" protected="false" public="true" static="false" strictfp="false" synchronized="false" transient="false" volatile="false">
<ClassOrInterfaceBody beginColumn="19" beginLine="3" endColumn="1" endLine="15">
<ClassOrInterfaceBodyDeclaration anonymousInnerClass="false" beginColumn="5" beginLine="8" endColumn="5" endLine="13" enumChild="false">
<MethodDeclaration abstract="false" beginColumn="19" beginLine="8" block="Block" endColumn="5" endLine="13" final="false" interfaceMember="false" methodName="main" modifiers="17" native="false" packagePrivate="false" private="false" protected="false" public="true" resultType="ResultType" static="true" strictfp="false" synchronized="false" syntacticallyAbstract="false" syntacticallyPublic="true" transient="false" void="true" volatile="false">
<ResultType beginColumn="19" beginLine="8" endColumn="22" endLine="8" void="true"/>
<MethodDeclarator beginColumn="24" beginLine="8" endColumn="42" endLine="8" image="main" parameterCount="1">
<FormalParameters beginColumn="28" beginLine="8" endColumn="42" endLine="8" parameterCount="1">
<FormalParameter abstract="false" array="true" arrayDepth="1" beginColumn="29" beginLine="8" endColumn="41" endLine="8" final="false" modifiers="0" native="false" packagePrivate="true" private="false" protected="false" public="false" static="false" strictfp="false" synchronized="false" transient="false" typeNode="Type" varargs="false" volatile="false">
<Type array="true" arrayDepth="1" beginColumn="29" beginLine="8" endColumn="36" endLine="8" typeImage="String">
<ReferenceType array="true" arrayDepth="1" beginColumn="29" beginLine="8" endColumn="36" endLine="8">
<ClassOrInterfaceType beginColumn="29" beginLine="8" endColumn="34" endLine="8" image="String"/>
</ReferenceType>
</Type>
<VariableDeclaratorId array="false" arrayDepth="0" beginColumn="38" beginLine="8" endColumn="41" endLine="8" exceptionBlockParameter="false" image="args" typeNameNode="ReferenceType" typeNode="Type"/>
</FormalParameter>
</FormalParameters>
</MethodDeclarator>
<Block beginColumn="44" beginLine="8" endColumn="5" endLine="13">
<BlockStatement allocation="false" beginColumn="9" beginLine="9" endColumn="9" endLine="11">
<Statement beginColumn="9" beginLine="9" endColumn="9" endLine="11">
<IfStatement beginColumn="9" beginLine="9" endColumn="9" endLine="11">
<Expression beginColumn="12" beginLine="9" endColumn="15" endLine="9">
<PrimaryExpression beginColumn="12" beginLine="9" endColumn="15" endLine="9">
<PrimaryPrefix beginColumn="12" beginLine="9" endColumn="15" endLine="9">
<Literal beginColumn="12" beginLine="9" charLiteral="false" endColumn="15" endLine="9" floatLiteral="false" intLiteral="false" singleCharacterStringLiteral="false" stringLiteral="false">
<BooleanLiteral beginColumn="12" beginLine="9" endColumn="15" endLine="9" true="true"/>
</Literal>
</PrimaryPrefix>
</PrimaryExpression>
</Expression>
<Statement beginColumn="17" beginLine="9" endColumn="9" endLine="11">
<Block beginColumn="17" beginLine="9" endColumn="9" endLine="11"/>
</Statement>
</IfStatement>
</Statement>
</BlockStatement>
</Block>
</MethodDeclaration>
</ClassOrInterfaceBodyDeclaration>
</ClassOrInterfaceBody>
</ClassOrInterfaceDeclaration>
</TypeDeclaration>
</CompilationUnit>