先週はCookieを扱うためにJ2SE 5.0でCookieHandlerクラスが導入されたことを紹介しました。また、Java SE 6でCookieHandlerクラスを派生させた実装クラスjava.net.CookieManagerが提供されたことも述べました。
今週は、そのCookieManagerクラスを使って、実際にCookieを扱ってみましょう。
CookieManagerクラスはCookieを保持する際のポリシーをjava.net.CookiePolicyインタフェース、Cookieの保持をjava.net.CookieStoreインタフェースに委譲します。
CookiePolicyインタフェースには以下に示す3つの定数が定義されているので、通常はこれらのいずれかを使用します。
定数 | 説明 |
---|---|
ACCEPT_ALL | すべてのCookieを受け入れる |
ACCEPT_NONE | Cookieをまったく受け入れない |
ACCEPT_ORIGINAL_SERVER | 元のサーバからのCookieのみを受け入れる |
CookieManagerクラスはデフォルトでACCEPT_ORIGINAL_SERVERを使用します。
また、CookieStoreインタフェースはデフォルトではメモリ内にCookieを保持するsun.net.www.protocol.http.InMemoryCookieStoreクラスが使用されます。
もし、ファイルなどにCookieをシリアライズしたい場合は、CookieStoreインタフェースを実装したクラスを自作する必要があります。
CookieStoreインタフェースはjava.net.HttpCookieクラスを使用してCookieを保持します。
この関係をUMLで示したのが、図1です。
![]() |
図1 Cookie関連のクラス図 |
---|
それでは、サンプルでCookiManagerクラスの動作を確かめてみましょう。
ここではサーブレットでサーバを、Java SEでクライアントを作成しました。
サンプルのソース TestServlet.java, CookieManagerSample.java
サーブレットはCookieをセットするだけの単純なものです。
public class TestServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); Cookie cookie = new Cookie("user", "sakuraba"); cookie.setDomain(".local"); cookie.setPath("/cookietest/TestServlet"); response.addCookie(cookie); cookie = new Cookie("id", "123456789"); cookie.setDomain(".local"); cookie.setPath("/cookietest/TestServlet"); response.addCookie(cookie); PrintWriter out = response.getWriter(); out.println("<html><body><p>Cookie Test</p></body></html>"); out.close(); } }
次にクライアントです。
CookieManagerオブジェクトを生成したら、CookieHandlerクラスのsetDefaultメソッドをコールしてデフォルトの設定をします。デフォルト設定を行なうことでHttpURLConnectionクラスがCookieManagerクラスを使用できるようになることは、先週解説したとおりです。
// CookieManager の生成 CookieManager manager = new CookieManager(); // CookieHandler のデフォルトに設定 CookieHandler.setDefault(manager);
CookieManagerクラスのデフォルトコンストラクタでオブジェクトを生成すると、前述したようにCookiePolicyオブジェクトにACCEPT_ORIGINAL_SERVER、CookieStoreオブジェクトにInMemoryCookieStoreクラスを使用します。
コンストラクタの引数でCookiePolicyオブジェクトと、CookieStoreオブジェクトを指定することも可能です。また、CookiePolicyオブジェクトはsetCookiePolicyメソッドでも設定することができます。
たとえば、ACCEPT_ALLにしたい場合は次のようにします。
// CookieManager の生成
CookieManager manager = new CookieManager();
// すべての Cookie を受けいれる
manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
// CookieHandler のデフォルトに設定
CookieHandler.setDefault(manager);
Cookieの受けいれや、送信はHttpURLConnectionクラスが行ないます。したがって、Cookieの受け入れ、送信を行なうための記述をする必要はありません。
後は、必要に応じて、CookieStoreオブジェクトからCookieを取得、設定します。
CookieManagerSampleクラスではHttpURLConnectionクラスでサーバと通信した後、受け入れたCookieを表示しています。
public CookieManagerSample() { // CookieManager の生成 CookieManager manager = new CookieManager(); // CookieHandler のデフォルトに設定 CookieHandler.setDefault(manager); // サーバに接続 try { URL url = new URL( "http://localhost:8084/cookietest/TestServlet"); HttpURLConnection http = (HttpURLConnection)url.openConnection(); int response = http.getResponseCode(); System.out.println("Response: " + response); } catch (IOException ex) { // 例外処理 } // Cookie の表示 CookieStore store = manager.getCookieStore(); List<HttpCookie> cookies = store.getCookies(); for (int i = 0; i < cookies.size(); i++) { HttpCookie cookie = cookies.get(i); System.out.println("Cookie[" + i + "]: " + cookie); } }
CookieManagerオブジェクトからCookieStoreオブジェクトを取得するには、赤字で示したようにgetCookieStoreメソッドを使用します。CookieStoreオブジェクトからは、青字で示したようにgetCookiesメソッドでCookie、つまりHttpCookieオブジェクトを取得できます。
ここではCookieStoreオブジェクトが保持しているすべてのCookieを表示しています。
それでは、CookieManagerSampleを実行してみます。サーバは上記のソースにも記述されているように、localhostの8084番ポートで動作しており、サーブレットはcookietest/TestServletに配置しました。
CookieManagerSampleを実行した結果を以下に示します。
C:\>java CookieManagerSample Response: 200 Cookie[0]: id=123456789 Cookie[1]: user=sakuraba
正しくCookieが取得できていることが確認できます。
ここで、サーブレットでCookieの有効期限を変更することにより、Cookieの受け入れが変化するか試してみます。
サーブレットでCookieの有効期限はCookie#setMaxAgeメソッドで設定します。引数は秒数です。引数がマイナスの場合クライアントが終了するまで、また0の場合は保存されません。デフォルトでは-1が使用されます。
ここでは、setMaxAgeの引数を0にしてみます。
Cookie cookie = new Cookie("user", "sakuraba"); cookie.setMaxAge(0); response.addCookie(cookie);
実行結果は次のようになりました。
C:\>java CookieManagerSample Response: 200 Cookie[0]: id=123456789
userが保持されていないことが確認できます。
Cookieの更新
![]() |
図2 Cookieを利用したカウンタ |
---|
Cookieを取得することができたので、次はクライアントからCookieを送信してみましょう。
サーバから取得したCookieを変更することなく、再びサーバに送信するのであれば、CookieManagerクラスがすべて行なってくれます。せっかくですから、Cookieを変更させてみましょう。
ここでは、カウンタをCookieを利用して作ってみます。サンプルを実行するとカウンタを表示するラベルと、カウンタを増加させるボタンが表示されます(図2)。カウンタはCookieで表すことにしました。
ボタンをクリックすると、カウンタの値を増加させます。そして、新しい値を保持したCookieを要求ヘッダに付加してサーバに送信します。サーバは受けとったCookieをそのまま応答ヘッダに付加してクライアントに戻します。
サンプルのソース TestServlet2.java, CookieManagerSample2.java
サーブレットのソースを以下に示します。
public class TestServlet2 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); Cookie[] cookies = request.getCookies(); boolean flag = false; if (cookies != null) { for (Cookie cookie: cookies) { response.addCookie(cookie); flag = true; } } if (!flag) { Cookie cookie = new Cookie("count", "0"); cookie.setDomain(".local"); cookie.setPath("/cookietest/TestServlet2"); response.addCookie(cookie); } PrintWriter out = response.getWriter(); out.println("<html><body><h1>Cookie Test</h1></body></html>"); out.close(); } }
次にクライアントです。GUIを構築する部分は本題には関係ないので、省略します。
ボタンをクリックされた時の処理を以下に示します。
JButton button = new JButton("Increase Count"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { // カウンタを増加させる increaseCounter(); // 通信を行い、値を更新する update(); } });
カウンタを増加させているのがincreaeCounterメソッド、通信を行なって表示の更新をおこなうのがupdateメソッドです。
increaseCounterメソッドを次に示します。
private void increaseCounter() { // Cookie の値を増加させる CookieStore store = manager.getCookieStore(); List<HttpCookie> cookies = store.getCookies(); for (HttpCookie cookie: cookies) { if (cookie.getName().equals("count")) { String countText = counter.getText(); int count = Integer.parseInt(countText); count++; cookie.setValue(String.valueOf(count)); } } }
CookieStoreオブジェクトは前述したとおり、CookieManager#getCookieStoreメソッドで取得できます。
後は、Cookieの中でcountという名前を持つものがあれば、値を取りだし、増加させます。
次にupdateメソッドです。
private void update() { // 通信は非同期に行なうため、SwingWorkerを使用する SwingWorker worker = new SwingWorker<Object, Object>() { @Override public Object doInBackground() { // 通信処理 communicate(); return null; } @Override protected void done() { // 表示を更新 updateCounter(); } }; worker.execute(); }
通信には時間がかかるため、SwingWorkerクラスを使用して非同期に行ないます。SwingWorkerクラスに関してはSwingでマルチスレッド - SwingWorker その1、その2をご覧ください。
通信を行なっているのがcommunicateメソッド、表示の更新がupdateCounterメソッドです。communicateメソッドはHttpURLConnectionクラスを使用して通信を行なっているだけです。
updateCounterメソッドはincreaseCounterメソッドと同じような処理を行ないます。
private void updateCounter() { // Cookie の値をラベルに表示する CookieStore store = manager.getCookieStore(); List<HttpCookie> cookies = store.getCookies(); for (HttpCookie cookie: cookies) { if (cookie.getName().equals("count")) { String count = cookie.getValue(); counter.setText(count); return; } } }
変数counterはカウンタを表示するJLabelオブジェクトを表しています。
![]() |
図3 実行結果 |
---|
それでは、さっそく実行してみましょう。
ボタンがクリックされると、カウンタの値も更新されることが確認できるはずです。図3は5回クリックした後の結果を示しています。
今週はCookieManagerクラスがデフォルトで使用するInMemoryCookieStoreクラスを使ってCookieを保持させました。
しかし、メモリ内にCookieを保持するため、アプリケーションを終了させてしまうと、Cookieも消滅してしまいます。
そこで、来週はCookieStoreインタフェースを実装するクラスを作成して、Cookieをファイルにシリアライズしてみます。お楽しみに。
著者紹介 櫻庭祐一 横河電機 ネットワーク開発センタ所属。Java in the Box 主筆 今月の櫻庭ここ数年、夏になると沖縄にいっています。去年までは雨に降られたことがなかったのですが、今年はとうとう台風に遭遇してしまいました。 沖縄のJavaコミュニティであるJava Kücheの一周年記念講演会で講演するため、先月の12日に沖縄に向かいました。ところが、同じく沖縄に来ていたのが台風4号。せっかくの、講演会も中止の憂き目にあってしまいました。 それでも、懇親会だけは開かれました。居酒屋にプロジェクターを持ち込み、講演の内容についてざっくばらんにお話しすることができました。 それにしても、沖縄の台風はすごいとは聞いていましたが、本当にすごかったです。 泊まっていたホテルは建物全体が風で揺れ、窓のサッシからも雨がしみこんできます。倒れているヤシの木も多数。 沖縄でも、今回のような大型台風の直撃は久しぶりだそうです。 なお、中止になってしまった講演会ですが、 仕切り直して8/8に講演会が行なわれました。残念ながら櫻庭は参加できなかったのですが、盛況であったようです。 |