eclipseのWTPプロジェクトでユーザライブラリを参照
あらまし
Tomcat等のサーブレットコンテナは複数のWebアプリケーションをデプロイでき、それぞれが独立したClassLoaderの管理下となる。つまり、複数アプリケーションをデプロイした場合、同じライブラリをそれぞれのClassLoaderに読み込んでしまう。
これによってライブラリのバージョンの競合は避けられるが、PermGenが肥大化するため低スペックサーバーでのメモリ圧迫やJava.1.7以前はPermGenのOutOfMemory等の問題に悩まされた。
しかし、サーブレットコンテナ全体に一つのライブラリを参照させることで個別のClassLoaderに新しいクラスが読み込まれるのを阻止し、PermGen領域を節約することができる。(1.8からはPermGen自体は可変らしい)
何をやりたいか
TomcatのClassLoaderに任意のクラスを読み込むためには開発環境上でもアプリケーションとライブラリを切り離してバージョン管理する必要が出てくる。
このような場合、WEB-INF/libにjarを放り込むのではなく、eclipseのユーザライブラリの機能を使用すればいいと考えたが、WTPプロジェクトで実施したところ、ビルドは正常に通ったがユーザライブラリがクラスパスに含まれず動作させることができなかった。
解決方法
サーブレットコンテナの実行構成のクラスパスで任意のライブラリを指定すれば、WEB-INF/libにjarファイルを置かずともサーバーにバンドルすることができます。
C言語のエラー処理で思い悩む
C言語のエラー処理は戻り値で返すのが一般的ですよね。
errnoのようにスレッドローカル変数に格納して返すのも可能ですが、この時に戻り値をどうするかなどの問題もあり、やはり戻り値ベースが無難です。
エラー処理で重要なのはエラーを握りつぶさないことですが、実践的にはエラー判定時、即returnだけでなくgoto エラーラベルで異常系時の解放処理を分離するパターンもあります。
try-catch-finallyのfinallyに当たるものですね。
ここまで考えると、setjmpでtry-catch-finallyもどきを作るのもアリかなと思って、一発ネタ検討考えてみました。ネットを検索すると関数内でのジャンプが可能なものは多々あります。
以下がネタ。
typedef struct __try_location { struct __try_location *next; jmp_buf jmpbuf; } __try_location; { __try_location current; int __status_exception = setjmp(__init_try_location(¤t)); int __rethrow = 0; if(__status_exception == 0) { // try ブロック } if(__status_exception != 0) { // catchブロック __rethrow = 1; } for(; i++ <= 0; __rethrow != 0? rethrow_exception() ? 0) { // finallyブロック } }
これなら、try-catch-finallyのマクロに置き換えられそう。catchだけはブロック中で例外タイプの判定が必要だけども。__init_try_locationはリスト要素として使用でき、スレッドローカル変数へチェインを登録します。__rethrow_exception()でさらなる__try_locationをjmp_bufの巻き戻し。
スレッドローカル変数アクセスとjmp_bufのレジスタコンテキスト保存にかかるオーバーヘッドが心配。(10個レジスタあると10x64bitの保存とかになるし・・・)
ちなみにWin32構造化例外は__exceptブロックに入ると変数がすべてレジスタからの再読み込みとなり、レジスタコンテキストを特別に保存はしていないようです。スタックフレームを巻き戻してexceptブロックを特定しているのではないかと思います。
プロジェクト管理とか品質管理についてグチ
短納期でアホほど品質ドキュメント作らされたにも関わらず受入テストも碌にせずに納品して大炎上してるようでは、何のために品質管理やっているか分からんな。
こう言うのは納期通り納めた内に入らないと言うか、そもそも1月ズルズル納期伸ばしてるし。
課題点も目をつむるばっかりだったし。
根本的にマネージできていないと言うか組織としどうよ。
なにそれおいしいの?
いまさらServletとJSPで作るWebアプリケーション
Javaで言うWebフレームワークと言うとちょっと前に流行ったStrutsと言うのがある。 これはここ数年で開発主体がStruts2へと主流が移り、またVersion 1系がEnd Of Lifeになったりと、 状況が大きく動いている。
今回はJSFやStruts2をはじめ乱立するWebフレームワークを置いておいて、 ServletとJSPへ回帰してみた話。
実際にServletベースで小規模なアプリケーションを開発したが、 思いのほかいい感じだったのでWebアプリケーションの フレームワークに必要な要素などを再考してみました。
余談:個人的にはコントローラの記述はSeasar ProjectのSAStrutsが一番好きですが、 JSPのタグライブラリがStruts1ベースなんだよね。あのタグライブラリあんまり好きじゃない。JerseyのViewableクラスもいっそのことJ2EE標準にすれば良いのに。
と言う所で惜しい物は一杯あるけど、これだ!と言う決定版がない。誰か良い物を知っていたら教えてください。
今回のAPの構成
APの構成は下記。
Persistence層の技術にHibernateを使用しているが それ以外の層は素朴なJava環境にて構築した。
メリット
とにかく枯れている
Javaが存在する限りサポートされ続けるであろう基盤技術。
Webベースの基本知識を流用可能
素朴なWebアプリケーションの知識をそのまま転用可能。
JSFやWicketなど最近のライブラリではWebの機能を大きくラップしているため、 独自の技術を獲得する必要あり。
モデルビューコントローラとWeb Frameworkが果たすべき機能
生産性の高いビュー開発技術
生産性の高いビューの開発技術はWebアプリケーションを構築する上では必須となる。 JSPやVelocity等がここに位置する技術となる。 また、Wicketではコンポーネントベースの考え方によってビューを開発する。
JSPは単体では十分な生産性を持つとは言い難いが、 JSTL(JSP Standard Tag Library)を組み込むことで生産性を向上させる事が出来る。
ビューとコントローラの分離
モデルの分離はWebアプリケーションに限らない 多くのアプリケーション共通のテーマであると考えるため、 あえてWebフレームワークの基本機能とは考えない。
モデルとコントローラの分離は ビジネスロジック層、サービス層の技術をもって分離する。
実はServletはRequestDispatcherを使用する事で、 コンテキスト内の任意のコンテンツに転送したり、 任意のコンテンツをインクルードする事が可能。
今回はこの技術をコアにしてJSPによるビューと Servletによるコントローラの分離を行った。
まとめ
要するにロジックはJavaでUIはJSPで書ければ良いと考える。
また、画面と画面遷移を分離できることも重要な要素。
実装例
ログインのサブミットボタンのアクションで実装例。 Servletはアクションのみ処理し、 エラーならログイン画面へ戻し、 ログイン成功すればメニューへ転送している。
public class LoginSubmitServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override protected void doPost( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String userName = request.getParameter("userName"); String password = request.getParameter("password"); AuthenticationService service = Services.getAuthenticationService(); ArrayListerrors = new ArrayList (); Authentication auth = null; try { auth = service.authenticate(userName, password); if(auth == null) { errors.add("The user name or password is unmatch."); } } catch(Exception ex) { errors.add(ex.getMessage()); } if(errors.size() > 0) { request.setAttribute( "messages", errors.toArray(new String[0])); request.setAttribute("userName", userName); request.setAttribute("password", password); forwardToLogin(request, response); return; } HttpSession session = request.getSession(true); session.setAttribute(Constants.AUTH_SESSION_ATTR, auth); response.sendRedirect(request.getContextPath() + Constants.APP_ROOT_PATH); } private void forwardToLogin( ServletRequest request, ServletResponse response) throws IOException, ServletException { // ログイン画面へフォワード request.getRequestDispatcher("/WEB-INF/jsp/login.jsp") .forward(request, response); } }
/WEB-INF/jsp/login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <link rel="stylesheet" type="text/css" href="<c:url value="/o/style/rssreader.css"/>"/> <title>ログイン</title> </head> <body> <jsp:include page="/WEB-INF/jsp/base/header.jsp"/> <jsp:include page="/WEB-INF/jsp/base/menu-nologin.jsp"/> <div id="content"> <h3>ログイン</h3> <form method="post" action="<c:url value="/o/login-submit"/>"> <table class="form"> <tr> <th>メールアドレス:</th> <td><input type="text" name="userName" value="<c:out value="${userName}"/>"/></td> </tr> <tr> <th>パスワード:</th> <td><input type="password" name="password" value="<c:out value="${password}"/>"/></td> </tr> <tr> <td colspan="2"> <input type="submit" name="login" value="ログイン"/> </td> </tr> </table> </form> <c:if test="${messages != null}"> <div class="messagebox"> <ul> <c:forEach var="message" items="${messages}"> <li><c:out value="${message}"/></li> </c:forEach> </ul> </div> </c:if> </div> <jsp:include page="/WEB-INF/jsp/base/footer.jsp"/> </body> </html>
Java:JSON Jacksonを使ってみる 浮動小数点マッピング編
前回に引き続きJacksonの話題。「Java/JSON Jacksonを使ってみる マッピング編」
Jacksonとは
Jacksonはhttp://jackson.codehaus.org/で開発されている JSONデータをJavaにマッピングするためのライブラリ。
jackson-databindを利用すればJavaオブジェクトへ直接データをマッピングできる。
jacksonを利用するにあたって、どのようにJavaオブジェクトへマッピングされるのかを検証してみました。
Jacksonの浮動小数点マッピングについて
JSONの仕様で疑問点があるのが浮動小数点マッピング。
Javaではfloat/doubleにはじまり10進数浮動小数点のBigDecimal等もあるが、 JSONでは浮動小数点の精度についての取り決めが存在しない。
Jacksonにおいて、それぞれの場合にどのような精度でのマッピングとなるかを検証してみました。
各種浮動小数点の精度とJacksonによるマッピング
floatの場合
ObjectMapper mapper = new ObjectMapper(); Float flt = mapper.readValue("0.123456789123456789", Float.class); System.out.println("value : " + flt); System.out.println("json : " + mapper.writeValueAsString(flt));
value : 0.12345679 json : 0.12345679
float精度まで切り詰められる。
doubleの場合
ObjectMapper mapper = new ObjectMapper(); Double dbl = mapper.readValue("0.123456789123456789", Double.class); System.out.println("value : " + dbl); System.out.println("json : " + mapper.writeValueAsString(dbl));
value : 0.12345678912345678 json : 0.12345678912345678
double精度まで切り詰められる。
BigDecimalの場合
ObjectMapper mapper = new ObjectMapper(); BigDecimal dec = mapper.readValue("0.123456789123456789", BigDecimal.class); System.out.println("value : " + dec); System.out.println("json : " + mapper.writeValueAsString(dec));
value : 0.123456789123456789 json : 0.123456789123456789
なんと、BigDecimal精度で取り扱える。 てっきり、Doubleまで切り詰められると思っていたのでビックリ。
Objectの場合
BigDecimalをBigDecimal精度で扱えるようなので、 Object型の場合も試してみた。
ObjectMapper mapper = new ObjectMapper(); Object obj = mapper.readValue("0.123456789123456789", Object.class); System.out.println("value : " + obj); System.out.println("type : " + obj.getClass().getName()); System.out.println("json : " + mapper.writeValueAsString(obj));
value : 0.12345678912345678 type : java.lang.Double json : 0.12345678912345678
Objectの場合、Double型へ変換されるよう。 予想ではBigDecimalだったのでこちらもビックリ。
まとめ
Jacksonにおいてはfloat/double/BigDecimalについてそれぞれの精度で正確に値を扱えるよう。
しかし、JSONの浮動小数点形式は基本的にJavaScriptの物と考え、 Double以下の精度と考えておいた方が良いだろう。