• JavaFXのUIをJUnit形式でテストする

    Java Advent Calendar 2015と JavaFX Advent Calendar 2015の10日目の記事です。 昨日は下記のお二人でした。 Java Advent Calendarは@namihira_kさんの 「パフォーマンスを意識したJavaコーディング」 JavaFX Advent Calendarは@skrbさんの 「Interpolator で補間」 明日は下記のお二人です。 Java Advent Calendarは@bitter_foxさん JavaFX Advent Calendarは@y_q1mさん TestFXを知る 先月ダウンロードしたJava Magazine vol23に面白い記事が載っていました。 テストについて特集された中の、TestFXによるJavaFXのテストについての記事です。 TestFXはJavaFXのユーザ・インターフェースをJUnitベースでテストするためのAPIということで、 JUnitで書いたロジック通りにユーザ・インターフェースのテストが実施されます。 単純にロジックをなぞるだけではなく、実際にユーザ・インターフェースを操作した結果を判定してくれるようです。 これは、実際にテストを実行した際に、JavaFXのアプリ上でマウスカーソルが自動的に動いてボタンをクリックしたりすることからもわかります。 普段のプロジェクトでは、残念ながらJavaFXではなくFlexを使っているのですが、 ユーザ・インターフェース周りのテストの仕組みはあってもなかなか思ったようなテストができていないのが現実です。 TestFXはJUnitの延長上でテストができそうなので期待できそうです。 内容を説明する前に、実際に実行した際の動画を記録しました。 動画だと自動で動いているのかわからないと思いますが、 テスト起動後には何も操作をしていません。 アプリの説明 テストに使ったアプリは、ラベルとボタンのあるシンプルなものです。 ボタンをクリックすることで、ラベルに「Hello World!」と表示します。 実際のコードは下記にあります。 https://github.com/kokuzawa/javafx-test TestFXを設定 Mavenプロジェクトでは下記のDependencyを追加します。 <dependency> <groupId>org.loadui</groupId> <artifactId>testFx</artifactId> <version>3.1.2</version> <scope>test</scope> </dependency> テストを書く 対象のテストクラスは、TestFXを使うためにorg.loadui.testfx.GuiTestクラスを継承します。 org.loadui.testfx.GuiTestクラスはgetRootNode()メソッドを持ち、そのメソッドでテストしたい画面のFXMLをロードします。 package org.katsumi; import javafx.fxml.FXMLLoader; import javafx.scene.Node; import javafx.
  • WildFly SwarmでEJBを試す

    WildFly Swarmを試すのも今日で3回目です。 だんだんと実装方法に慣れてきました。 この辺で当初の目的であったWildFly SwarmでEJBを使ってみたいと思います。 EJBが使えないのならSpring Bootで全然構わないわけで、 EJBが使えるかどうかはとても大事なところです。 EJBを組み込む EJBのモジュールを組み込みます。 よくよく考えてみると、EJBだけでは動きを確認するのが大変なので、 リクエストの受け口だけはJAXRSで作ります。 なので、JAXRSのモジュールも合わせて組み込みます。 また、JAXRSのリソースから@InjectでEJBをDIするにはWeldも必要です。 そのため、JAXRSのモジュールはwildfly-swarm-jaxrs-weldを利用することにします。 <dependencies> <dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>wildfly-swarm-jaxrs-weld</artifactId> <version>1.0.0.Alpha5</version> </dependency> <dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>wildfly-swarm-ejb</artifactId> <version>1.0.0.Alpha5</version> </dependency> </dependencies> EJBを使ったアプリケーションを作る まずはEJBです。 package org.katsumi.ejb; import javax.ejb.Stateless; @Stateless public class HelloEjb { public String say() { return "Hello!"; } } EJBを呼び出すRESTリソースです。 package org.katsumi.ejb; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; @Path("/hello") public class HelloResource { @Inject private HelloEjb helloEjb; @GET public String hello() { return helloEjb.
  • WildFly SwarmでJSFを試す

    昨日はWildFly SwarmでJAXRSを触ったので、今日はJSFを試してみることにします。 JSFを組み込む 仕様毎にモジュールが分かれているので、JAXRSの時と同じく、今回はJSFのモジュールを取り込みます。 あと、ここで特に記載はしませんがwildfly-swarm-pluginももちろん設定する必要があります。 <dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>wildfly-swarm-jsf</artifactId> <version>1.0.0.Alpha5</version> </dependency> JSFアプリケーションを作る JSFアプリケーションを作ると言ってもJavaのコードを書くわけではなく、 動くことが分かれば良いのでXHTMLファイルだけを作るだけにします。 <?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"> <body> <h:outputText value="Hello JSF!"/> </body> </html> あと、JAXRSの時と違い、web.xmlを作る必要があります。 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <context-param> <param-name>javax.faces.PROJECT_STAGE</param-name> <param-value>Development</param-value> </context-param> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.xhtml</url-pattern> </servlet-mapping> </web-app> 動かす JAXRSの時と同じく、mainメソッドから実行するため、mainメソッドを持つクラスを作ります。 今回はJAXRSArchiveではなく、WARArchieをデプロイします。 package org.katsumi.jsf; import org.
  • WildFly SwarmでJAXRSを試す

    WildFly Swarmのサイトにも記載されていますが、WildFly Swarmは自己完結型のJava microservicesを作成するのに役立つプロジェクトとのこと。 この分野だとSpring Bootの方が運用実績もあり、先行しているようですが、 将来的にはEJBも使いたいのでWildFly Swarmの方を使ってみます。 WildFly Swarmは一つのモジュールというわけではなく、JavaEEの仕様毎に複数のモジュールに分かれていて、 自分の必要なモジュールを取り込んで利用する形になるようです。 JAXRSを組み込む 今回は数あるモジュールの中からJAXRSのモジュールを利用してみます。 2015年11月時点での最新バージョンは1.0.0.Alpha5です。 pom.xmlに下記のdependencyを追加します。 <dependency> <groupId>org.wildfly.swarm</groupId> <artifactId>wildfly-swarm-jaxrs</artifactId> <version>1.0.0.Alpha5</version> <dependency> これを依存グラフで見てみると…依存がすごいです(笑) JAXRSのモジュールを組み込んだだけではビルドしても実行できないので、 下記のpluginもpom.xmlに追加します。 <plugin> <groupId>org.wildfly.swarm</groupId> <artifactId>wildfly-swarm-plugin</artifactId> <version>1.0.0.Alpha5</version> <executions> <execution> <goals> <goal>package</goal> </goals> </execution> </executions> </plugin> JAXRSアプリケーションを作る WildFly Swarmの設定が一通り終わったので、次はJAXRSアプリケーションを作ります。 特別なことはなく、普通のJAXRSアプリケーションです。 package org.katsumi; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; @ApplicationPath("/rest") public class MyApplication extends Application { } package org.katsumi; import javax.ws.rs.GET; import javax.ws.rs.Path; @Path("/hello") public class HelloResource { @GET public String hello() { return "Hello World!
  • JAX-RSでStreamを扱う

    JAX-RSでExcelファイルをダウンロードする際にストリームを直接触る必要があって、 どうすればストリームにアクセスできるかちょっと調べてみました。 Excelファイルの生成にはApache POIを使っています。 Apache POIでExcelファイルを生成する場合、下記のようなコードを書きます。 final Workbook wb = new HSSFWorkbook(); final FileOutputStream fileOut = new FileOutputStream("workbook.xls"); wb.write(fileOut); fileOut.close(); 生成したファイルをファイルとして保存せずにServletでダウンロードしようとした場合、 そのコードは下記のようにHttpServletResponse#getOutputStream()でアウトプットストリームを取得し、 レスポンスボディに対してストリーミング処理をすることになります。 final Workbook wb = new HSSFWorkbook(); final FileOutputStream fileOut = new FileOutputStream(response.getOutputStream()); wb.write(fileOut); fileOut.close(); ここからが本題です。 JAX-RSでファイルをダウンロードするにはどうしたら良いのか。 通常のファイルの場合は下記のようなコードを書くことで実現できます。 final File file = new File("workbook.xls"); return Response.ok(file).build(); Servletでのダウンロードのように、 レスポンスボディに対してストリーミング処理をする場合はjavax.ws.rs.core.StreamingOutputクラスを利用します。 そのコードは下記のようになります。 final Workbook wb = new HSSFWorkbook(); final StreamingOutput so = out -> wb.write(out); return Response.ok(stream).build();
  • Googleのテキスト読み上げAPIを組み込む(その2)

    先日書いたコードがiOSのSafariで上手く動きませんでした。 Googleのテキスト読み上げAPIを組み込む 子供向けなのでiPhoneまたはiPadで音声が再生されないと困ります。 いろいろ調べたけれど、JavaScriptから呼び出す方法がわからないので 勝手知ったるJavaの世界に取り込んでiOSのSafariでも音声が再生されるようにしました。 下記がそのコードです。 @Path("/tts") public class TTSResource { @GET @Produces("audio/mpeg") public Response textToSpeech(@QueryParam("text") String text) throws IOException { final URL url = new URL("http://translate.google.com/translate_tts?tl=en&q=" + text); final URLConnection connection = url.openConnection(); connection.setRequestProperty("User-Agent", "Mozilla"); return Response.ok(connection.getInputStream()).build(); } } JAX-RSでGoogle Translateの結果をそのままレスポンスとして返すようにしています。 User-Agentを指定していないと上手く動きません。 ここで気がついたのですがUser-Agentで振る舞いが変わるようなので、 もしかしたらiOSからのアクセスの場合にもUser-Agentを偽装できれば音声が再生されるのかもしれないです。 先日の記事にも書きましたがクライアント側(javaScript)は下記のようになります。 <h:outputScript> function play() { var voice = new Audio(); voice.src = "${request.contextPath}/rest/tts?text=one"; voice.play(); } </h:outputScript> <a href="javascript:play()">音声</a>
  • Googleのテキスト読み上げAPIを組み込む

    子供用の英単語帳Webアプリを作っています。 単語の読み上げ機能があると便利だなと思い、そんなAPIはないかと調べてみると、 Googleが提供しているテキスト読み上げAPIというのを見つけました。 使い方はとても簡単で、下記のようなURLを呼び出せば良いようです。 この例ではoneという単語を読み上げてくれます。 http://translate.google.com/translate_tts?tl=en&q=one これをHTMLに組み込みます。 HTMLで音声を再生するためにはaudioタグを利用します。 下記のように組み込むことで音声を再生するためのコントロールを表示することができます。 <video src="http://translate.google.com/translate_tts?tl=en&q=one" controls /> こんな感じになります。 実際のアプリはJSF-2.2でFaceletを利用しています。 このタグを組み込んだ場合、最初の一回目は正しく再生されますが 2回目以降が再生されません。 なぜダメなのか結局分からなかったのですが、 UI的にはaudioコントロールを表示したくなかったので、 タグを埋め込むのではなくJavaScriptで再生するようにしました。 <script> function play() { var audio = document.createElement("audio"); audio.src = "http://translate.google.com/translate_tts?tl=en&q=one"; audio.play(); } </script> <a href="#" onClick="play()">再生</a> この場合audioコントロールは表示されないのですが、 再生リンクをクリックするたびに正しく再生されます。
  • web.xmlのバージョン別DTD・XSDの宣言方法

    JavaでWebアプリを作る場合、Mavenのmaven-archetype-webappテンプレートを利用しています。 非常に便利なのですが、Servlet 3.1が出ている今、 生成されたwe.xmlがServlet 2.3の記述になっていてちょっと古すぎます。 そのため、毎回テンプレートから生成したあとにweb.xmlを書き換えなければなりません。 このちょっと面倒な作業を楽にするためにバージョン毎の宣言方法をメモしておきます。 Servlet 2.3 maven-archetype-webappテンプレートで生成されるものです。 <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> </web-app> Servlet 2.4 <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" > </web-app> Servlet 2.5 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> </web-app> Servlet 3.0 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="
  • Deprecated org.hibernate.ejb.HibernatePersistence

    どのバージョンからなのかはわからないが、Hibernate-4.3.8.Finalではpersistence.xmlのproviderに記載するHibernateのプロバイダクラスがdeprecatedになっていた。 <provider>org.hibernate.ejb.HibernatePersistence</provider> 上記を下記に置き換える必要がある。 <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
  • java.util.Objectsを使おう

    この記事はJava Advent Calendar 2014の25日目です。 昨日は@smogamiさんの「LombokとLombok-pg: Javaコードを減量する魔法のスパイス」でした。 今年も様々な面白い記事がアップされました。 そんな楽しかった25日間も今日で最終日です。 最終日なのですが、ちょっと地味にJava7で追加されたjava.util.Objectsについて書きたいと思います。 java.util.Objectsクラスでは今までありそうでなかった便利なユーティリティメソッドが用意されています。 Java7の時点では下記のメソッドが定義されています。 static int compare(T a, T b, Comparator<? super T> c) static boolean deepEquals(Object a, Object b) static boolean equals(Object a, Object b) static int hash(Object… values) static int hashCode(Object o) static T requireNonNull(T obj) static T requireNonNull(T obj, String message) static String toString(Object o) static String toString(Object o, String nullDefault) 詳細はJavadocを参照してもらうとして、 私はrequireNonNullメソッドをよく利用しています。 いろいろなプロジェクトでメソッドの引数チェックの方法がマチマチだったのが、 このメソッドのおかげで引数チェック方法が統一できたのが画期的でした。 Lombokの@NonNullアノテーションでチェックすればいいじゃない?という話もありますが…(^^;