Archive for the ‘Java’ Category

連結リストについて驚いたこと。

火曜日, 5月 10th, 2005

 先日、Javaのコードを書いている際に、隣の席のFさんと話題になったことがあります。Java.util.iteratorクラスについてです。
まず、Bean(Cでいう構造体みたいなの?VBでいうユーザー定義名前みたいなの?)の中に値を入れ、それをいくつか挿入したArrayListを作成し、それを繰り返し処理していくというコードです。

JAVA:
  1. System.out.println(list); // ①listにはBeanがいくつか入っている
  2. Iterator iterator = list.iterator();
  3. while(iterator.hasNext()) {
  4. Bean bean = (Bean)iterator.next(); // これでlistの中の一つのbeanが取れる
  5. bean.setXXX("abcdefg"); // beanの中の変数の値を変える
  6. }
  7. System.out.println(list); // ②

①の時点でリストをシスアウトしたものと②でシスアウトしたものが違って出力されました。私は今まで、while文のなかで取得したbeanを変更しても、元のlistは変わらないと思っていたのですが、iterator反復子で処理した内容は元のリストにも反映されました。今まではリストをもう一つ作ってbeanを追加しなおしてから使っていたので影響は無かったのですが、こういう知らないで使っていた、という事実にびっくりしました。ResultSetと同じようにiterator.next()をすると、カーソルを移動させるみたいです。
whileの中で処理をしている途中でlistの値を変更すると、ConcurrentModificationExceptionという例外が起こります。なので、Iteratorオブジェクトを使った繰り返しはsynchronizedブロックの中で行うと良い、ということです。実際、そこまでやってるコードは見たこと無いですが…。

JAVA:
  1. synchronized(list) {
  2.     Iterator iterator = list.iterator();
  3.     while(iterator.hasNext()) {
  4.         Bean bean = (Bean)iterator.next();
  5.         bean.setXXX("abcdefg");
  6.     }
  7. }

この事を調べるのに Javaプログラマのためのアルゴリズムとデータ構造 という本を読みました。知らなくてもコードは書けるのですが、知らないことがいっぱいあるのに書いてるのが怖くなりました。



XMLをJavaで扱う

月曜日, 2月 21st, 2005

SAXとDOM
XMLからデータを読んでDBへ…、という機能を作りました。
JavaでXMLを操作する際は、SAX (Simple API for XML)とDOM(Document Object Model)という標準APIが使えます。オープンソースのXML解析ライブラリもどちらかを使っています。

今回は以下のようなXMLを読込み実験しました。

XML:
  1. <?xml version="1.0" encoding="Shift_JIS" ?>
  2. <root>
  3. <aaa>あいうえお</aaa>
  4. </root>

XMLはW3Cの規約に沿っていないと読み込み時に例外が発生します。
マルチバイト文字の数字とか、括弧とかもです。(意外)

SAXはXML文書を先頭から順に読んでいき、発生したイベント(タグが始まった、終わった等)をアプリケーションでとらえて処理するAPIです。
ノードに階層の深さが決まりきっているXMLを読む際は便利です。
実装はorg.xml.sax.helpers.DefaultHandlerを継承したHandlerクラスを作成します。

JAVA:
  1. package test;
  2.  
  3. import org.xml.sax.*;
  4. import org.xml.sax.helpers.*;
  5.  
  6. public class SampleHandler extends DefaultHandler {
  7.  
  8. public void startDocument() {
  9. //ドキュメント開始
  10. }
  11. public void endDocument() {
  12. //ドキュメント終了
  13. }
  14. /**
  15. * @param namespaceURI
  16. * @param localName
  17. * @param qName
  18. * @param atts
  19. */
  20. public void startElement
  21. (String namespaceURI, String localName, String qName, Attributes atts) {
  22. // 要素の始まりの処理
  23. }
  24. /**
  25. * @param namespaceURI
  26. * @param localName
  27. * @param qName
  28. */
  29. public void endElement(String namespaceURI, String localName, String qName) {
  30. System.out.println("</"+qName+">");
  31. }
  32. public void characters(char[] ch, int start, int length) {
  33. // 要素の間の値
  34. }
  35. }

上で作ったHandlerはこのように使います。

JAVA:
  1. SAXParserFactory factory = SAXParserFactory.newInstance();
  2.  SAXParser parser = factory.newSAXParser();
  3.  String url = "read_test.xml"; // XML文書の実際のURL
  4.  SampleHandler handler = new SampleHandler("a");
  5.  parser.parse(url, handler);// これで解析ができる。

DOMは要素の入れ子階層が不確定なときにwhileなどで使うのに便利です。

JAVA:
  1. package test;
  2.  
  3. import java.io.*;
  4.  
  5. import org.w3c.dom.Document;
  6. import org.w3c.dom.Node;
  7. import javax.xml.parsers.*;
  8.  
  9. /**
  10. * @author nakaya
  11. *
  12. * $Log: $
  13. */
  14. public class NodeSample {
  15.  
  16. private static final String nodeNameText = "#text";
  17. public static void main(String args[]) throws Exception{
  18.    // XMLファイル読込
  19. Document document= DocumentBuilderFactory.newInstance()
  20. .newDocumentBuilder().parse(new File("read_test.xml"));
  21. // ルートノード
  22. Node rootNode = (Node)document.getDocumentElement();
  23.  
  24. // 最初のノード
  25. Node firstNode = rootNode.getFirstChild();
  26. while (null!=firstNode) {
  27. // ノード名
  28. String nodeName = firstNode.getNodeName();
  29. if (nodeName.equals(nodeNameText)) {
  30. firstNode = firstNode.getNextSibling();
  31.     continue;
  32. } else if (nodeName.equals("aaa")) {
  33.     threeNestings(firstNode);
  34. } else if (nodeName.equals("bbb")) {
  35.     threeNestings(firstNode);
  36. }
  37. firstNode = firstNode.getNextSibling();
  38. }
  39. }
  40.  
  41. /**
  42. * 入れ子が2段階
  43. * @param firstNode
  44. */
  45. private static void twoNestings (Node firstNode) {
  46.     Node secondNode = firstNode.getFirstChild();
  47.     while (null!=secondNode) {
  48.     String nodeName2 = secondNode.getNodeName();
  49.     if (!nodeName2.equals(nodeNameText)) {
  50.       System.out.println(nodeName2 + " : "
  51.         + secondNode.getFirstChild().getNodeValue());
  52.     }
  53.    secondNode = secondNode.getNextSibling();
  54. }
  55. }

どちらにせよ、XMLの書式は始めにきっちり決めておいた方が作りやすいのは間違いないですが…。



フィルタ

水曜日, 1月 19th, 2005

今まで等閑にしておいたFilterについて調べてみました。
まだ使ったこと無いので、メモ程度に。

フィルタとはクライアントからのリクエストおよびレスポンスに対して、リソースServlet、JSP、HTHMLでの処理の前および後に何らかの処理を行うオブジェクトの事。文字のエンコーディングやログ取得などに使う。

フィルタはServlet2.3から使用可能。
フィルタはjavax.servlet.Filterインターフェイスを実装して作成する。

JAVA:
  1. public class XxxxFilter implements Filter {
  2. public void <strong>doFilter</strong>
  3. (ServletRequest request, ServletResponse response, FilterChain chain)
  4. throws IOException, ServletException {
  5. // ここでフィルターにさせたい処理を記述
  6. }
  7. public void <strong>init</strong>(FilterConfig config) throws ServletException {
  8. }
  9. public void <strong>destroy</strong>() {
  10. }
  11. }

それから、アプリケーションの配置記述子(web.xml)かTomcatのConfの下などにあるサービスのweb.xmlにフィルタのマッピングを記述しないといけない。

XML:
  1. <filter>
  2. </filter><filter>XXXFilterName</filter>
  3. <filter>xxx.yyy.zzz.XxxxFilter</filter>
  4.  
  5. <filter>
  6. </filter><filter>XXXFilterName</filter>
  7. <url>/*</url><!--←こうするとURL下の全てのServletの処理に行く前にフィルタを通る。-->

Tomcatのweb.xmlの下の個所をShift_JISにするとエンコーディングをやってくれるらしい。今までStringユーティリティクラス等を作成して使っていた…。

XML:
  1. <filter>
  2.     </filter><filter -name>Set Character Encoding</filter>
  3.     <filter -class>filters.SetCharacterEncodingFilter</filter>
  4.     <init -param>
  5.         <param -name>encoding</param>
  6.         <param -value><strong>Shift_JIS</strong></param>
  7.     </init>
  8.  
  9.   <filter -mapping>
  10.     </filter><filter -name>Set Character Encoding</filter>
  11.     <url -pattern>/*</url>



日付の妥当性チェック

土曜日, 10月 30th, 2004

日付の妥当性をチェックします。
Calendar#setLenient()にfalseをセットしてDateオブジェクトの生成を試みると、ありえないCalendarインスタンスは例外を投げます。
例:2004年02月29日はOK
例:2005年02月29日は例外

JAVA:
  1. /**
  2. * 与えられた日付が妥当であるかをチェックします。
  3. * author R.NAKAYA
  4. * @param strDate 8桁の生年月日 (1981年4月15日→19810415)
  5. * @return true:妥当である false:あり得ない日付
  6. */
  7. public static boolean isNotDateLenient(String strDate) {
  8. // 生年月日が8桁でない場合はエラー
  9. if (8 != strDate.length()) {
  10. return false;
  11. }
  12.  
  13. // 年・月・日の整数に変換
  14. int year = Integer.parseInt(strDate.substring(0, 4));
  15. int month = Integer.parseInt(strDate.substring(4, 6)) - 1;
  16. int day = Integer.parseInt(strDate.substring(6, strDate.length()));
  17.  
  18. Calendar cal = Calendar.getInstance();
  19. cal.setLenient(false); // 厳密なチェックを行う
  20. cal.set(year, month, day); // 日付を指定
  21.  
  22. try {
  23. // Dateオブジェクトを生成してみて、例外が発生するかを試す
  24. Date date = cal.getTime();
  25. return false;
  26. }
  27. return true;
  28. }
  29. }

たいがい、このようなチェックはWebではJavaScriptで行うのですが、携帯サイトなどのチェック用に。



JNLPファイルについて

金曜日, 9月 10th, 2004

 プロ野球、ストライキ回避!ということはさておき。
Java Web Start でクライアント側にアプリケーションを呼び出す場合、JNLPファイルというのを作らないといけない。
詳しいことは Java TM Web Start 開発者ガイド Ver.1.2 を参照のこと。
以下のようにindex.jnlpというファイルを作成する。

XML:
  1. &lt;?xml version="1.0" encoding="Shift_JIS"?&gt; <!-- (1) -->
  2. <jnlp spec="1.0+" codebase="http://localhost:8080/swttest/"><!-- (2) --></jnlp>
  3.  
  4. <information>
  5. <!-- (3) -->
  6. <vendor>提供者</vendor> <!-- (4) -->
  7. <offline>
  8. </offline></information>
  9.  
  10. <resources>
  11. <j2se version="1.4">
  12. <jar><!-- (5) -->
  13. <nativelib><!-- (6) -->
  14. </nativelib></jar></j2se></resources>
  15.  
  16. <resources os="Windows">
  17. <jar>
  18. </jar><!-- (7) -->
  19. <application main-class="xxx.yyy.hello"></application> <!-- (8) -->
  20. </resources>

(1) ここを日本語にしないとスタート時のダイアログに日本語文字を表示しようとすると文字化けする。
(2) codebaseはWebサーバーに配置したサービスのURL。基本的にWebアプリケーションの配置の仕方と同じ。localhostにしとくと、クライアント自身のlocalhostを見に行こうとするので、本番環境に移行した際にブラウザのエラーになるミスをよくやる。
(3) アプリケーションのスタート時ダイアログに表示される。
(4) これも。
(5) JVM起動時にクラスパスとして指定するJarファイル名。
(6) ネイティブライブラリ(Windowsでは*.dll, Linuxでは*.soとか)のロードを行うJarファイルの指定。
(7) OSによって、使うJarを使い分けできる。これはSWTのWidows用Jar。
(8) 起動するメインクラスのパッケージ+クラス名を指定。

作成したindex.jnlpをWebアプリの配置ルールにしたがって、Webアプリケーションのベースフォルダ(jspとかhtmlファイルを置くフォルダか、それ以下)に配置して、httpでアクセスしてみる。起動用リンクなどを作ると良い。このとき、もちろんクライアント側には Java Web Start がインストールされていないといけない。



スレッドの必要性

木曜日, 9月 9th, 2004

org.eclipse.swt.SWTException: Invalid thread access
という、見たこと無い例外が発生。SWTでクライアントアプリを作成している途中です。何をしていたか、というと、ひとつのShell(ウィンドウ)を開いて、ボタンを押したときのイベントリスナから他のShellを呼び出す、という動作。
Eclipseでデバック実行しているときは、Exceptionがでないんですが、WebStartから実行すると、二つ目のShellを表示する前にorg.eclipse.swt.SWTException: Invalid thread accessという例外がでました。
SWTは資源の競合が起こらないように、ウィジェットの操作を別スレッドから行うことを禁止しているそうです。
呼び出すShellの実行を他スレッドで実行しないといけないらしく。二つ目のShellを実行するクラスにRunnableをimplementsして、run() メソッドを実装しないといけないらしい。そして、run() メソッドは start() メソッドから呼び出す。
サンプルを作ってみた。これだとWebStartからの起動時も例外が出なかった。

JAVA:
  1. /**
  2. *呼び出される側
  3. */
  4. class XXXX implements Runnable {  // Runnableをimplementsする
  5. private final Shell shell;// Shell
  6. public XXXX() {//コンストラクタ
  7. // 自作メソッドopen()をコール
  8. open();
  9. }
  10. public void open() {
  11. checkAsyncExec(new Runnable() {
  12. public void run() {
  13.    …Shellの初期化などをする
  14. // Shellを開く
  15. shell.open();
  16. }
  17. }
  18.  
  19. // Displayが生きてるか死んでるかのチェック
  20. private boolean checkAsyncExec(Runnable r) {
  21. if (!display.isDisposed()) {
  22. display.asyncExec(r);
  23. return true;
  24. } else {
  25. return false;
  26. }
  27. }
  28. }
  29.  
  30. /**
  31. *呼び出す側
  32. */
  33. class YYYY {
  34. private final Shell shell;// Shell
  35. public YYYY () {//コンストラクタ
  36. // シェルの生成
  37. Shell = new Shell(display, SWT.TITLE);
  38. Button buttonY;
  39. }
  40.  
  41. /**
  42. * メイン関数
  43. */
  44. public static void main(String[] strArgs) {
  45. Display display = new Display();
  46. YYY YFace = new YYY(display);
  47. // ログイン画面を開く
  48. YFace .open();
  49. // ウインドウが閉じるまでメインスレッドを終了しない
  50. while (!isAllShellsDesposed(display)) {
  51. if (!display.readAndDispatch()) {
  52. display.sleep();
  53. }
  54. }
  55. // 破棄処理
  56. display.dispose();
  57. }
  58.  
  59. open(){
  60. ・・・Shellの初期化など。
  61. }
  62.  
  63. /**
  64. * 表示されているすべてのShellが破棄されるまでFalseを返す
  65. * @param display
  66. * @return
  67. */
  68. private static boolean isAllShellsDesposed(Display display) {
  69. Shell[] shells = display.getShells();
  70. for (int i = 0; i &lt;shells.length; i++) {
  71. if(!shells[i].isDisposed()){
  72. return false;
  73. }
  74. }
  75. return true;
  76. }
  77.  
  78. /**
  79. * イベントリスナの設定
  80. */
  81. setEventListeners() {
  82. buttonY.addSelectionListener(new SelectionAdapter() {
  83. new Thread(new XXX(shell.getDisplay()).start();
  84. }
  85. });



Java Web Start

火曜日, 9月 7th, 2004

 swtを利用してアプリを作るからには Java Web Start を利用してみたいと思いました。というわけで、プロトタイプを作ってお客さんに提案してみたところ、「これで行きましょう!」ということに。
 今までWebアプリケーション以外、殆ど経験無いですし、周りに開発経験のある人がいないので、少し不安ですが、なんとかなるでしょう。できなかったらバッチかEXEを配布する、という妥協案も受け入れてもらえたし。
 こういう、好きな技術を提案できる仕事は良いです。好きです。



JDBCドライバについてメモ2

月曜日, 9月 6th, 2004

先日の「JDBCドライバについてメモ」の訂正。

JDBCドライバの接続の際は
jdbc:oracle:thin:@サーバー名、またはIP:1521:ORCL
とURLを指定するけど、SID名であるORCLはサーバーのSIDを探しに行くらしい。
ojdbc14.jarというドライバを使用しているが、このドライバはクライアントにOracleクライアントが入って無くても、接続可能だということ。

じゃ、問題解決です。



swtリッチクライアント

土曜日, 9月 4th, 2004

久々に早く寝たので、夜中の二時に目が覚めてしまった…。JUGEMが復旧してるみたいなので、今回の仕事のまとめを。これからこういう仕事増えると面白い。

今回のお題

  • JavaでWidnowsGUIのようなアプリを実現する
  • 再配布が楽なリッチクライアント技術を試みる
  • 懸念事項

  • クライアントマシンに Oracle Client が入って無くても動作するか
  • :→他は試してないが、ojdbc14.jarは接続できる。

  • Oracle だけでなく、その他パッケージソフトで動作するサービスに接続できるか
  • :→結果、できる。

  • バッチファイルで起動するのは何か嫌。
  • →:実は Project Amateras で Boot Java というツールが公開されてます。Javaアプリをexeから起動できる。iniファイルにメイン関数を持ったクラスとか、起動時に必要なパラメータ、クラスパスとかを記述できる。

    提案事項

  • Appletは見栄えが悪いので、swtを提案
  • クライアント配布用に Java Web Start を提案
  • 主な予想外の自体

  • 避けては通れないと思っていたが、スレッドを勉強するきっかけになった
  • war ファイルを作成するのに手間取った。自己署名証明書の作成など。あと、jarファイル作成時、プロパティファイルの "Duprication Entry" 圧縮時の例外でかなり時間を食ってしまった。Eclipse でデバック実行時にはclassesの下にプロパティファイルが無いと MissingResourceException が発生するのだが、圧縮する際は Classes フォルダの下のプロパティファイルは削除して圧縮。それでも、リソースとかはちゃんと表示される。不思議。というか、これらの処理をAnt 記述で行ったのだが、Ant 自体を良く理解できてない。
  • Eclipseのデバック起動時には何事も無く動作しても、デプロイして Java Web Start から動かしてみると、スレッド処理時の例外とかがポコポコでてくる。難しい。
  • 「何か動かんなぁ?」と思ったら、データベースサーバーの名前が解決できてなかったりした。本番環境で動かしてみるときは、DNS とか、サーバーのプロキシ設定とかをセットアップ手順に加えたほうが良い。


  • JDBCドライバについてメモ

    火曜日, 8月 31st, 2004

     SWTアプリケーションからあるサーバーのデータベースに接続しないといけなくなりました。今まではWebコンテナの入っているサーバーにOracleクライアントがインストールされていますが、それをインストールせずに接続することは可能でしょうか?
    Javaのコード

    // Oracle JDBC Driverのロード
    Class.forName("oracle.jdbc.driver.OracleDriver");
    // Oracle8iに接続
    Connection conn =
      DriverManager.getConnection ("jdbc:oracle:thin:@localhost:1521:ORCL", "scott", "tiger");

    でコネクションを取得することが可能。
    けど、jdbc:oracle:thin:@localhost:1521:ORCLのORCLという個所は各クライアントに設定されているSIDを読み込まなければならないので、これをどうにかしたい。

  • Oracle JDBC Thin Driver接続のURL
  • jdbc:oracle:thin:@< ホスト名>:< リスナのポート番号>:

    JDBC-ODBC ブリッジドライバというものがあるらしい。

  • JDBC-ODBCブリッジ・ドライバ接続のURL
  • jdbc:odbc:xyz
    (“xyz”は、ODBCデータ・ソース名)
    これって、各クライアントに何か設定しないといけんのでしょうか?週末は本番環境に持っていって試してみよう。だめなら、オラクルクライアントを全クライアントにインストールする覚悟らしいし…。