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ファイルを置かずともサーバーにバンドルすることができます。
 

f:id:dachihada:20151112173008j:plain

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(&current));
	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フレームワークを置いておいて、 ServletJSPへ回帰してみた話。

実際にServletベースで小規模なアプリケーションを開発したが、 思いのほかいい感じだったのでWebアプリケーションの フレームワークに必要な要素などを再考してみました。

余談:個人的にはコントローラの記述はSeasar ProjectのSAStrutsが一番好きですが、 JSPのタグライブラリがStruts1ベースなんだよね。あのタグライブラリあんまり好きじゃない。JerseyのViewableクラスもいっそのことJ2EE標準にすれば良いのに。

と言う所で惜しい物は一杯あるけど、これだ!と言う決定版がない。誰か良い物を知っていたら教えてください。

今回のAPの構成

APの構成は下記。

Persistence層の技術にHibernateを使用しているが それ以外の層は素朴なJava環境にて構築した。

メリット

とにかく枯れている

Javaが存在する限りサポートされ続けるであろう基盤技術。

Webベースの基本知識を流用可能

素朴なWebアプリケーションの知識をそのまま転用可能。

JSFWicketなど最近のライブラリでは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();
		
		ArrayList errors = 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の場合

BigDecimalBigDecimal精度で扱えるようなので、 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以下の精度と考えておいた方が良いだろう。