CXFでWebサービスを試す
Webサービスを実装する必要に迫られたので CXF で試してみる。
以下のような環境で、Webサービスインタフェースの定義、Webサービスのサーバ、Webサービスのクライアント、の3つのMavenアーティファクトをEclipseで開発できるところまでが目標。
サービスインタフェースのプロジェクトを作成
Webサービスインタフェースの生成元になるJavaのinterfaceを定義するプロジェクト。とりあえず別プロジェクトにしてみる。
- Eclipseで普通に「Javaプロジェクト」を作成(とりあえず ws-contract というプロジェクト名)。ただしソースフォルダは src/main/java、出力先は {プロジェクト名}/target/classes に変更。
- プロジェクトを右クリックして「Maven」→「依存関係管理を使用可能にする」。グループIDは適当に example.cxf、アーティファクトIDは ws-contract。
- pom.xml に以下のように build 要素を追加。
<version>0.0.1-SNAPSHOT</version> <!-- ここから追加 --> <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> <!-- ここまで追加 --> </project>
追加したら、プロジェクトを右クリックして「Maven」→「プロジェクト構成の更新」を行う。
あとは以下のような src/main/java/example/cxf/HelloWorldService.java をつくるだけ(@WebParam がないとWSDLのパラメータ名が arg0 とかになってしまうので一応つける)。
package example.cxf; import javax.jws.WebService; @WebService public interface HelloWorldService { String sayHi(@WebParam(name="text") String text); }
サービス実装のプロジェクトを作成
上で作ったインタフェースを実装するプロジェクト*1。結局普通のサーブレットを使ったWebアプリケーションなので、WTPのプロジェクトとして作成。
- Eclispeで普通に「動的Webプロジェクト」を作成(とりあえず ws-server というプロジェクト名)。ただしソースフォルダは src/main/java と src/main/resources を作り、出力先は target/classes に変更、Webコンテンツフォルダも src/main/webapp に変更。
- プロジェクトを右クリックして「Maven」→「依存関係管理を使用可能にする」。グループIDはさっきと同じ example.cxf、アーティファクトIDは ws-server。
- プロジェクトを右クリックして「プロパティー」→「Javaのビルド・パス」→「順序およびエクスポート」で、「Maven Dependencies」にチェック。
- pom.xml に以下のように依存関係と build 要素を追加。SLF4j+Logbackしか使わないと決めているのでそのための設定もする。
<version>0.0.1-SNAPSHOT</version> <!-- ここから追加 --> <properties> <cxf.version>2.2.5</cxf.version> </properties> <dependencies> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>${cxf.version}</version> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http</artifactId> <version>${cxf.version}</version> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> <!-- さっき作ったのをm2eclipseが勝手に参照してくれる --> <dependency> <groupId>example.cxf</groupId> <artifactId>ws-contract</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.5.8</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.5.8</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>0.9.17</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>0.9.17</version> </dependency> </dependencies> <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> <!-- ここまで追加 --> </project>
追加したら、プロジェクトを右クリックして「Maven」→「プロジェクト構成の更新」を行う。
src/main/resources/logback.xml を仮に以下のように作成。
<?xml version='1.0' encoding ="UTF-8" ?> <!DOCTYPE configuration> <configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <Target>System.out</Target> <layout class="ch.qos.logback.classic.PatternLayout"> <Pattern>%d{HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</Pattern> </layout> </appender> <logger name="org.apache.cxf"> <level value="TRACE" /> </logger> <root> <level value="INFO" /> <appender-ref ref="STDOUT" /> </root> </configuration>
java.util.logging ではなく Log4j(実際はLogback)を使わせるため、src/main/webapp/META-INF を src/main/resources/META-INF に移動させて META-INF/cxf フォルダを作成、さらにその中に org.apache.cxf.Logger という名前のファイルを作成して以下のように記入する(*2)。
org.apache.cxf.common.logging.Log4jLogger
Webサービスの実装 src/main/java/example/cxf/HelloWorldServiceImpl.java を以下のように作成。
package example.cxf; import javax.jws.WebService; @WebService(serviceName="HelloWorld") public class HelloWorldServiceImpl implements HelloWorldService { public String sayHi(String text) { return "Hello " + text; } }
ここでは implements HelloWorldService しているけど、もちろん無くてもいい。
web.xml を以下のように変更。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WSServer" version="2.5"> <display-name>ws-server</display-name> <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> <servlet> <servlet-name>CXFServlet</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CXFServlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
src/main/resources/applicationContext.xml を以下のような内容で作成。全てのメッセージをログに出力するため、cxf:bus に cxf:logging の設定もしている。
<?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-servlet.xml" /> <cxf:bus> <cxf:features> <cxf:logging /> </cxf:features> </cxf:bus> <jaxws:endpoint id="helloWorld" implementor="example.cxf.HelloWorldServiceImpl" address="/HelloWorld" /> </beans>
これでこのプロジェクトを Tomcat 等で動かし、http://localhost:8080/ws-server/ をブラウザで確認して EndPoint や WSDL などの定義が見えるページが現れればOK。
クライアントのプロジェクトを作成
Webサービスを利用するプログラムを作るテスト。
- Eclipseで普通に「Javaプロジェクト」を作成(とりあえず ws-client というプロジェクト名)。ただしソースフォルダは src/main/java、出力先は {プロジェクト名}/target/classes に変更。
- プロジェクトを右クリックして「Maven」→「依存関係管理を使用可能にする」。グループIDは適当に example.cxf、アーティファクトIDは ws-client。
- pom.xml に以下のように依存関係と build 要素を追加。
<version>0.0.1-SNAPSHOT</version> <!-- ここから追加 --> <properties> <cxf.version>2.2.5</cxf.version> </properties> <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> <!-- m2eclipseがさっき作ったのを勝手に参照してくれる --> <dependency> <groupId>example.cxf</groupId> <artifactId>ws-contract</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> <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> <!-- ここまで追加 --> </project>
追加したら、プロジェクトを右クリックして「Maven」→「プロジェクト構成の更新」を行う。
あとは以下のような src/main/java/example/cxf/Main.java をつくる。
package example.cxf; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; public class Main { public static void main(String[] args) { JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); factory.setServiceClass(HelloWorldService.class); factory.setAddress("http://localhost:8080/ws-server/HelloWorld"); HelloWorldService client = (HelloWorldService) factory.create(); System.out.println(client.sayHi("hoge")); } }
Main.java を右クリック→「実行」→「Javaアプリケーションとして実行」を行うと、Eclipseのコンソールに "Hello hoge" と表示される。
とりあえずめでたしめでたし。
"WS-*"関係をいまいち把握しきれていないので時間がかかったけど、やってること自体は簡単だし直感に反することもそんなになかった。
TODO
- 単なる文字列でなくオブジェクトのやりとりをする
- サービス側をDBを使うような本格的なものにする
- クライアント側をWebアプリケーションにする
- クライアント側にユーザ認証を追加し、さらに認証情報をサービス側に受け渡す
道は長い……