このたび職場を移ることにしました。
お世話になっている皆様には本当に感謝しております。
これからも宜しくお願いいたします。
http://www.youtube.com/watch?v=2w12u_B7mCo
このたび職場を移ることにしました。
お世話になっている皆様には本当に感謝しております。
これからも宜しくお願いいたします。
http://www.youtube.com/watch?v=2w12u_B7mCo
JavaオブジェクトからXMLへ変換したい、またはXMLからJavaオブジェクトへ変換したい、ということはよくあるものです。
方法はいくつか考えられますが、JavaSE6 にある標準技術 JAXB (Java Architecture for XML Binding) では、この変換を手軽に実装できるようになっています。その使い方を解説しようと思います。
まずはJAXBの考え方を俯瞰しておきます。
図のように、JavaクラスとXML Schemaとが対応するものと考えます。言われてみればもっともな話で、実体としてJavaオブジェクト・XML文書があり、その雛形としてJavaクラス・XML Schemaがあるということですね。
ここから分かる通り、JAXBの出発点はJavaクラスかXML Schemaになります。どちらから始めても実装可能です。
まずはごくごく単純なデータ構造として「会社」と「従業員」を使うことにし、
会社(従業員*)
従業員(名前,給料)
とします。ありがちですいません。これをJavaクラスにするとおそらく以下のようになるでしょう。何ということはない、ごく普通のJavaクラスです。
Companyクラス
public class Company { private List<Employee> employees; public List<Employee> getEmployees() { return employees; } public void setEmployees(List<Employee> employees) { this.employees = employees; } }
Employeeクラス
public class Employee { private String name; private int salary; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getSalary() { return salary; } public void setSalary(int salary) { this.salary = salary; } }
次に、このクラスのオブジェクトをXMLに変換するプログラムがどうなるか見てみます。
※ なお、JAXB関連のクラスは javax.xml.bind 以下のパッケージに入っています。
public class Sample1 {
public static void main(String[] args) {
// 会社
Company c = new Company();
// 2人の従業員
List<Employee> employees = new ArrayList<Employee>();
Employee e1 = new Employee();
e1.setName("taro");
e1.setSalary(1000);
employees.add(e1);
Employee e2 = new Employee();
e2.setName("hanako");
e2.setSalary(800);
employees.add(e2);
c.setEmployees(employees);
// マーシャル
JAXB.marshal(c, System.out);
}
}
特別なことは何もしていません。ただJAXB#marshal()を呼んでいるだけです。
これを実行すると、以下のような出力が得られるはずです。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <company> <employees> <name>taro</name> <salary>1000</salary> </employees> <employees> <name>hanako</name> <salary>800</salary> </employees> </company>
これで一応JavaオブジェクトからXMLへの変換はできたことになります。
出力されたXMLをよく見ると従業員の要素名が「employees」になっています。これは少し嫌なので調整したいですね。そのためにはアノテーションを使ってバインディングを変更する必要があります。
また、JavaクラスとXML Schemaとが対応すると言っておきながら、XML Schemaが一度も登場していません。このあたりを併せて次回に解説しようと思います。
GoogleのJavaユーティリティライブラリで、Apache Commons の Lang、Collectionsなどに替わる機能を提供してくれるものです。コンパクトながら、かゆいところに手が届く良質なAPIになっていると思います。
いくつかの機能を紹介します。
List<A>を基にしてList<B>を作るとします。
ライブラリを使わない場合はこんな感じになるでしょう。
List<B> blist = new ArrayList<B>(alist.size()); for (A a : alist) { blist.add(new B(a)); }
Guavaを使う場合は次のように書きます。
List<B> blist = Lists.transform(alist, new Function<A, B>(){ @Override public B apply(A a) { return new B(a); } });
おや? どこかで見たような・・・。そう、Commons CollectionsのListUtilsと似ています。が、あちらはジェネリクスを使わないので、ダサいキャストが必要になります。
このリスト変換の利点は、要素を使う時(リストから要素を取り出そうとした時)に初めて変換が行われるところにあります。
ちなみにキャメルケースとは、単語の頭文字を大文字にして結合した文字列の形式(someString)、スネークケースとは、単語間をアンダースコアで結合した文字列の形式(some_string)です。
例えばDBを扱うときに多いですね。Java内ではキャメルケース・DB内ではスネークケースを使ってる場合、変換が必要になることがありますが、自前で実装するのは結構大変で面倒です。こんな場面ではCaseFormatというenumを使います。
キャメルケースからスネークケースへの変換
String str = LOWER_CAMEL.to(LOWER_UNDERSCORE, "someString"); assert str == "some_string";
スネークケースからキャメルケースへの変換
String str = LOWER_UNDERSCORE.to(LOWER_CAMEL, "some_string"); assert str == "someString";
Joinerというクラスを使います。
List<String> list = Arrays.asList("aaa", "bbb", "ccc"); String str = Joiner.on(", ").join(list); assert str == "aaa, bbb, ccc";
セントラルリポジトリに登録されていますので、Mavenからの使用も可能です。
dependencyの記述は以下の通りです。(現時点での最新バージョンはr07)
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>r07</version> </dependency>
SHOW SLAVE STATUSG でスレーブの状態をチェックして、問題がありそうな場合はメールを送信するスクリプトのメモです。
意図としては、
ちなみにMySQLそのものに接続できないとかの場合はこのスクリプトでは対応できません。
#! /bin/sh # # watch replication # ALERT_TO="{mail-destination}"; ALERT_SUBJECT="[MySQL] replication alert"; CHECK_COMMAND="SHOW SLAVE STATUS\G"; ALLOW_DELAY_SECONDS=30; echo "$CHECK_COMMAND"; slave_status=`mysql -u {user} -e "$CHECK_COMMAND"`; echo "$slave_status"; # set variables from status eval "`echo "$slave_status" | sed -ne 's/: (.*)/="1"/p'`"; if [ "$Slave_IO_Running" != "Yes" ]; then msg="Slave_IO_Running : $Slave_IO_Runningn"; fi if [ "$Slave_SQL_Running" != "Yes" ]; then msg="${msg}Slave_SQL_Running : $Slave_SQL_Runningn"; fi if [ "$Last_Errno" != "0" ]; then msg="${msg}Last_Error : [${Last_Errno}] $Last_Errorn"; fi if [ "$Seconds_Behind_Master" -gt $ALLOW_DELAY_SECONDS ]; then msg="${msg}Seconds_Behind_Master : $Seconds_Behind_Mastern"; fi if [ "$msg" != "" ]; then echo "n-- Need alert mail --"; echo "$msg"; (echo "${msg}n${CHECK_COMMAND}n${slave_status}" | mailx -s "$ALERT_SUBJECT" "$ALERT_TO"); fi
標準のAPIを使ってプログラムを書いておくと色々と都合がいいので、RESTfulなWebサービスを作成しようとするときはJAX-RSを使いたいものです。
JavaEE6には初めから入っているJAX-RSですが、現場ではTomcat5.5(もちろんJava5です)とかが頑張っているのでそのままでは使えません。Apache Tomcat 5.5 と、JAX-RSのリファレンス実装である Jersey 1.4 を使って、単純なRESTサービスを実装する手順を紹介します。
まずはJersey1.4のインストールから。Maven2を使っている場合は、java.netのリポジトリを設定します。
(pom.xml に以下を追加) <repositories> <repository> <id>maven2-repository.dev.java.net</id> <name>Java.net Repository for Maven</name> <url>http://download.java.net/maven/2/</url> <layout>default</layout> </repository> </repositories>
サーバアプリケーション側のライブラリは以下です。
(pom.xml に以下を追加) <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-server</artifactId> <version>1.4</version> </dependency>
jarをそのまま使いたい場合は、 http://jersey.java.net/ のDownloadからライブラリをダウンロードしてクラスパスに含めましょう。
入門にはありがちですが、まずは単純に"Hello World!"という文字列を返すだけのRESTサービスを構築してみます。以下のクラスを作成してください。
jerseytest.resources.HelloworldResource
package jerseytest.resources; import javax.ws.rs.GET; import javax.ws.rs.Path; @Path("/helloworld") public class HelloworldResource { @GET public String getMessage() { return "Hello World!n"; } }
サービスの記述はこれだけです。アノテーションについては後で解説することにしましょう。
次にweb.xmlにサーブレットの設定をします。
<servlet> <servlet-name>JAX-RS Servlet</servlet-name> <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> <init-param> <param-name>com.sun.jersey.config.property.packages</param-name> <param-value>jerseytest.resources</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>JAX-RS Servlet</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping>
jerseyに含まれているサーブレットクラスを指定して、初期化パラメータとしてサービスを記述したクラスのパッケージ名を与えるだけです。
それではアプリケーションをTomcatに配備して、サービスを参照してみましょう。
$ curl "http://localhost:8080/jerseytest/rest/helloworld" Hello World!
おお!ちゃんど文字列が返ってきましたね!
RESTサービスでは、”サービスはURLで指定されるweb上のリソースである”と考えます。それで先ほどのHelloworldResourceクラスにもResourceと名付けていました。
そのリソースクラスで使用した2つのアノテーションを説明します。
RESTサービスでは、サービスを提供するために必要な引数をURLのクエリパラメータとして受け取ることがあります。クエリパラメータを受け取るリソースクラスを作成してみましょう。
jerseytest.resources.SayHelloResource
package jerseytest.resources; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; @Path("/sayhello") public class SayHelloResource { @GET public String say(@QueryParam("name") String name) { return String.format("Hello %s!%n", name); } }
では実際にサービスを参照してみます。
$ curl "http://localhost:8080/jerseytest/rest/sayhello?name=George" Hello George!
確かにクエリパラメータを受け取ることができていますね。
引数をURLのパスに含めたいこともあるかもしれません。その場合には次のようなコードにします。
jerseytest.resources.SayHelloResource2
package jerseytest.resources; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; @Path("/sayhello2/{name}") public class SayHelloResource2 { @PathParam("name") private String name; @GET public String say() { return String.format("Hello %s!%n", name); } }
今度はプロパティにセットするようにしました。また、@Pathの指定方法にも注目してください。
サービスを参照すると以下のようになります。
$ curl "http://localhost:8080/jerseytest/rest/sayhello2/emily" Hello emily!
新たにパラメータを扱うアノテーションが2つ登場しました。
ここまでで、サービスの記述とURLとのマッピング、そしてパラメータの取り出しなどが簡単にできることがお分かり頂けたと思います。
ただ、これまでのところ単純な文字列を返すサービスを構築しただけで、まったく現実的なサービスを提供できたとは言えません。次回はXMLやJSONを返すサービスを構築する手順を紹介しようと思います。