WEB-INF内のファイルをBeanからアクセスする
今度は、サーブレットとJSPに、さらに「JavaBeans」を加えて、相互のやり取りの方法を考えてみることにしましょう。先ほどの実験で、ServletContextを利用するとWebアプリケーション内に常に存在する値を保持できることができることが分かりました。これはセッションごとに用意されるのではなく、Webアプリケーションに付き1つしか存在しません。すなわち、このWebアプリケーションにアクセスしたすべてのクライアントは、同一の値を取得することになります。
これを利用することで、Webアプリケーション内に常時起動し続けるJavaBeansオブジェクトを用意し、それをJSP/サーブレットから利用できるようになります。これは、考えてみるとなかなか便利そうです。
例として、サーバに保管されているファイルを読み込んだテキストを管理するBeanクラスを作成してみましょう。そして、このBeanをサーブレットから起動し、サーブレットやJSPからアクセスして利用してみます。
まず、データを保管するテキストファイルを用意しましょう。WTPでプロジェクトの「WebContent」内にある「WEB-INF」フォルダを選択し、[ファイル]-[新規]-[ファイル]メニューを選びます。現れた画面で親フォルダに「WEB-INF」が設定されているのを確認し、ファイル名に「data.txt」と入力して終了します。これで、WEB-INF内に「data.txt」ファイルが作成されます。ファイルには、サンプルとして適当にテキストを書いておきましょう。
次に、Beanクラスの作成です。[ファイル]-[新規]-[その他]メニューで新規作成のウインドウを呼び出し、「クラス」を選んで新規作成します。ここでは、クラスは次のように設定しておけばいいでしょう。
- パッケージ: jp.codezine
- 名前: MyDataModel
作成された「MyDataModel.java」を開き、ソースコードを記述します。ここでは、テキストファイルの読み書きと、保持するテキストデータのGetter/Setterを持ったBeanクラスを作成することにします。
package jp.codezine; import java.io.*; public class MyDataModel { private String fname = null; private String data = null; public MyDataModel(){ this("data.txt"); } public MyDataModel(String s){ this.fname = s; this.loadData(); } public String getData(){ return this.data; } public void setData(String s){ this.data = s; } public void loadData(){ FileReader reader = null; BufferedReader breader = null; try { reader = new FileReader(this.fname); breader = new BufferedReader(reader); String tmp = ""; String result = ""; while((tmp = breader.readLine()) != null){ result += tmp + "\n"; } this.data = result; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { breader.close(); } catch (IOException e) { e.printStackTrace(); } } } public void saveData(){ FileWriter writer = null; BufferedWriter bwriter = null; try { writer = new FileWriter(this.fname); bwriter = new BufferedWriter(writer); bwriter.write(this.data); bwriter.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { bwriter.close(); } catch (IOException e) { e.printStackTrace(); } } } }
ここでは、コンストラクタを2種類用意してあります。引数に読み込むファイルのパスを指定したものと、引数なしのデフォルトコンストラクタです。引数なしの場合は、初期値として読み込むファイルに"data.txt"を設定しておくことにしました。ファイル名はインスタンス作成時以外には変更できず、読み込んだテキストは外部から変更したり、その変更を保存したりできるようにしてあります。
サーブレットからのBeanの利用
では、このBeanクラスをサーブレットから起動し利用してみることにしましょう。MyServletを修正して、アクセス時にBeanがなければ作成し、GETされたらBeanのデータを送り返すようにしてみます。
package jp.codezine; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class MyServlet extends HttpServlet implements Servlet { private static final long serialVersionUID = 1L; public MyServlet() { super(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("Shift_JIS"); response.setContentType("text/html;charset=Shift_JIS"); this.checkBean(); PrintWriter out = response.getWriter(); ServletContext context = this.getServletContext(); MyDataModel datamodel = (MyDataModel)context.getAttribute("datamodel"); out.println(datamodel.getData()); out.flush(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("Shift_JIS"); response.setContentType("text/html;charset=Shift_JIS"); this.checkBean(); } public void checkBean(){ ServletContext context = this.getServletContext(); if (context.getAttribute("datamodel") == null){ String path = context.getRealPath("WEB-INF/data.txt"); context.setAttribute("datamodel", new MyDataModel(path)); } } }
ここでは、chckBeanメソッドでBeanがあるかどうかチェックし、なければ新たにインスタンスを作成しています。ここでは、ServletContextに"datamodel"という名前のアトリビュートとしてMyDataModelを保管していますので、この値がnullであれば新たにインスタンスを作成しsetAttributeしています。
ここでは、インスタンスを作成する際に、読み込むファイルのパスを渡していますが、このようにしていることが分かりますね。
String path = context.getRealPath("WEB-INF/data.txt");
ServletContextの「getRealPath」は、引数に指定した相対パスを絶対パスに変換したStringを返します。サーブレットやJSPは、あくまでJavaサーバにアップされ公開されますから、それがサーバのどの場所に配置されるかは分かりません。そこで、Webアプリケーション内のファイルの相対パスを絶対パスに変換するための道具が用意されているわけです。
インスタンスの作成さえできれば、後の利用は簡単です。ServletContextのgetAttributeでMyDataModelインスタンスを取得し、そのgetDataを呼び出してテキストを取り出せばいいだけです。
なお、今回はBeanクラスを使ってファイルアクセスをしましたが、これは「JSP/サーブレットからはファイルアクセスができない」ということではありません。サーブレットなどでも、まったく同様のやり方でサーバ内のファイルにアクセスすることができます。
JSPからのBeanの利用
では、今度はJSPからBeanを利用してみることにしましょう。適当なJSPファイルを開き、次のように修正してみましょう。
<%@ page language="java" contentType="text/html; charset=Shift_JIS"
pageEncoding="Shift_JIS"%>
<%@ page import="jp.codezine.MyDataModel" %>
<%
MyDataModel datamodel
= (MyDataModel)application.getAttribute("datamodel");
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; Shift_JIS">
<title>PAGE</title>
</head>
<body>
※データの表示<br>
<textarea cols="20" rows="5"><%=datamodel.getData() %>
</textarea>
</body>
</html>

これは、Beanからテキストを取り出して<textarea>に書き出すものです。Beanの初期化処理は省略しておきました。
JSPからBeanを利用する場合、注意した起きたいのは「必ずクラスをimportしておく」という点です。一般的なJavaのコーディングでは、同じパッケージに作成してあるクラスは、わざわざimport文を書かなくともそのまま利用することができます。が、JSPで他のクラスを利用する場合、例え同じパッケージ内にあるクラスであってもimportを宣言しておく必要があります。
<%@ page import="jp.codezine.MyDataModel" %>
この部分です。JSPでは、<%@ page %>タグでimportを指定します。これでJSP内からMyDataModelクラスが利用可能になります。importさえできていれば、後は普通のJavaのソースコードとまったく変わりません。
まとめ
今回は、リダイレクト、インクルード、各種のアトリビュートによる値の受け渡し、Webアプリケーションに保管したBeanとのやりとり、といったことについて説明を行いました。サーブレットの使い方を覚えるというより、JSPとサーブレットを連携しながら処理を行うという手法に慣れることを目標にして考えてください。
サーブレットはサーバサイドJavaの中心となる技術です。しかし、サーブレットだけで何かを作成することはないでしょう。常に、フロントエンドたるJSPと組み合わせて処理を作成することになるはずです。サーブレットは、それ単体の機能だけでなく、いかにしてJSPと連携して使いこなせるか、が重要になるでしょう。
また、ServletContextを使ったBeanの利用は、ある程度大きなWebアプリケーションを作成するようになると非常に役に立つことが分かるはずです。サーバサイドの世界では、より本格的なサーバ開発のために「EJB(Enterprise Java Beans)」というサーバサイドでのBean利用の技術が用意されていますが、一般的なWebアプリケーションでも、このようにServletContextを使うことで常時利用可能なBeanを用意することができます。ただし、この手法では、最初にBeanを生成しておく必要があるのを忘れないようにしましょう。

