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>


では実行してみましょうファイルを添付して
f:id:den2sn:20130618233444p:plain


Uploadボタンを押すと
f:id:den2sn:20130618233500p:plain


無事ファイルがアップロードされてファイル名が表示されました。


では今度は処理を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>


では同じように実行してみましょうファイルを添付して
f:id:den2sn:20130618233444p:plain


Uploadボタンを押すと
f:id:den2sn:20130618234615p:plain


こちらも無事ファイルがアップロードされてファイル名が表示されました。AjaxによりoutputTextの部分しか更新されていないため入力部分のファイルのパスが消えていません。


実は現状AjaxのファイルアップロードはIEではもう一度実行しようとするとエラーになってしまいます。また、ChromeFirefox等の別のブラウザでは見えてはいけないiFrameが表示されて処理が途中で終了してしまいます。


GlassFishJSF実装のMojarraのJIRAに障害報告されているようなのでいずれ修正されると思います。

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メソッドを実行するともちろんデータが増える
f:id:den2sn:20130617221833p:plain


ではデータ登録後にExceptionを発生させるように修正する

@Transactional
public void test() throws Exception {
    Test t = new Test();
    t.setMessage("test");
    em.persist(t);
    throw new Exception(); //追加
}


実行するとExceptionがスローされるがチェック例外なのでやっぱりデータは追加される。
f:id:den2sn:20130617221946p:plain


ではExceptionではなくてRuntimeExceptionを発生させる。

@Transactional
public void test() throws Exception {
    Test t = new Test();
    t.setMessage("test");
    em.persist(t);
    throw new RuntimeException(); //変更
}


実行すると今度は非チェック例外なのでjavax.transaction.TransactionalExceptionがスローされて追加処理はロールバックされる。
f:id:den2sn:20130617221946p:plain


では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になる。
f:id:den2sn:20130617221946p:plain


では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ではロールバックするように修正されていました。

f:id:den2sn:20130617222243p:plain


基本的には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/)のAPIJavaから簡単に呼び出すライブラリを作りました。

・Cacoo4J
https://github.com/den2sn/cacoo4j

クラスパスにzip内の「cacoo4j-1.0.0.jar」を通すと使えるようになると思います。
認証にOAuthを使用する場合はさらに「signpost-core-1.2.1.1.jar」が必要ですがAPI Keyで認証する場合は不要です。

ソースとか汚いですがなんとなく動くと思うので気にしないで下さい。

Mavenリポジトリにはめんどくさいのでまだ登録してません。
気が向いたらやるかもね。

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で簡単管理

f:id:den2sn:20080622164547j:plain:rightBootstrapや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の気になるところ

f:id:den2sn:20130425133434p:plain:right
やっとやっと待ちに待った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には間に合わなかったようですね。
まだ標準化は難しいんでしょうか。

注目どころ

注目どころというよりは私が気になっているところをまとめます。

・JSON Processing

やっとな感じですがJava標準でJSONが操作出来るようになりますね。
基本はJAX-RS用な感じでしょうか。

・WebSocket

WebSocketもEE7で導入になります。
これでリアルタイム系のWebアプリも増えてきたりするかもしれないですね。
アノテーションベースでサーバ実装がとても簡単のようです。

・Batch

やっぱり企業系だとバッチ処理系のニーズは多いんでしょうかね。
Springのバッチ機能を踏襲しているようなので
ファーストリリースにしては結構使いやすい感じに
落ち着いているのではないかと思っています。

JTA

仕様の中に@Transactionalっていうアノテーションがあるようなのですが、
これってSessionBeanもういらないんじゃない的な
POJOにトランザクション指定できる機能なのでしょうか。
そうだとしたらアプリケーションがよりシンプルに
開発出来るようになるのではないかと思っています。

JSF

やっとな感じですがJSFがファイルアップロード対応します。
今まで無かったのが驚きですよね。
これでPrimeFacesとかに頼らなくてもファイルアップロードできます。
あとは、HTML5の対応とか。
Faces flowってやつ(ショッピングカートとか簡単に作れるのかな?)
が入るっぽいです。

JAX-RS

EE7のメインと言っても良いのがJAX-RSでは無いでしょうかね。
JSFも2.0でとても良いものになりましたが
JAX-RSも2.0でかなり整備されるようです。
主にフィルターとインターセプターの機能や
CDI連携、Bean Validation対応、非同期処理、クライアントAPI
等がありますね。


あとの問題はGlassFishは同時リリースされるとして
他のサーバはどれくらいでEE7に対応するかに注目と言ったところでしょうか。


リリースされたらいろいろと検証してみたいと思います。