環境ごとまるっと提供とは
webアプリ(特にJava)構築の勉強を始めようとした場合、書籍やサイトを参考にしながら実際にやってみるというのは、非常に有効な手段です。
一方で、参考にした情報だけではうまく動作させることができないケースが多い気がします(たとえば前提としてeclipseのtomcatプラグインが必要だとかで)。
そういった前提条件を調べているうちに、よくわからないエラーにハマり、結局挫折してしまうなんてことも多いのではないでしょうか??もちろんそういうエラーを解決するのは、トラブルシューティングなども見据えると非常に力になって良いことだと思うのですが、挫折してしまって何もしなくなるのは本末転倒です。
各個人に芽生えた学習意欲が、「動かないから断念」という理由で消えてしまうのは非常にもったいないことだと思います。そういった意欲がなくなってしまうことを少しでも防げるように、本稿では、Vagrantを利用した環境構築スクリプトとセットで紹介し、動かしながら学習することまでサポートしたいと思います。
※VirtualBox&Vagrantの環境をセットアップしてあることが前提になってしまうのですが、それらのインストールは、非常に簡単ですので、「動かないから断念」問題になる可能性が低いです。
Jersey2とは
javaでRestfulなwebサービスを構築しようとした場合、ほとんどのケースでJAX-RS(Java API for RESTful Web Services)を利用することになります。ただし、JAX-RSはあくまでも仕様でしかないため、その仕様を実装を利用する必要があります。Jerseyというのは、JAX-RSのリファレンス実装のことです。Jersey2というのはJerseyのversion2系のことです(Stack Over Flowなどでは、ちょくちょく見かける表記ですが、公式な呼び方じゃないかもしれません。。。)。
他には、Apache CXFやRest Easyなどの実装があります。
ちなみにですが、JAX-RSは、そこまで大きな仕様ではありません。JAX-RSの仕様からたどることができますが、appendixを除けば全部で60P程度にまとまっています。英語ですが、仕様がよくわからなかったら参照してみるとよいでしょう。
Jersey2のサンプルプログラム紹介
mavenを使って構成管理をしているのですが、その中心核となるpom.xmlについての詳細は割愛します。github:jersey2-sampleにコミットしてりますので、気になる方はそちらをご参照ください。
また、Jersey2の核となる「org.glassfish.jersey.servlet.ServletContainer」の設定をweb.xmlにしているのですが、その書き方などが気になる場合についてもコミットしてあるweb.xmlを参照していただければと思います。
それでは、実際にJersey2を使っている部分を見てみましょう。
JAX-RSでは、「あるURLにリクエストが来た場合」に、「そのHTTPメソッドに応じた処理」を実行する動きをします。
その実行の際に、様々な形でリクエストの情報をプログラム側で利用することが可能です。
たとえば、「URLのパターンにマッチした値を利用する」「クエリパラメータを利用する」「POSTパラメータを利用する」「Cookieを利用する」などなどです。
一つ一つを詳細に、説明することもできるのですが、本稿としては実際に環境ごと提供して動かしてもらい、全然実装していないのにどうなっているんだろう??と興味を持ち、学習意欲を刺激するのが目的ですので敢えて、詳しい説明はいたしません。プログラムを実行するためのURLをコメントで記載しておきましたので、後述する環境構築後に実際にブラウザからアクセスして、あれこれ考えてみましょう。Sevletの知識がある程度ある人なら、けっこう感動できると思いますよ??
package yukimura.sample.rest.jersey2.spec;
import javax.ws.rs.CookieParam;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.MatrixParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Cookie;
import yukimura.sample.rest.jersey2.jaxbean.SampleBean;
@Path("/sample")
public interface HelloWorldService {
// ex) http://192.168.55.10:8080/jersey2-sample/rest/sample/hello/HELLO
@GET
@Path("/hello/{message}")
public String sayHello(@PathParam("message") String message);
// ex) http://192.168.55.10:8080/jersey2-sample/rest/sample/path_param/4+7/
@GET
@Path("/path_param/{op1}+{op2}/")
public String pathParamSample(@PathParam("op1") int op1, @PathParam("op2") int op2);
// ex) http://192.168.55.10:8080/jersey2-sample/rest/sample/matrix_param/HOGE;attr=fuga
@GET
@Path("/matrix_param/{message}")
public String matrixParamSample(@PathParam("message") String message, @MatrixParam("attr") String attr);
// ex) http://192.168.55.10:8080/jersey2-sample/rest/sample/query_param/HOGE?param1=hoge
@GET
@Path("/query_param/{message}")
public String queryParamSample(@PathParam("message") String message, @QueryParam("param1") String param1);
// ex) http://192.168.55.10:8080/jersey2-sample/post_test.jsp
@POST
@Path("/form_param/")
public String formParamSample(@FormParam("text1") String text);
// ex) http://192.168.55.10:8080/jersey2-sample/rest/sample/header_param/
@GET
@Path("/header_param/")
public String headerParamSample(@HeaderParam("User-Agent") String userAgent);
// ex) http://192.168.55.10:8080/jersey2-sample/rest/sample/cookie_param/
@GET
@Path("/cookie_param/")
public String cookieParamSample(@CookieParam("JSESSIONID") Cookie jsessionId);
// ex) http://192.168.55.10:8080/jersey2-sample/rest/sample/test/xml
@GET
@Path("/test/xml")
@Produces("application/xml")
public SampleBean getXml();
// ex) http://192.168.55.10:8080/jersey2-sample/rest/sample/test/json
@GET
@Path("/test/json")
@Produces("application/json")
public SampleBean getJson();
}
上記のようなinterfaceを作成することで、URLとメソッドがバインドされることになります。
実装がどうなるかを見てみましょう↓↓
package yukimura.sample.rest.jersey2.impl;
import javax.ws.rs.core.Cookie;
import org.springframework.stereotype.Service;
import yukimura.sample.rest.jersey2.jaxbean.SampleBean;
import yukimura.sample.rest.jersey2.spec.HelloWorldService;
@Service
public class HelloWorldServiceImpl implements HelloWorldService {
@Override
public String sayHello(String message) {
return String.format("Hello %s!!", message);
}
@Override
public String pathParamSample(int op1, int op2) {
int result = op1 + op2;
return Integer.toString(result);
}
@Override
public String matrixParamSample(String message, String attr) {
return "message:" + message + " attr:" + attr;
}
@Override
public String queryParamSample(String message, String param1) {
return "message:" + message + " param1:" + param1;
}
@Override
public String formParamSample(String text) {
return text;
}
@Override
public String headerParamSample(String userAgent) {
return userAgent;
}
@Override
public String cookieParamSample(Cookie jsessionId) {
if( jsessionId == null ) {
return "JSESSIONIDが取得できませんでした。";
}
return jsessionId.toString();
}
@Override
public SampleBean getXml() {
return new SampleBean("HOGEHOGE", "FUGAFUGA");
}
@Override
public SampleBean getJson() {
return new SampleBean("HOGEHOGE", "FUGAFUGA");
}
}
package yukimura.sample.rest.jersey2.jaxbean;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="root")
public class SampleBean {
public SampleBean() {}
public SampleBean(String str1, String str2) {
this.str1 = str1;
this.str2 = str2;
}
@XmlElement
private String str1;
public String getStr1() {
return str1;
}
public void setStr1(String str1) {
this.str1 = str1;
}
@XmlElement
private String str2;
public String getStr2() {
return str2;
}
public void setStr2(String str2) {
this.str2 = str2;
}
}
どうでしょう。値をxmlで返却する例を示したかったため、SampleBeanがJAXBというXMLライブラリに依存しているということはあるのですが、それ以外はPOJOで表現できていることがわかるかと思います。シンプルでJUnitなどによるテストも容易に実現できそうです。
たったこれだけで、基本的なRestアプリを実現できそうな気がしてきませんか??
この紹介で留めてしまうと、上っ面はわかって、実際に作ってみようとしたけど動かない。。。なんて人が出てきてしまうので、そこをフォローアップします。
環境構築用のVagrantfile
お手元の環境に、Vagrantがインストールされていれば、以下のVagrantfileとshell群を配備して、vagrant upしていただければ、自動的に環境が構築されます。(各種ミドルウェアを自動的にインストールしたうえで、jersey2のサンプルプログラムをgit cloneで取得して、コンパイルしてデプロイするところまで自動で実施します。)
下記のファイルをコピペした上でvagrant upを実行し、ブラウザからアクセスしてみてください。linuxでも、Macでも、Windowsでも動くはず!!
Vagrant::Config.run do |config|
config.vm.box = "centos64_6_4"
config.vm.box_url = "file://./box/centos-64-x64-vbox4210.box"
config.vm.define "web" do |web|
web.vm.network "hostonly", "192.168.55.10"
web.vm.provision "shell", path: "setup_jdk.sh"
web.vm.provision "shell", path: "setup_tomcat7.sh"
web.vm.provision "shell", path: "setup_maven.sh"
web.vm.provision "shell", path: "setup_git.sh"
web.vm.provision "shell", path: "setup_jersey2_sample.sh"
end
end
# !/bin/bash
BASE_DIR="/tmp"
SAMPLE_PROJECT_NAME="jersey2-sample"
GIT_URL="https://github.com/yukimura1227/${SAMPLE_PROJECT_NAME}.git"
TOMCAT_WEBAPP_DIR="/usr/local/tomcat7/webapps"
cd ${BASE_DIR}
echo "get jersey2-sample..."
git clone ${GIT_URL}
echo "build by maven..."
cd ${BASE_DIR}/${SAMPLE_PROJECT_NAME}
mvn install
echo "deploy war..."
cp -p ${BASE_DIR}/${SAMPLE_PROJECT_NAME}/target/*.war ${TOMCAT_WEBAPP_DIR}
sudo service tomcat restart
"setup_jdk.sh"→Vagrant Tips#2 Oracleのjdkをインストールさせるをご参照ください
"setup_tomcat7.sh"→Vagrant Tips#6 JavaのWebアプリ実行用にTomcat7環境をセットアップするをご参照ください
"setup_maven.sh"→Vagrant Tips#7 Javaのビルド環境としてmaven3の環境をセットアップするをご参照ください。
"setup_git.sh"→Vagrant Tips#8 gitをソースからコンパイルして利用する(centos)をご参照ください。
どうでしょう?動きましたか??
環境も一緒に提供することで、皆様の学習のサポートになれれば幸いです。