まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

2016/10

Java で一時的な作業ディレクトリを用意する場合に、こんなコードを書いていました:
String tmpdir = System.getProperty( "java.io.tmpdir" );  //. システムの一時ディレクトリ
tmpdir += ( File.separator + "_abc" + File.separator );
   :

まず System.getProperty( "java.io.tmpdir" ) で、システム上の一時ディレクトリ(例えば "/tmp")を取得し、そこにファイルセパレータと任意の文字列(上記例では "_abc" )を追加して作ってました。

これらの処理が終わった後の tmpdir 文字列の値は "/tmp/_abc/" となることを想定していて、例えば一時ファイルを作るのであれば、
  :
String tmpfilepath = tmpdir + "test.txt"
  :

みたいにして、tmpdir にファイル名を付けるだけでよい、という風に使っていました。


ところが、この System.getProperty( "java.io.tmpdir" ) の実行結果にシステム間で違いがあることがわかりました。Linux 系システムだと "/tmp" のような値が返ってきますが、Windows システムで実行すると "C:\Users\username\AppData\Local\Temp\" のような、最後に File.separator が付いている値が返ってくるようです。なのでその結果に対して、
tmpdir += ( File.separator + "_abc" + File.separator );
を実行してしまうと、tmpdir の値は "C:\Users\username\AppData\Local\Temp\\_abc\" となってしまい、File.separator が2回繰り返しで付いてしまうことになってしまうようでした。

要するに System.getProperty( "java.io.tmpdir" ) の実行結果はシステムによって末尾に File.separator が付いたり付かなかったりするようでした。これは想定外。

というわけで、今度からはこう書くことにしました:
String tmpdir = System.getProperty( "java.io.tmpdir" );
tmpdir += ( ( tmpfolder.endsWith( File.separator ) ? "" : File.separator ) + "_abc" + File.separator );
   :

File.separator で終わっていなかった場合だけ File.separator を付けて、で "_abc" と・・・ という感じに。これでシステム依存部分を回避できました。




自分は、仕事柄サンプルコードを書くことが多く、 Java とかで例外処理が必要な時に、
try{
    :
    :
}catch( Exception e ){
  // 例外が発生したらここで処理
}

という書き方でまとめちゃうことが多いです。要は特定の Exception を個別にキャッチするのではなく、まとめて捕まえてやれ、的な例外処理です。これはエラーが発生したりすると原因追求がわかりにくくなるし、コードレビュー受けると「わかりにくい」って言われる、あまりいいやり方とはいえない方法です、弁解ではないですが、サンプルコードを書いて提供することが多く、例外処理がメインではないようなコードを紹介するような場合に便宜的にこういうコードを書いちゃうことが多いです。はい。以上言い訳でしたw

で、こういう「全ての例外を捕まえてやる!」的な例外処理方法のことを "Pokemon Exception Handling(ポケモン例外処理)" って呼ぶんですね。知らなかった・・・
http://stackoverflow.com/questions/2308979/exception-handling-question/2308988


でも日本では聞いたことないな・・・アメリカで流行ってるんだろうか・・・


Apache Derby(或いは "Cloudscape")という RDB をご存知でしょうか?
derby-logo-web


最近は SQLite や HTML5 のローカルデータストアの台頭であまり名前を聞かなくなりましたが、Pure Java で記述された軽量の RDB です。元々は Cloudscape Inc. によって開発されましたが、Informix Software を経て IBM 製品として扱われていた時代もあります。その影響もあってか "DB2 互換 SQL" に対応し、DB2 の SQL が動く軽量の Java RDB という立ち位置でした。軽量であるが故に組み込み系のアプリケーション内で使われることが多いようです。歴史的には 2004 年に IBM から Apache 財団へソースコードが寄贈され、現在の Apache Derby という名称のプロジェクトになりました。また Oracle JDK 1.6 以降に(オプションとして)組み込まれている JavaDB の実装はこの Apache Derby です。

私自身は "Cloudscape" と呼ばれていた頃に使ったことがありました。今回、久しぶりに Apache Derby を使ってみました。

JDK のオプションに組み込まれているとはいえ、せっかくなので最新版を使ってみることにしました。まずは Apache Derby のダウンロードページから最新バージョン(2016/Oct/07 時点では 10.12.1.1)のリンクをクリックします:
2016100601


最新版のアーカイブファイル:db-derby-(バージョン番号)-bin.zip をクリックしてダウンロードします:
2016100602


ダウンロードした zip ファイルを展開し、lib フォルダ内の必要な JAR ファイルを取り出します。今回は本体である derby.jar と、日本語ロケールが含まれた derbyLocale_ja_JP.jar の2ファイルを取り出します:
2016100603


この2ファイルを Java の開発環境から使えるようにします。J2SE/EE プロジェクトであれば、WebContent/WEB-INF/lib 以下にコピーするなどして、コンパイル/実行時に参照できるようにしておきます:
2016100604


試しに以下のような index.jsp ファイルを用意してみました:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.*" %>
<%@ page import="java.io.*" %>
<%@ page import="java.sql.*" %>
<%@ page import="me.juge.derby.*" %>
<%
  request.setCharacterEncoding("utf-8");
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3c.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title>Derby JDBC Sample</title>
</head>
<body>

<table border="1">
<tr><th>ID</th><th>NAME</th><th>PRICE</th></tr>
<%
final String driverName = org.apache.derby.jdbc.EmbeddedDriver.class.getCanonicalName();
final String dbName = "derbydb";
final String connURL = "jdbc:derby:" + dbName + ";create=true";  //. DBが存在していない場合は作成するオプション

try{
  Class.forName( driverName );
  Connection conn = null;

  try{
    conn = DriverManager.getConnection( connURL );
  }catch( Exception e ){
  }

  if( conn != null ){
    ResultSet rs = null;
    //. 初期化
    try{
      //. 試しに items テーブルにアクセス
      Statement s0 = conn.createStatement();
      rs = s0.executeQuery( "select count(*) from items" );
    }catch( SQLException e ){
      //e.printStackTrace();

      String state = e.getSQLState();
      if( state.equals( "42X05" ) ){
        //. テーブルが存在しない
        try{
          Statement s1 = conn.createStatement();
          s1.execute( "create table items("
            + " id int generated always as identity primary key"  //. この辺りが DB2 互換 SQL
            + ", name varchar(100)"
            + ", price int"
            + " )");

          PreparedStatement s2 = conn.prepareStatement( "insert into items( name, price ) values( ?, ? )" );
          s2.setString( 1, "シャンプー" );
          s2.setInt( 2, 1000 );
          s2.execute();

          PreparedStatement s3 = conn.prepareStatement( "insert into items( name, price ) values( ?, ? )" );
          s3.setString( 1, "石鹸" );
          s3.setInt( 2, 500 );
          s3.execute();
        }catch( Exception e1 ){
        }
      }else if( state.equals( "42X14" ) || state.equals( "42821" ) ){
        //. テーブル定義が不正

      }else{
        //. その他の想定外の例外

      }
    }finally{
      if( rs != null ) rs.close();
    }

    //. レコード表示
    try{
      rs = conn.createStatement().executeQuery( "select * from items" );
      while( rs.next() ){
        int id = rs.getInt( 1 );
        String name = rs.getString( 2 );
        int price = rs.getInt( 3 );
%>
<tr><td><%= id %></td><td><%= name %></td><td><%= price %></td></tr>
<%
      }
    }catch( SQLException e ){
    }finally{
      if( rs != null ) rs.close();
    }
  }
}catch( Exception e ){
  e.printStackTrace();
}
 %>
</table>
</body>
</html>

コードそのものは一般的な JDBC プログラミングです。DB を開く際に目的の DB が存在していない場合はその場で作成するようなオプション("create=true")を付与しています。また items というテーブルのレコード数を取得して、エラーが出るようであればテーブルが存在していないと判断し、新規に items テーブルを定義して作成し、2つほどデータを insert するようにしています。 最終的には items テーブル内の全レコードを画面に出力する、という内容にしています。


このプロジェクトを実行して、index.jsp にブラウザでアクセスすると以下のような表形式で2つのレコード内容が確認できます:
2016100701

まあ普通に RDB として使うこともできますが、組み込み系以外であればフットプリントの小ささからローカルレプリカ DB として利用する、というケースも考えられます。特にサーバー側が DB2 だったりすると SQL 互換が便利に使えたりしますね。

久しぶりに Cloudscape を使ってみました。相変わらずですが( Java が動いている前提はありますが)JAR ファイル置くだけで使える RDB は便利ですね。



このページのトップヘ