JSF 2.2 で待ちに待ったFileUploadを試す
JSF 2.2 でやっと導入されたFileUploadを試してみました。
まずサーバ側のBackingBeanを作成します。
package sample; import javax.inject.Named; import javax.enterprise.context.RequestScoped; import javax.servlet.http.Part; @Named @RequestScoped public class UploadPage { private Part file; public Part getFile() { return file; } public void setFile(Part file) { this.file = file; } }
JSFすごい。もはや処理すらありません。
fileのゲッターセッターを準備します。
ファイルはServlet3.0のファイルアップロードと同様にjavax.servlet.http.Partで受け取れます。
では送信するFecelets側の処理を書いてみましょう。
<?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"> <h:head> <title>Upload Test</title> </h:head> <h:body> <h:form enctype="multipart/form-data"> <h:inputFile id="file" value="#{uploadPage.file}" /><br /> <h:commandButton value="Upload" /><br /> <h:outputText id="fileName" value="#{uploadPage.file == null ? '' : uploadPage.file.submittedFileName}" /> </h:form> </h:body> </html>
ファイルアップロードなのでもちろんformにenctype="multipart/form-data"を指定します。
あとはh:inputFileを配置するだけです。
ファイルが存在している場合はServlet3.1で追加されたsubmittedFileNameを呼び出してファイル名を表示しています。
最後にこのままだと日本語のファイル名が文字化けしてしまうのでglassfish-web.xmlにdefault-charsetでUTF-8を指定しておきます。NetBeansで自動生成したものに1行追加。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd"> <glassfish-web-app error-url=""> <class-loader delegate="true"/> <jsp-config> <property name="keepgenerated" value="true"> <description>Keep a copy of the generated servlet class' java code.</description> </property> </jsp-config> <parameter-encoding default-charset="UTF-8" /> </glassfish-web-app>
では実行してみましょうファイルを添付して
Uploadボタンを押すと
無事ファイルがアップロードされてファイル名が表示されました。
では今度は処理をAjaxでのファイルアップロードに修正してみましょう。
<h:form enctype="multipart/form-data"> <h:inputFile id="file" value="#{uploadPage.file}"></h:inputFile><br /> <h:commandButton value="Upload"> <f:ajax execute="@this" render="fileName" /> </h:commandButton><br /> <h:outputText id="fileName" value="#{uploadPage.file == null ? '' : uploadPage.file.submittedFileName}" /> </h:form>
では同じように実行してみましょうファイルを添付して
Uploadボタンを押すと
こちらも無事ファイルがアップロードされてファイル名が表示されました。AjaxによりoutputTextの部分しか更新されていないため入力部分のファイルのパスが消えていません。
実は現状AjaxのファイルアップロードはIEではもう一度実行しようとするとエラーになってしまいます。また、ChromeやFirefox等の別のブラウザでは見えてはいけないiFrameが表示されて処理が途中で終了してしまいます。
Java EE 7のTransactionalアノテーションを試してみる
Java EE 7からの新機能 JTA 1.2で追加されたTransactionalアノテーションを試してみた。
まずmessageというフィールドをもった単純なEntityクラスを作成して
@Entity public class Test implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String message; // GetterとSetterは長いので省略 }
次にデータを追加するクラスを作成するのだけれども今まで作成していたSessionBeanはこんな感じ。
@Stateless public class TestFacade { @PersistenceContext(unitName = "TESTPU") private EntityManager em; public void test() throws Exception { Test t = new Test(); t.setMessage("test"); em.persist(t); } }
でも今回はこれをTransactionalを付加したCDIのクラスに修正する。
@RequestScoped public class TestFacade { @PersistenceContext(unitName = "TESTPU") private EntityManager em; @Transactional public void test() throws Exception { Test t = new Test(); t.setMessage("test"); em.persist(t); } }
TestFacadeをインジェクトしてtestメソッドを実行するともちろんデータが増える
ではデータ登録後にExceptionを発生させるように修正する
@Transactional public void test() throws Exception { Test t = new Test(); t.setMessage("test"); em.persist(t); throw new Exception(); //追加 }
実行するとExceptionがスローされるがチェック例外なのでやっぱりデータは追加される。
ではExceptionではなくてRuntimeExceptionを発生させる。
@Transactional public void test() throws Exception { Test t = new Test(); t.setMessage("test"); em.persist(t); throw new RuntimeException(); //変更 }
実行すると今度は非チェック例外なのでjavax.transaction.TransactionalExceptionがスローされて追加処理はロールバックされる。
ではTransactionalアノテーションのrollbackOnにExceptionクラスを指定したうえでExceptionを発生させてみる
@Transactional(rollbackOn = Exception.class) public void test() throws Exception { Test t = new Test(); t.setMessage("test"); em.persist(t); throw new Exception(); }
実行するとチェック例外であってもデータはロールバックされて追加されない。エラーはもちろんTransactionalExceptionになる。
ではExceptionを継承したClassNotFoundExceptionを発生させてみる
@Transactional(rollbackOn = Exception.class) public void test() throws Exception { Test t = new Test(); t.setMessage("test"); em.persist(t); throw new ClassNotFoundException(); }
実行するとバグなのか仕様なのかわからないけれど処理はロールバックされずにコミットされた。継承クラスじゃ駄目なのかな。
(バグっぽい:https://java.net/jira/browse/GLASSFISH-20533)
2015/4/26 追記:GlassFish4.1ではロールバックするように修正されていました。
基本的にはSessionBeanのトランザクション制御と大きな大差は無い感じ。今後はSessionBeanはあまり使わずにこっちがメインになるかもね。
Java EE 7(GlassFish V4)でJAX-RSのMVCテンプレートを動かす
GlassFish V4でJersyのMVCテンプレートを動かしてみました。
基本は下のブログで書いたGlassFish V3の時と大きく変わりません。
Javaを知らない世代が今からはじめるJava EE開発
http://den2sn.hatenablog.com/entry/2012/12/19/001146
V4で注意する点は2つ
Viewableのクラスパスが変わっている
Jerseyのパッケージが「com.sun」から「org.glassfish」に変わったこともあり以前のパッケージは
com.sun.jersey.api.view.Viewable
となっていましたが、GlassFish V4で使用しているJersy2.0では
org.glassfish.jersey.server.mvc.Viewable
に変わっています。
Mavenプロジェクトの場合
<dependency> <groupId>org.glassfish.jersey.ext</groupId> <artifactId>jersey-mvc</artifactId> <version>2.0</version> <scope>provided</scope> </dependency>
を追加すれば大丈夫でしょう。
ApplicationConfigクラスでJspMvcFeatureを追加する
GlassFish V4ではそのままではJSPのプロバイダが登録されていないのでApplicationConfigでJspMvcFeatureを追加します。
Class jspProvider = Class.forName("org.glassfish.jersey.server.mvc.jsp.JspMvcFeature"); resources.add(jspProvider);
ApplicationConfigの全ソースはこんな感じ。
package com.den2sn.mavenproject1; import java.util.Set; import javax.ws.rs.core.Application; @javax.ws.rs.ApplicationPath("webresources") public class ApplicationConfig extends Application { @Override public Set<Class<?>> getClasses() { Set<Class<?>> resources = new java.util.HashSet<Class<?>>(); try { Class jsonProvider = Class.forName("org.glassfish.jersey.jackson.JacksonFeature"); resources.add(jsonProvider); } catch (ClassNotFoundException ex) { java.util.logging.Logger.getLogger(getClass().getName()).log(java.util.logging.Level.SEVERE, null, ex); } // ここから try { Class jspProvider = Class.forName("org.glassfish.jersey.server.mvc.jsp.JspMvcFeature"); resources.add(jspProvider); } catch (ClassNotFoundException ex) { java.util.logging.Logger.getLogger(getClass().getName()).log(java.util.logging.Level.SEVERE, null, ex); } // ここまで addRestResourceClasses(resources); return resources; } private void addRestResourceClasses(Set<Class<?>> resources) { resources.add(com.den2sn.javaee7test.TestResource.class); } }
これで快適なMVCライフを送る事が出来ます。
JavaからCacoo APIを簡単に呼び出せるCacoo4jを作った
Cacoo(https://cacoo.com/)のAPIをJavaから簡単に呼び出すライブラリを作りました。
・Cacoo4J
https://github.com/den2sn/cacoo4j
クラスパスにzip内の「cacoo4j-1.0.0.jar」を通すと使えるようになると思います。
認証にOAuthを使用する場合はさらに「signpost-core-1.2.1.1.jar」が必要ですがAPI Keyで認証する場合は不要です。
ソースとか汚いですがなんとなく動くと思うので気にしないで下さい。
JSFのFaceletsでxhtmlに直接アクセスさせない方法
JSFでFaceletsを作成すると/facesでアクセスする場合は良いのですが、直接xhtmlファイルにアクセスするとソースがそのまま表示されて微妙な感じですよね。xhtmlファイルに直接アクセスさせないようにするには基本JSPで行う対処法と変わりません。
ただし、JSPでもっともよく使われているWEB-INF配下にファイルを配置する方法が、faceletsの場合テンプレートファイルでは可能ですが普通のファイルでは使えないと思うのでその他2つの方法を試してみたいと思います。
web.xmlのsecurity-constraintを使用する方法
特にアプリケーションの認証でsecurity-constraintを使用していない場合、security-constraintでxhtmlにアクセス制限を付加する事で直接アクセスを防ぐ事ができます。ただし、web.xmlのURLパターンマッチングは簡単なマッチングしか記載出来ないためパスがfacesで始まるかどうかをハンドリングできません。そのためFacesServletのURLパターンマッチングを.jsf等に変えてあげる必要があります。
servlet-mappingを変更
<servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.jsf</url-pattern> </servlet-mapping>
security-constraintを設定
<security-constraint> <display-name>XHTML Security</display-name> <web-resource-collection> <web-resource-name>Protected Area</web-resource-name> <url-pattern>*.xhtml</url-pattern> </web-resource-collection> <auth-constraint> </auth-constraint> </security-constraint>
このような設定を行うことで.jsfへのアクセスは許可し、.xthmlへのアクセスは拒否することができます。パターンマッチングを*.jsfに変える事でURLが変わってしまうのでその点は注意して使用する必要があります。
Filterを使う方法
ServletFilterを使用してxhtmlへのアクセスを制限します。ここでもurl-patternマッチングが貧弱なので*.xhtmlへのアクセスをフィルタで受けてそのパスが/facesで始まっているか確認します。
web.xml
<filter> <filter-name>XHTMLSecurity Filter</filter-name> <filter-class>filter.XHTMLSecurityFilter</filter-class> </filter> <filter-mapping> <filter-name>XHTMLSecurity Filter</filter-name> <url-pattern>*.xhtml</url-pattern> </filter-mapping>
XHTMLSecurityFilter.java
package filter; import java.io.IOException; import javax.servlet.*; import javax.servlet.http.*; public class XHTMLSecurityFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String path = ((HttpServletRequest) request).getServletPath(); if (path.startsWith("/faces")) { chain.doFilter(request, response); } else { ((HttpServletResponse) response).sendError(HttpServletResponse.SC_NOT_FOUND); } } @Override public void destroy() { } }
これでxhtmlファイルが表示されてしまう問題が解消できます。
結論
web.xmlのURLパターンマッチングをどうにかしてほしい。
BootstrapやjQueryをWebJarsで簡単管理
BootstrapやjQuery等のクライアントサイドライブラリを管理するのって意外とめんどくさいですよね。WebJarsはよく使用されるクライアントサイドライブラリをJarに固めてJVMベースのWebアプリケーションで簡単に扱えるようにしようというプロジェクトです。
WebJars
http://www.webjars.org/
クライアントサイドライブラリをJarにすることでMaven等のライブラリ管理ツールでインストールや依存関係を管理する事が出来るようになります。
利用方法は非常に簡単。Mavenの場合、まずWebJarsの必要なライブラリをpom.xmlに記載します。例えばBootstrapを使用したい場合は以下のような感じ。
<dependencies> <dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap</artifactId> <version>2.3.1</version> </dependency> </dependencies>
JSF2ではlibに配置されたjar内のMETA-INF/resources内のファイルに静的にアクセス可能なのであとはHTMLで参照すればOKです。たとえばJSFであればこんな感じで使用できます。
<h:outputStylesheet name="webjars/bootstrap/2.3.1/css/bootstrap.min.css" /> <h:outputScript name="webjars/bootstrap/2.3.1/js/bootstrap.min.js" /> <h:outputScript library="webjars/jquery/1.9.0" name="jquery.min.js" />
ここで試してみると気づくと思いますが、特に定義していなくてもbootstrapが依存しているjqueryのライブラリもちゃんと一緒にlibに配置されて使用可能になっています。
ドキュメントを見ると、Play 2やServlet3、Grails、Spring MVC等でも利用可能なようなので大抵のJVMベースのWebアプリケーションでは使用できるのではないでしょうか。
やや最新のライブラリの配置が遅かったりするようですが、わざわざ各サイトにいってjsファイル等をダウンロードしてくるよりはとても簡単にライブラリ管理が出来るようになります。
リリース間近!Java EE 7の気になるところ
やっとやっと待ちに待ったJava EE 7のリリースが近づいて来ましたね。
予定では5/13(日本だと14日?)にFinal Releaseのようです。
https://java.net/projects/javaee-spec/pages/Home#Java_EE_7_Schedule
リリースに備えて軽く自分の備忘録も兼ねて予習しておきます。
あんまり追っかけているわけではないので間違ってたらすみません。
各テクノロジーのバージョンはこんな感じ。
Java EE 7で新規で追加されるもの
テクノロジー | バージョン |
---|---|
Java API for JSON Processing (JSR-353) | 1.0 |
Java API for WebSocket (JSR-356) | 1.0 |
Batch Application for the Java Platform (JSR-352) | 1.0 |
Concurrency Utilities for Java EE (JSR-236) | 1.0 |
アップデートされるもの
テクノロジー | EE6バージョン | EE7バージョン |
---|---|---|
Enterprise JavaBeans (EJB) | 3.1 | 3.2 |
Servlet | 3.0 | 3.1 |
JavaServer Pages (JSP) | 2.2 | 2.3 |
Expression Language (EL) | 2.2 | 3.0 |
Java Messaging Service (JMS) | 1.1 | 2.0 |
Java Transaction API (JTA) | 1.1 | 1.2 |
JavaMail API | 1.4 | 1.5 |
Java Connector Architecture (JCA) | 1.6 | 1.7 |
Web Services | 1.3 | 1.4 |
Java API for XML-based Web Services (JAX-WS) | 2.2 | 2.2 |
Java API for RESTful Web Services (JAX-RS) | 1.1 | 2.0 |
Java Architecture for XML Binding (JAXB) | 2.2 | 2.2 |
Java EE Management | 1.1 | 1.1 |
Java Authorization Service Provider Contract for Containers (JACC) | 1.4 | 1.5 |
Java Authentication Service Provider Interface for Containers (JASPIC) | 1.0 | 1.1 |
JSP Debugging | 1.0 | 1.0 |
JavaServer Pages Standard Tag Library (JSTL) | 1.2 | 1.2 |
Web Services Metadata for the Java Platform | 2.1 | 2.1 |
JavaServer Faces (JSF) | 2.0 | 2.2 |
Common Annotations | 1.1 | 1.2 |
Java Persistence API (JPA) | 2.0 | 2.1 |
Bean Validation | 1.0 | 1.1 |
Managed Beans | 1.0 | 1.0 |
Interceptors | 1.1 | 1.2 |
Contexts and Dependency Injection for Java EE (CDI) | 1.0 | 1.1 |
Dependency Injection for Java | 1.0 | 1.0 |
あと削除ものとしてJAX-RPCのような既に使われないであろう仕様は
外されている感じです。
JCache (JSR-107)は残念ながらEE7には間に合わなかったようですね。
まだ標準化は難しいんでしょうか。
注目どころ
注目どころというよりは私が気になっているところをまとめます。
・WebSocket
WebSocketもEE7で導入になります。
これでリアルタイム系のWebアプリも増えてきたりするかもしれないですね。
アノテーションベースでサーバ実装がとても簡単のようです。
・Batch
やっぱり企業系だとバッチ処理系のニーズは多いんでしょうかね。
Springのバッチ機能を踏襲しているようなので
ファーストリリースにしては結構使いやすい感じに
落ち着いているのではないかと思っています。
・JTA
仕様の中に@Transactionalっていうアノテーションがあるようなのですが、
これってSessionBeanもういらないんじゃない的な
POJOにトランザクション指定できる機能なのでしょうか。
そうだとしたらアプリケーションがよりシンプルに
開発出来るようになるのではないかと思っています。