JSF 2.0 + CXF でWebサービス呼び出し

前回 の続き。クライアントをWebアプリケーションにしてみる。Webアプリケーションフレームワークは、最近気になるJSF2。環境はこんな感じ。

プロジェクトの作成

  1. Eclispeで普通に「動的Webプロジェクト」を作成(とりあえず ws-webclient というプロジェクト名)。ただしソースフォルダは src/main/java と src/main/resources を作り、出力先は target/classes に変更、Webコンテンツフォルダも src/main/webapp に変更。
  2. プロジェクトを右クリックして「Maven」→「依存関係管理を使用可能にする」。グループIDは前と同じ example.cxf、アーティファクトIDは ws-webclient。
  3. プロジェクトを右クリックして「プロパティー」→「Javaのビルド・パス」→「順序およびエクスポート」で、「Maven Dependencies」にチェック(不要?)。
  4. pom.xml に以下のように依存関係と build 要素を追加。
  <version>0.0.1-SNAPSHOT</version>
  <!-- ここから追加 -->
  <properties>
    <cxf.version>2.2.5</cxf.version>
  </properties>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.0.2</version>
        <configuration>
          <encoding>UTF-8</encoding>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <dependencies>
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-frontend-jaxws</artifactId>
      <version>${cxf.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-transports-http</artifactId>
      <version>${cxf.version}</version>
    </dependency>
    <dependency>
      <groupId>example.cxf</groupId>
      <artifactId>ws-contract</artifactId>
      <version>0.0.1-SNAPSHOT</version>
    </dependency>
    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-api</artifactId>
      <version>2.0.1</version>
    </dependency>
    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-impl</artifactId>
      <version>2.0.1</version>
    </dependency>
  </dependencies>
  <repositories>
    <repository>
      <id>maven2-repository.dev.java.net</id>
      <name>Java.net Repository for Maven</name>
      <url>http://download.java.net/maven/2/</url>
    </repository>
  </repositories>
  <!-- ここまで追加 -->
</project>

追加したら、プロジェクトを右クリックして「Maven」→「プロジェクト構成の更新」を行う。

Springでのスタブ生成の設定

JSFの管理対象BeanにインジェクションするためのサービススタブをSpringで管理するため、src/main/resources フォルダに applicationContext.xml を以下の内容で追加。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:cxf="http://cxf.apache.org/core"
  xmlns:jaxws="http://cxf.apache.org/jaxws"
  xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
    http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

  <import resource="classpath:META-INF/cxf/cxf.xml" />
  <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
  <import resource="classpath:META-INF/cxf/cxf-extension-http.xml" />

  <cxf:bus>
    <cxf:features>
      <cxf:logging />
    </cxf:features>
  </cxf:bus>

  <jaxws:client id="helloWorldService"
    address="http://localhost:8080/ws-server/HelloWorld" serviceClass="example.cxf.HelloWorldService" />

</beans>

中身は、ほぼ前回の Main クラスの中身をXMLで書いているだけ。ただし、サーバ側と同じく全てのメッセージをログに出力するように cxf:bus の設定を追加している。

web.xml にも Spring の初期化用の設定を追加。

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>

  <listener>
    <listener-class>
      org.springframework.web.context.ContextLoaderListener
    </listener-class>
  </listener>

JSF

src/main/java 以下に example/cxf/managed フォルダを作成し、管理対象bean HelloWorld.java を作成。

package example.cxf.managed;

import java.io.Serializable;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.RequestScoped;

import example.cxf.HelloWorldService;

@ManagedBean
@RequestScoped
public class HelloWorld implements Serializable {

  private static final long serialVersionUID = 1L;

  @ManagedProperty(value="#{helloWorldService}")
  private HelloWorldService helloWorldService;

  private String name;

  private String result;

  public String greet(){
    result = helloWorldService.sayHi(name);
    return null;
  }

  public void setHelloWorldService(HelloWorldService helloWorldService) {
    this.helloWorldService = helloWorldService;
  }

  public String getResult() {
    return result;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

上の @ManagedProperty(value="#{helloWorldService}") に指定されたEL変数 helloWorldService を Spring の bean からも探すようにするため、src/main/resources フォルダに META-INF フォルダを作成し、その中に faces-config.xml ファイルを以下のような内容で作成する。

<?xml version="1.0"?>
<faces-config version="2.0"
  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-facesconfig_2_0.xsd">
  <application>
    <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
  </application>
</faces-config>

テンプレートファイル index.xhtml を src/main/webapp フォルダに作成。"Greet" ボタンを押すと、Ajaxで管理対象Bean HelloWorldのgreetメソッドを呼び出し、結果を p#result 要素の中身にセットする。

<?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://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:ui="http://java.sun.com/jsf/facelets">
<head jsfc="h:head">
<title>HelloWorld</title>
<h:outputScript name="jsf.js" library="javax.faces" />
</head>
<body>

<form jsfc="h:form">
  <p>
    <input jsfc="h:inputText" id="name" value="#{helloWorld.name}" />
    <input jsfc="h:commandButton" id="greetButton" action="#{helloWorld.greet}" value="Greet">
      <f:ajax execute="name" render="result" />
    </input>
  </p>

  <p id="result" jsfc="h:outputText" value="#{helloWorld.result}" />
</form>

</body>
</html>

最後に、web.xmlJSF用の設定を追加し、以下のようにする。

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
  </context-param>
  <listener>
    <listener-class>
      org.springframework.web.context.ContextLoaderListener
    </listener-class>
  </listener>
  <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>/faces/*</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>faces/index.xhtml</welcome-file>
  </welcome-file-list>

完成

ws-webclient プロジェクトを右クリックし、「実行」→「サーバーで実行」でTomcat上で動かす。

しかるのちに http://localhost:8080/ws-webclient/ にアクセスするとテキスト入力とGreetボタンがあるだけの画面が表示される。

テキストに hoge と入力しGreetボタンを押せば、前回と同じく HelloWorld Webサービスが呼び出され、"Hello hoge" が画面に返される。

めでたしめでたし。

TODO

  • 単なる文字列でなくオブジェクトのやりとりをする
  • サービス側をDBを使うような本格的なものにする
  • クライアント側にユーザ認証を追加し、さらに認証情報をサービス側に受け渡す