2008年12月6日土曜日

改めてSpringを試す(DIコンテナとしての)

これまでSpring MVCのついでにSpringの機能を使って気ましたが、改めてDIコンテナとしてのSpringコアを触ってみたいと思います。

大体"DIコンテナ"なんていう名称が胡散臭かったんだよね。またしてもJargonか。けっ。位にしか思っていなかった。でも理解しないままケチをつけるのもカッコ悪いので。

試したのは以下のチュートリアルです。こなれていて(特徴の示し方がシンプルで明確。分かりやすい)悪くないです。
http://www.atmarkit.co.jp/fjava/rensai3/springdi01/springdi01_2.html

【まとめ】

いや、Springって結構いいアイデアですね。要するにXMLを介してオブジェクトを連携させることで、疎結合やAOP(処理の注入。intercept。そんな感じ)を実現するフレームワークです。例によってSpringの本質をぜんぜん理解してなかったみたい(なんでわざわざHibernateやらStrutsと連携させる必要があるんだ。わずらわしい。くらいの認識でした。お恥ずかしい)。

以前「SpringはアーミーナイフでStrutsは鋭い刃物」と喩えました。このときはSpring MVCのみを念頭においていたのですが、Spring全体に対しても当たらずとも遠からずかもしれません。Springは便利で軽快な汎用ツール群だと思います。それにSpringで開発するのは結構楽しい(これって重要)。エンジニア魂をくすぐる。人気があるのも理解できます。

使い場所にもよるでしょうが、プロジェクトによっては有効ではないでしょうか。ただちょっとマニアックな気がするなあ。Springで作って良かった。と実感するケースはあまりなかったりして・・・(「再利用可能性」を志向して一生懸命デザインしても、実は誰も再利用しない、とかそんな感じ)

【おまけ】

チュートリアルの流れで少し拡張してみました。(ぜんぜん大したものではないですが一応)

■ SpringTest.java
package test;

import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {

/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"applicationContext.xml");
Target target = (Target) context.getBean("target");
System.out.println(target.getMessage());
Parent parent = (Parent) context.getBean("parent");
System.out.println("親:" + parent.getName());
List<Child> children = parent.getChildren();
for (Child child : children) {
System.out.println(child.getName());
}

}

}

■ applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="target" class="test.Target">
<property name="message">
<value>Hello World!</value>
</property>
</bean>
<bean id="oldest-son" class="test.Child">
<property name="name">
<value>長男</value>
</property>
<property name="no">
<value>1</value>
</property>
</bean>
<bean id="oldest-daughter" class="test.Child">
<property name="name">
<value>長女</value>
</property>
<property name="no">
<value>2</value>
</property>
</bean>

<bean id="parent" class="test.Parent">
<property name="children">
<list>
<ref bean="oldest-son" />
<ref bean="oldest-daughter" />
</list>
</property>
<property name="name">
<value>親</value>
</property>
</bean>
</beans>

■Parent.java

package test;

import java.util.List;

public class Parent {

private List<Child> children;
private String name;

public List<Child> getChildren() {
return children;
}
public void setChildren(List<Child> children) {
this.children = children;
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}

}

■Child.java

package test;

public class Child {
private String name;
private Integer no;

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getNo() {
return no;
}
public void setNo(Integer no) {
this.no = no;
}
}

以上
.

2008年12月5日金曜日

RAD7.5でWeb Serviceを作ってみる

ここ数年使われているjargonであるところのWeb service(英語圏では大文字でなく、かつ複数形のweb servicesみたいな記載をよく見ます)。せっかくなのでRAD7.5で作ってみました。個人的にはこのテクノロジーに対しては出た当初から半信半疑です。セールストークやらわけの分からない専門用語が飛び交い過ぎている。まあ私が見限ったのはずいぶん昔のことだし、食わず嫌いもよくないので、最近のweb sevicesがどうなってるのか見てみたいと思います。

TutorialはIBM dW "Create stand-alone Web services applications with Eclipse and Java SE 6, Part 1"。例によってIBM IDの登録が必要です。

注意点)
build.xmlを実行する箇所について。RAD上で右クリック、実行 > Antビルド としてもエラーとなります。コマンド・プロンプトからantを実行すると(別途Apache antの導入が必要)、問題なく実行できました。ちなみにCygwinでもダメです。"Program Files"以下に導入しているからかもしれません。

RunServer を実行する箇所について。実行するためには commons-logging.jar と commons-discovery.jar をビルドパスに追加する必要がありました。

WebサービスエクスプローラーからUDDIメインを開いた後の画面について。Tutorialとは違うようです。RAD7.5では、
レジストリー名:SayHelloService
照会 URL :http://localhost:8080/wsServerExample?wsdl
と入力します。

Webサービスエクスプローラーの使い勝手。ぜんぜん違いますね。いろいろいじってみましたが、とうとうgetGreetingsを呼び出すことができませんでした。

結論。大山鳴動ねずみ一匹、という感じです。まだまだだな。(<偉そう)

以上。
.

2008年11月30日日曜日

カワハギ、カワハギ

だって魚関係のWebサイトを見れば「カワハギといえば肝。肝は旨い」ばっかりですよ。@nifty:デイリーポータルでも取り上げられてるし。で、スーパーに行く途中のこと。しっかりカワハギ=肝=旨いと刷り込まれた私の脳は、目ざとく通りすがりの居酒屋のメニュー「カワハギの肝和え 800円」を発見。カワハギの肝和えか。高いな。旨いらしいな。

で、スーパーに行ってみたら魚コーナーに未処理のカワハギが。何かカワハギにインスパイアされまくりって感じで。魚コーナーのお兄ちゃんに「これ刺身いけますか」と聞くと「大丈夫ですよー」と威勢のよい答え。もう買うしかないでしょ。今日の晩御飯はカワハギの肝和えに決定、ということで、カワハギを一匹買って帰りました。子供には無理だから、メインというよりは大人向けのサブアイテム。

そして夕食。メインのコロッケを作るのに一生懸命になってしまい、カワハギのことはすっかり忘れてコロッケ食べてごちそうさまー。あー美味しかった。ってカワハギどうしよう。とよくあるパターン。

しょうがない、お父さんの夜のおやつはカワハギだ。と重い腰を上げて(それでも少し楽しみにしながら)カワハギの解体に向かいました。出刃でツノを切り落とし、口を切り落とす。皮をメリメリと剥いで、後頭部?こめかみ?あたりにざくっと包丁を入れて骨を断ち、そこから頭をもぎ取る。頭にくっついて肝が出てきました。他人のWebサイトで見ると肝だけきれいに取れているんですが、実際にはグダグダといろいろ張り付いている。写真はイメージで実物とは多少異なる場合がってやつですな。慎重に取り外したつもりでしたが少し崩れてしまった。肝は皿に乗せてしょうゆを掛けておきます。子供たちが「気持ち悪ーい」「これ何?」「苦玉ってどれ?」と騒ぎながら覗き込んでます。頼むから出刃包丁のそばに顔を近づけるな。それから身を三枚に下ろして(下手なので骨にしっかり肉がついてしまう・・・クヤシイ)から、中骨を避けてサク取りに。刺身包丁で小さく切って、肝と和えました。(子供はもう飽きてどっかで遊んでる。)

実家からもらった酒「越中五箇山 三笑楽」をコップにたっぷり一合分注ぎます。普段呑む酒と違って薄い琥珀色してて、香りとうまみが濃い。ちびりとすすって、デワデワ、とカワハギに箸をつける。

う~ん。見た目結構グロいっすよ。肝にはところどころ薄い血管がまとわりついてるし(多分解体が下手だった。もしくは取るか裏ごしするべきだったみたい)。身には薄皮がついてて(これも取り忘れた)、全体になんだかぴろぴろしてる感じ。

箸に肝をつけてちょっとなめてみると。うん。確かに旨い。結構旨い。生臭さは許容範囲。

肝にまみれた身を食べてみると、こりこりして旨い。これからは普通に好物って言うかも。カワハギの肝和え行っとく?みたいな。高いから自分で作るか。

高級そうな店で、小ざかしい器にちょこんと盛られてきたら、その旨さに感動してしまうかもしれません。形のそろわない刺身にところどころ赤い血管が張り付いている、その時点で負けだったかも。でも逆に言えばその見た目にもかかわらず旨いことは確か。

演出がまずかったな。あとは技術的な失敗もいくつかあったように思われました。以下が次回の課題。

・生臭さを取るため肝には酒を振りかけておくこと
・しょうがの絞り汁を入れたい
・血管は取り除いておくこと
・身から薄皮を取ること
・刺身は形をそろえて薄く切ること(いっそ軽く叩くか)

また食べようと思ってます。

以上。
.

2008年11月28日金曜日

偽善とか自己満足という言葉が気になっていた

気になりますよね。え?気にしたことなんかないって?そうか・・・私だけか。時々妙に「自己満足?偽善?」って考えこんじゃうんだけどな。

確かに青臭い同士学生の議論じゃあるまいし、大人になってしまえば「それはお前の自己満足だ」とか「偽善だ」などと決め付けられる機会も滅多にありませんね。それにたとえ他人から決め付けられるても「それがどーした」「ほっとけ」で済みます。オレが満足しようがしまいがお前の知ったことか。2ちゃんかどこかの標語にありましたが「しない善よりする偽善」。客観的な成果がでればいいじゃないか。だいたいお前には善が分かっているのか。お前はブッシュ大統領か、と。

そもそも、「自己満足」と「偽善」という言葉を平然と並べているが、関係あるのか、と。一括りにして語る意味があるのか。そんな声も聞こえてきそうです。

う~ん。そのとおりかもしれません。でも引っかかる。昔から幾度か反芻させられてきた言葉なんです。非難と若干の責める側の優越感を漂わせたこの言葉。「自己満足で終わってないか?」「それは偽善じゃないのか?」

なぜそれほどまでにこの言葉に引っかかるんだろう。ちょっと考えてみました。

両者の共通点って何だろう。

それはこれらの言葉が二つとも、その程度で満足していいのか、それでいいと思っているのか、と人に根底的に問いかける意図を持って発せられる、まずはそこが共通する点であるように思えます(言葉の裏にひそむ悪意や揶揄は考えないことにしましょう。ストレートに問いかけられたとします)。

お前の人生それでいいのか?大きなお世話だといえばその通りですが、私はふと足を止めて考えてしまう。俺はこれまで正しいことをしてきたのだろうか。これでよかったのか?でも次の瞬間にその問いを忘れてしまう。今日中に完了しなければならない仕事がある。あるいは子供が呼んでいる。一時的に私はその問いを忘れてしまう。でも忘れたつもりでもその問いは心の奥深くに沈殿し、再び浮かび上がる機会をじっと待っている。

お前の人生それでいいのか?実に青臭い問いかけです。あるいは人生を知らない浅い問いなのかもしれません。昔は常に自問していたような気がします。これでいいんだろうか。俺は何をしたいんだろうか。何をすべきなんだろうか。でも年を取るにつれてそんな問いは忘れていった。仕事も収入もあり、養うべき家族もいる。いまさら根本的に反省する必要などない。それより日々働いて家族と暮らすので精一杯。

希望や挫折や将来の不安で押しつぶされそうになりつつ、何ごとかを成し遂げたいと考えていたかつての自分。むやみに焦り、イライラとしながら周りを軽蔑し、でも何が出来るわけでもなく独り悶々と過ごしていた昔の"俺"。そんな"俺"が今の私を見たら何と言うだろうか。今の人生でいいのか。それでお前は本当に良かったのか。そんなふうに厳しく問い詰めてくるのかもしれません。

昔の自分と今の自分が顔を見合わせたとき、恥ずかしそうに去って行くのは今の"私"の方なのでしょうか。それとも昔の"俺"?

「自己満足」とか「偽善」という言葉を聞くと、かつての青臭い自分を思い出すような気がする。それがこの言葉が引っかかる理由なのかも知れません。

生きて行くために失われるもの。それは失われる必然性があったのか、それとも失われるべきではなかったのか。

死ぬときに自分の人生を肯定できれば、それでいい。そんな気がします。
.

RAD v7.5 試用版 を使ってJSFとEJB3.0・・・(11)

値をサーバ側で取得します。

1. ListBeanに選択された値を扱う変数と処理を追加します。
2. list.jspを編集します(変数の追加・送信ボタンの追加)。
3. faces-config.xmlに画面遷移定義を追加します。

1. ListBeanに選択された値を扱う変数と処理を追加
package test.beans;

import java.util.List;

import javax.ejb.EJB;

import test.MySample;
import test.MySampleSessionLocal;

public class ListBean {
private static final long serialVersionUID = 1L;

List<MySample> mySamples = null;
// Stringにしないとエラーが出ます。理由はよく分かりません・・・
private String selectedIndex = null;

public String getSelectedIndex() {
return selectedIndex;
}

public void setSelectedIndex(String selectedIndex) {
this.selectedIndex = selectedIndex;
}

@EJB(name="MySample")
private MySampleSessionLocal mySampleSession;

public List<MySample> getMySamples() {
// まあ二回くらい呼ばれたっていいでしょう
if (mySamples == null) {
try {
List<MySample> tmpList = mySampleSession.listMySamples();
setMySamples(tmpList);
} catch (Exception e) {
//
}
}
return mySamples;
}

private void setMySamples(List<MySample> mySamples) {
// 読み取り専用
this.mySamples = mySamples;
}

public String executeTest() {
System.out.println("Selected index -> " + selectedIndex);
return "success";
}

}

2. list.jspを編集
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><%@page
language="java" contentType="text/html; charset=windows-31j"
pageEncoding="windows-31j"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="/WEB-INF/tld/mysample.tld" prefix="custom"%>
<html>
<head>
<title>list</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
</head>
<body>
<h2>リスト MySample</h2>
<f:view>
<h:form>
<h:dataTable value="#{listBean.mySamples}" var="item" border="1">
<h:column>
<f:facet name="header">
<h:outputText value="Select" />
</f:facet>
<custom:radioButton id="myRadioId1" name="myRadioCol"
overrideName="true" value="#{listBean.selectedIndex}"
itemValue="#{item.myid}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="ID" />
</f:facet>
<f:verbatim>id: </f:verbatim>
<h:outputText value="#{item.myid}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="VAL" />
</f:facet>
<f:verbatim>value: </f:verbatim>
<h:outputText value="#{item.myFlag}" />
</h:column>
</h:dataTable>
<h:commandButton value="実行"
action="#{listBean.executeTest}" />
</h:form>
</f:view>
</body>
</html>

3. faces-config.xmlに画面遷移定義を追加
 <navigation-rule>
<from-view-id>list.jsp</from-view-id>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>list.jsp</to-view-id>
</navigation-case>
</navigation-rule>

以上。
.

RAD v7.5 試用版 を使ってJSFとEJB3.0・・・(10)

一覧表示した行を、ラジオボタンで選択できるようにします。
残念ながらカスタムタグを開発する必要があります。
(JSTLでもこの手の実装に苦労した記憶が・・・どうして標準の機能に無いんだろう??)

以下のページを参考にしました。というか、一部型のエラーが出たりしたので、変数をStringからObjectに変更してtoString()してますが、ほとんどそのまんまです。(感謝)

※RAD環境ではweb.xmlへのtaglib要素の追加は不要でした。web-app_2_5.xsdだから?よく分かってません。

Group radio buttons inside a JSF dataTable component
Write a custom JSF tag for enhancing radio button behavior
By Srijeeb Roy, JavaWorld.com, 02/27/07
(JAVAWORLD)
http://www.javaworld.com/javaworld/jw-02-2007/jw-02-jsf.html

ずいぶんとdeprecatedになってしまっているようですが、Webをダラダラ調べたところ、気にせず使うのが一番いいみたい。まだこれといった代替方法は確立されていないようです。

■準備
MySampleJSFの中にそのまま作ってもよいのですが、jarに分けたほうがカッコよいのでユーティリティ・モジュールプロジェクトとして作成します。

まずファイル > 新規 > その他 > 新規Javaユーティリティ・モジュールプロジェクト > "MySampleCTG" を作成。

MySampleJSF のプロパティを開き、Java EE モジュール依存関係 > Webライブラリータブ(J2EEモジュールではなく)を開いてMySampleCTGにチェックを入れます。

準備完了。

■カスタムタグの作成
以下のステップが必要です。結構大変ですね。

1. 以下の三つのクラスを作成します。
1-1. コンポーネント クラス(MySampleCTGプロジェクトに作成)
1-2. レンダー クラス(同上)
1-3. カスタム・タグ クラス(同上)
2. TLDファイルを作成します。(MySampleJSFプロジェクトに作成。MySampleCTGのMETA-INFではうまく行きませんでした)
3. faces-config.xmlにコンポーネント要素を追加します。(同上)
4. jspにタグを書きます。(同上)

1-1. コンポーネントクラス(UICustomSelectOneRadio)
package test.ctg;

import javax.el.ValueExpression;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;

public class UICustomSelectOneRadio extends UIInput {

// 1. 属性をクラス変数として定義
private String name = null;
private String overrideName = null;
private String styleClass = null;
private String style = null;
private String disabled = null;
private String itemLabel = null;
private Object itemValue = null;
private String onClick = null;
private String onMouseOver = null;
private String onMouseOut = null;
private String onFocus = null;
private String onBlur = null;

// 2. 動的に属性を取得するためのメソッドを定義
public String returnValueBindingAsString(String attr) {
// サンプルコードは一部Deprecatedだったので適宜変更しています。
ValueExpression valueExpression = getValueExpression(attr);
if (valueExpression != null) {
return valueExpression.getValue(this.getFacesContext()
.getELContext()).toString();
} else {
return null;
}
}

// 3. setter, getterを定義. getterでreturnValueBindingAsStringを使っている
// ので注意
public String getName() {
if (name != null) {
return name;
}
return returnValueBindingAsString("name");

}

public void setName(String name) {
this.name = name;
}

public String getOverrideName() {
if (overrideName != null) {
return overrideName;
}
return returnValueBindingAsString("overrideName");
}

public void setOverrideName(String overrideName) {
this.overrideName = overrideName;
}

public String getStyleClass() {
if (styleClass != null) {
return styleClass;
}
return returnValueBindingAsString("styleClass");
}

public void setStyleClass(String styleClass) {
this.styleClass = styleClass;
}

public String getStyle() {
if (style != null) {
return style;
}
return returnValueBindingAsString("style");
}

public void setStyle(String style) {
this.style = style;
}

public String getDisabled() {
if (disabled != null) {
return disabled;
}
return returnValueBindingAsString("disabled");
}

public void setDisabled(String disabled) {
this.disabled = disabled;
}

public String getItemLabel() {
if (itemLabel != null) {
return itemLabel;
}
return returnValueBindingAsString("itemLabel");
}

public void setItemLabel(String itemLabel) {
this.itemLabel = itemLabel;
}

public Object getItemValue() {
if (itemValue != null) {
return itemValue;
}
return returnValueBindingAsString("itemValue");
}

public void setItemValue(Object itemValue) {
this.itemValue = itemValue;
}

public String getOnClick() {
if (onClick != null) {
return onClick;
}
return returnValueBindingAsString("onClick");
}

public void setOnClick(String onClick) {
this.onClick = onClick;
}

public String getOnMouseOver() {
if (onMouseOver != null) {
return onMouseOver;
}
return returnValueBindingAsString("onMouseOver");
}

public void setOnMouseOver(String onMouseOver) {
this.onMouseOver = onMouseOver;
}

public String getOnMouseOut() {
if (onMouseOut != null) {
return onMouseOut;
}
return returnValueBindingAsString("onMouseOut");
}

public void setOnMouseOut(String onMouseOut) {
this.onMouseOut = onMouseOut;
}

public String getOnFocus() {
if (onFocus != null) {
return onFocus;
}
return returnValueBindingAsString("onFocus");
}

public void setOnFocus(String onFocus) {
this.onFocus = onFocus;
}

public String getOnBlur() {
if (onBlur != null) {
return onBlur;
}
return returnValueBindingAsString("onBlur");
}

public void setOnBlur(String onBlur) {
this.onBlur = onBlur;
}

// 4. restoreStateとsaveStateメソッドを実装します。
@Override
public void restoreState(FacesContext context, Object state) {
Object[] values = (Object[]) state;
super.restoreState(context, values[0]);
styleClass = (String) values[1];
style = (String) values[2];
disabled = (String) values[3];
itemLabel = (String) values[4];
itemValue = (String) values[5];
onClick = (String) values[6];
onMouseOver = (String) values[7];
onMouseOut = (String) values[8];
onFocus = (String) values[9];
onBlur = (String) values[10];
name = (String) values[11];
overrideName = (String) values[12];
}
@Override
public Object saveState(FacesContext context) {
Object[] values = new Object[13];
values[0] = super.saveState(context);
values[1] = styleClass;
values[2] = style;
values[3] = disabled;
values[4] = itemLabel;
values[5] = itemValue;
values[6] = onClick;
values[7] = onMouseOver;
values[8] = onMouseOut;
values[9] = onFocus;
values[11] = name;
values[12] = overrideName;
return (values);
}

// 5. getFamilyを実装します。faces-config.xmlにこの名前を指定します。
@Override
public String getFamily() {
return ("CustomSelectOneRadio");
}
}

次はレンダークラス(HTMLCustomSelectOneRadioRenderer)です。
package test.ctg;

import java.io.IOException;
import java.util.Map;

import javax.faces.component.UIComponent;
import javax.faces.component.UIData;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;

public class HTMLCustomSelectOneRadioRenderer extends Renderer {

@SuppressWarnings("unchecked")
@Override
public void decode(FacesContext context, UIComponent component) {
if ((context == null) || (component == null)) {
throw new NullPointerException();
}
UICustomSelectOneRadio aUICustomSelectOneRadio = null;
if (component instanceof UICustomSelectOneRadio) {
aUICustomSelectOneRadio = (UICustomSelectOneRadio) component;
} else {
return;
}
Map map = context.getExternalContext().getRequestParameterMap();
String name = getName(aUICustomSelectOneRadio, context);
if (map.containsKey(name)) {
String value = map.get(name).toString();
if (value != null) {
setSubmittedValue(component, value);
}
}
}

public void setSubmittedValue(UIComponent component, Object obj) {

if (component instanceof UIInput) {
((UIInput) component).setSubmittedValue(obj);
}
}

private String getName(UICustomSelectOneRadio aUICustomSelectOneRadio,
FacesContext context) {

UIComponent parentUIComponent = getParentDataTableFromHierarchy(aUICustomSelectOneRadio);
if (parentUIComponent == null) {
return aUICustomSelectOneRadio.getClientId(context);
} else {
if (aUICustomSelectOneRadio.getOverrideName() != null
&& aUICustomSelectOneRadio.getOverrideName().equals("true")) {
return aUICustomSelectOneRadio.getName();
} else {
String id = aUICustomSelectOneRadio.getClientId(context);
int lastIndexOfColon = id.lastIndexOf(":");
String partName = "";
if (lastIndexOfColon != -1) {
partName = id.substring(0, lastIndexOfColon + 1);
if (aUICustomSelectOneRadio.getName() == null) {
partName = partName + "generatedRad";
} else
partName = partName + aUICustomSelectOneRadio.getName();
}
return partName;
}
}
}

private UIComponent getParentDataTableFromHierarchy(UIComponent uiComponent) {
if (uiComponent == null) {
return null;
}
if (uiComponent instanceof UIData) {
return uiComponent;
} else {
// try to find recursively in the Component tree hierarchy
return getParentDataTableFromHierarchy(uiComponent.getParent());
}
}

@Override
public void encodeEnd(FacesContext context, UIComponent component)
throws IOException {
if ((context == null) || (component == null)) {
throw new NullPointerException();
}

UICustomSelectOneRadio aUICustomSelectOneRadio = (UICustomSelectOneRadio) component;

if (component.isRendered()) {
ResponseWriter writer = context.getResponseWriter();

writer.write("<input type=\"radio\"");

writer.write(" id=\"" + component.getClientId(context) + "\"");
writer.write(" name=\"" + getName(aUICustomSelectOneRadio, context)
+ "\"");
if (aUICustomSelectOneRadio.getStyleClass() != null
&& aUICustomSelectOneRadio.getStyleClass().trim().length() > 0) {
writer
.write(" class=\""
+ aUICustomSelectOneRadio.getStyleClass()
.trim() + "\"");
}
if (aUICustomSelectOneRadio.getStyle() != null
&& aUICustomSelectOneRadio.getStyle().trim().length() > 0) {
writer.write(" style=\""
+ aUICustomSelectOneRadio.getStyle().trim() + "\"");
}
if (aUICustomSelectOneRadio.getDisabled() != null
&& aUICustomSelectOneRadio.getDisabled().trim().length() > 0
&& aUICustomSelectOneRadio.getDisabled().trim().equals(
"true")) {
writer.write(" disabled=\"disabled\"");
}
if (aUICustomSelectOneRadio.getItemValue() != null) {
writer.write(" value=\""
+ aUICustomSelectOneRadio.getItemValue().toString().trim() + "\"");
}
if (aUICustomSelectOneRadio.getOnClick() != null
&& aUICustomSelectOneRadio.getOnClick().trim().length() > 0) {
writer.write(" onclick=\""
+ aUICustomSelectOneRadio.getOnClick().trim() + "\"");
}
if (aUICustomSelectOneRadio.getOnMouseOver() != null
&& aUICustomSelectOneRadio.getOnMouseOver().trim().length() > 0) {
writer.write(" onmouseover=\""
+ aUICustomSelectOneRadio.getOnMouseOver().trim()
+ "\"");
}
if (aUICustomSelectOneRadio.getOnMouseOut() != null
&& aUICustomSelectOneRadio.getOnMouseOut().trim().length() > 0) {
writer
.write(" onmouseout=\""
+ aUICustomSelectOneRadio.getOnMouseOut()
.trim() + "\"");
}
if (aUICustomSelectOneRadio.getOnFocus() != null
&& aUICustomSelectOneRadio.getOnFocus().trim().length() > 0) {
writer.write(" onfocus=\""
+ aUICustomSelectOneRadio.getOnFocus().trim() + "\"");
}
if (aUICustomSelectOneRadio.getOnBlur() != null
&& aUICustomSelectOneRadio.getOnBlur().trim().length() > 0) {
writer.write(" onblur=\""
+ aUICustomSelectOneRadio.getOnBlur().trim() + "\"");
}

if (aUICustomSelectOneRadio.getValue() != null
&& aUICustomSelectOneRadio.getValue().equals(
aUICustomSelectOneRadio.getItemValue())) {
writer.write(" checked=\"checked\"");
}
writer.write(">");
if (aUICustomSelectOneRadio.getItemLabel() != null) {
writer.write(aUICustomSelectOneRadio.getItemLabel());
}
writer.write("</input>");
}
}
}

■カスタム・タグクラス(HTMLCustomSelectOneRadioTag)
package test.ctg;

import javax.faces.FactoryFinder;
import javax.faces.application.Application;
import javax.faces.application.ApplicationFactory;
import javax.faces.component.UIComponent;
import javax.faces.el.ValueBinding;
import javax.faces.webapp.UIComponentELTag;
import javax.faces.webapp.UIComponentTag;

public class HTMLCustomSelectOneRadioTag extends UIComponentELTag {

public HTMLCustomSelectOneRadioTag() {
name = null;
value = null;
styleClass = null;
style = null;
disabled = null;
itemLabel = null;
itemValue = null;
onClick = null;
onMouseOver = null;
onMouseOut = null;
onFocus = null;
onBlur = null;
overrideName = null;
}

public String getDisabled() {
return disabled;
}

public String getItemLabel() {
return itemLabel;
}

public String getItemValue() {
return itemValue;
}

public String getName() {
return name;
}

public String getOnBlur() {
return onBlur;
}

public String getOnClick() {
return onClick;
}

public String getOnFocus() {
return onFocus;
}

public String getOnMouseOut() {
return onMouseOut;
}

public String getOnMouseOver() {
return onMouseOver;
}

public String getOverrideName() {
return overrideName;
}

public String getStyle() {
return style;
}

public String getStyleClass() {
return styleClass;
}

public String getValue() {
return value;
}

public void setDisabled(String string) {
disabled = string;
}

public void setItemLabel(String string) {
itemLabel = string;
}

public void setItemValue(String string) {
itemValue = string;
}

public void setName(String string) {
name = string;
}

public void setOnBlur(String string) {
onBlur = string;
}

public void setOnClick(String string) {
onClick = string;
}

public void setOnFocus(String string) {
onFocus = string;
}

public void setOnMouseOut(String string) {
onMouseOut = string;
}

public void setOnMouseOver(String string) {
onMouseOver = string;
}

public void setOverrideName(String string) {
overrideName = string;
}

public void setStyle(String string) {
style = string;
}

public void setStyleClass(String string) {
styleClass = string;
}

public void setValue(String string) {
value = string;
}

protected void setProperties(UIComponent component) {
super.setProperties(component);
UICustomSelectOneRadio aUICustomSelectOneRadio =
(UICustomSelectOneRadio) component;
if (name != null) {
if (UIComponentTag.isValueReference(name)) {
aUICustomSelectOneRadio.setValueBinding("name",
getValueBinding(name));
} else {
aUICustomSelectOneRadio.getAttributes().put("name", name);
}
}
if (value != null)
if (UIComponentTag.isValueReference(value))
aUICustomSelectOneRadio.setValueBinding("value",
getValueBinding(value));
else
aUICustomSelectOneRadio.getAttributes().put("value", value);
if (styleClass != null)
if (UIComponentTag.isValueReference(styleClass))
aUICustomSelectOneRadio.setValueBinding("styleClass",
getValueBinding(styleClass));
else
aUICustomSelectOneRadio.getAttributes().put("styleClass",
styleClass);
if (style != null)
if (UIComponentTag.isValueReference(style))
aUICustomSelectOneRadio.setValueBinding("style",
getValueBinding(style));
else
aUICustomSelectOneRadio.getAttributes().put("style", style);
if (disabled != null)
if (UIComponentTag.isValueReference(disabled))
aUICustomSelectOneRadio.setValueBinding("disabled",
getValueBinding(disabled));
else
aUICustomSelectOneRadio.getAttributes().put("disabled",
disabled);
if (itemLabel != null)
if (UIComponentTag.isValueReference(itemLabel)) {
aUICustomSelectOneRadio.setValueBinding("itemLabel",
getValueBinding(itemLabel));
} else {
System.out.println("itemLabel=" + itemLabel);
aUICustomSelectOneRadio.getAttributes().put("itemLabel",
itemLabel);
}
if (itemValue != null)
if (UIComponentTag.isValueReference(itemValue))
aUICustomSelectOneRadio.setValueBinding("itemValue",
getValueBinding(itemValue));
else
aUICustomSelectOneRadio.getAttributes().put("itemValue",
itemValue);
if (onClick != null)
if (UIComponentTag.isValueReference(onClick))
aUICustomSelectOneRadio.setValueBinding("onClick",
getValueBinding(onClick));
else
aUICustomSelectOneRadio.getAttributes().put("onClick", onClick);
if (onMouseOver != null)
if (UIComponentTag.isValueReference(onMouseOver))
aUICustomSelectOneRadio.setValueBinding("onMouseOver",
getValueBinding(onMouseOver));
else
aUICustomSelectOneRadio.getAttributes().put("onMouseOver",
onMouseOver);
if (onMouseOut != null)
if (UIComponentTag.isValueReference(onMouseOut))
aUICustomSelectOneRadio.setValueBinding("onMouseOut",
getValueBinding(onMouseOut));
else
aUICustomSelectOneRadio.getAttributes().put("onMouseOut",
onMouseOut);
if (onFocus != null)
if (UIComponentTag.isValueReference(onFocus))
aUICustomSelectOneRadio.setValueBinding("onFocus",
getValueBinding(onFocus));
else
aUICustomSelectOneRadio.getAttributes().put("onFocus", onFocus);
if (onBlur != null)
if (UIComponentTag.isValueReference(onBlur))
aUICustomSelectOneRadio.setValueBinding("onBlur",
getValueBinding(onBlur));
else
aUICustomSelectOneRadio.getAttributes().put("onBlur", onBlur);
if (overrideName != null)
if (UIComponentTag.isValueReference(overrideName))
aUICustomSelectOneRadio.setValueBinding("overrideName",
getValueBinding(overrideName));
else
aUICustomSelectOneRadio.getAttributes().put("overrideName",
overrideName);
}

public ValueBinding getValueBinding(String valueRef) {
ApplicationFactory af = (ApplicationFactory) FactoryFinder
.getFactory("javax.faces.application.ApplicationFactory");
Application a = af.getApplication();
return a.createValueBinding(valueRef);
}

private String name;
private String value;
private String styleClass;
private String style;
private String disabled;
private String itemLabel;
private String itemValue;
private String onClick;
private String onMouseOver;
private String onMouseOut;
private String onFocus;
private String onBlur;
private String overrideName;

@Override
public String getComponentType() {
return "component.CustomSelectOneRadio";
}

@Override
public String getRendererType() {
return "renderer.CustomSelectOneRadio";
}
}

2. mysample.tldファイルです。
/WEB-INF/tldに置いてください。
<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE taglib
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">

<taglib>
<!-- ============== Tag Library Description Elements ============= -->
<tlib-version>0.03</tlib-version>
<jsp-version>1.2</jsp-version>
<short-name>Custom JavaServerFaces Framework Tag Library</short-name>
<uri>http://www.srijeeb.com/jsf/tags-html</uri>
<tag>
<name>radioButton</name>
<tag-class>test.ctg.HTMLCustomSelectOneRadioTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>name</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>overrideName</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>id</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>value</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>styleClass</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>style</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>disabled</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>rendered</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>Boolean</type>
</attribute>
<attribute>
<name>itemLabel</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>itemValue</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>onClick</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>onMouseOver</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>onMouseOut</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>onFocus</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
<attribute>
<name>onBlur</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>String</type>
</attribute>
</tag>
</taglib>

重要:TLDファイルをMySampleCTG/src/META-INF配下に置いたらうまく動きませんでした。web.xmlとかJSPをいろいろいじってみたのですがダメでした。

エラー例)
com.ibm.ws.jsp.inputsource.JspInputSourceImpl JspInputSourceImpl Failed to create inputsource contextURL =[file:/C:/Documents and Settings/user_name/IBM/rationalsdp/workspace/MySampleJSF/WebContent/ relativeURL =[http://www.srijeeb.com/jsf/tags-html]
java.net.MalformedURLException
at java.net.URL.<init>(URL.java:601)
...

おそらくそれと関連してると思われるのですが、WASテスト環境の調子も悪くなりました。
[08/11/27 13:14:21:000 JST] 0000001e annotations E ArchiveInputStreamData iterate unable to open file iterator
java.util.zip.ZipException: ZipFile closed
...のようなエラーが毎回出るようになった・WASの「公開」プロセスが0%のまま進まない、など。

ユーティリティプロジェクトは動作が怪しいですね。はっきり言って使わないほうがいいと思います。(昔のWSADでも動作が怪しかった記憶があります)
重要:ClassNotFoundエラーが出たら、MySampleCTGフォルダを右クリック Java EE > EARライブラリの更新 をしたら直ることがあります。MySampleCTG配下を編集したら「EARライブラリの更新」を実行するようにしましょう。

3. faces-config.xmlにコンポーネント要素を追加します。
以下の要素を追加してください。
 <component>
<component-type>component.CustomSelectOneRadio</component-type>
<component-class>test.ctg.UICustomSelectOneRadio
</component-class>
<component-extension>
<component-family>CustomSelectOneRadio</component-family>
<renderer-type>renderer.CustomSelectOneRadio</renderer-type>
</component-extension>
</component>
<render-kit>
<renderer>
<component-family>CustomSelectOneRadio</component-family>
<renderer-type>renderer.CustomSelectOneRadio</renderer-type>
<renderer-class>test.ctg.HTMLCustomSelectOneRadioRenderer
</renderer-class>
</renderer>
</render-kit>

4. jspにタグを書きます。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><%@page
language="java" contentType="text/html; charset=windows-31j"
pageEncoding="windows-31j"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="/WEB-INF/tld/mysample.tld" prefix="custom"%>
<html>
<head>
<title>list</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
</head>
<body>
<h2>リスト MySample</h2>
<f:view>
<h:dataTable value="#{listBean.mySamples}" var="item" border="1">
<h:column>
<f:facet name="header">
<h:outputText value="Select" />
</f:facet>
<custom:radioButton id="myRadioId1" name="myRadioCol"
overrideName="true" value="#{item.myid}"
itemValue="#{item.myid}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="ID" />
</f:facet>
<f:verbatim>id: </f:verbatim>
<h:outputText value="#{item.myid}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="VAL" />
</f:facet>
<f:verbatim>value: </f:verbatim>
<h:outputText value="#{item.myFlag}" />
</h:column>
</h:dataTable>
</f:view>
</body>
</html>s

以上です。表示だけはできるようになったはず。
(やれやれ。何で標準機能にしないんだろう・・・)

次回、radioボタンで送信されたデータを取得します。
.

2008年11月27日木曜日

RAD v7.5 試用版 を使ってJSFとEJB3.0・・・(9)

今度はテーブルの内容を一覧表示します。

■EJB(MySampleSession)に一覧(java.util.List)を取得するメソッドを追加します。
    // List<MySample>のwarningを抑止しています。それ以外は来ないはずなので。
@SuppressWarnings("unchecked")
public List<MySample> listMySamples() throws MySampleException {
Query query = em.createQuery("select m from MySample m");
List<MySample> result = query.getResultList();
return result;

}

■Webプロジェクト(MySampleJSF)に、新規にListBeanを追加します。
package test.beans;

import java.util.List;

import javax.ejb.EJB;

import test.MySample;
import test.MySampleSessionLocal;

public class ListBean {
private static final long serialVersionUID = 1L;
/*
* JPA直は好きじゃないのですが今回はこれで。
*/
List<MySample> mySamples = null;

@EJB(name="MySample")
private MySampleSessionLocal mySampleSession;

public List<MySample> getMySamples() {
// nullチェックでは厳密にインスタンスの生成を制御できませんが、
// まあ二回くらい呼ばれたっていいでしょう
if (mySamples == null) {
try {
List<MySample> tmpList = mySampleSession.listMySamples();
setMySamples(tmpList);
} catch (Exception e) {
//
}
}
return mySamples;
}

private void setMySamples(List<MySample> mySamples) {
this.mySamples = mySamples;
}
}

■faces-config.xmlにmanagedBean要素を追加します。
  <managed-bean>
<managed-bean-name>listBean</managed-bean-name>
<managed-bean-class>test.beans.ListBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>

■list.jspを新規に作成します。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><%@page
language="java" contentType="text/html; charset=windows-31j"
pageEncoding="windows-31j"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %><html>
<head>
<title>list</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
</head>
<body>
<h2>リスト MySample</h2>
<f:view>
<h:dataTable value="#{listBean.mySamples}" var="item" border="1">
<h:column>
<f:verbatim>id: </f:verbatim>
<h:outputText value="#{item.myid}"/>
</h:column>
<h:column>
<f:verbatim>value: </f:verbatim>
<h:outputText value="#{item.myFlag}"/>
</h:column>
</h:dataTable>
</f:view>
</body>
</html>

今度は一覧表示にラジオボタンを追加したいのですが、これが大変なのです(<h:dataTable ..>と<h:selectOneMenu...>は入れ子にできないため・・・)。次回以降で挑戦してみます。

以上
.

RAD v7.5 試用版 を使ってJSFとEJB3.0・・・(8)

RAD v7.5 試用版 を使ってJSFとEJB3.0・・・(8)

手順

1. サンプルDBを作成
2. DBをWAS v7のデータソースに登録
3. JPAプロジェクトを作成
4. EJBを作成(findメソッドのみ)
5. JSFを作成
6. EJBに挿入メソッドを作成する
7. JSFを作成(挿入)
8. EJBに更新メソッドを作成する
9. JSFを作成(更新)
(前回)
10. EJBに削除メソッドを作成する
11. JSFを作成(削除)
(今回はここまで)
12. 未定。テーブルの内容の一覧表示とか

後は似たようなものです。どんどん行きます。

10. EJBに削除メソッドを作成する

■MySampleSessionにロジックを追加します。
    public void deleteMySample(int myid) throws MySampleException {
MySample target = this.findMySample(myid);
if (target == null) {
throw new MySampleException();
}
em.remove(target);
em.flush();
}

以上。UTCなどで動作確認することもできます。

11. JSFを作成(削除)

■MySampleBeanにロジックを追加します。
    public void deleteMySample(int myid) throws MySampleException {
MySample target = this.findMySample(myid);
if (target == null) {
throw new MySampleException();
}
em.remove(target);
em.flush();
}

JSPは全てこれまでのものを流用します。
■success.jspに削除ボタンを追加します。
Found bean.<br>
id -> ${ mySampleBean.id } <h:inputHidden value="#{mySampleBean.id}"/>
value -> <h:inputText value="#{mySampleBean.value}"/>
<br>
<h:commandButton value="更新"
action="#{mySampleBean.updateValue}" />
<h:commandButton value="削除"
action="#{mySampleBean.deleteValue}" />

■faces-config.xmlを編集します(削除が成功したら検索画面に戻ります)。
  <navigation-rule>
<from-view-id>success.jsp</from-view-id>
<navigation-case>
<from-outcome>failure</from-outcome>
<to-view-id>/WEB-INF/results/failure.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/WEB-INF/results/success3.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>del_failure</from-outcome>
<to-view-id>/WEB-INF/results/failure.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>del_success</from-outcome>
<to-view-id>/search.jsp</to-view-id>
</navigation-case>
</navigation-rule>

以上。
.

2008年11月26日水曜日

痛恨の片思い

一方的に仲良くしたい、親しみたいと思っていろいろアプローチしても、どうも相手のほうはその気になってくれないというか、うまく行かないということはよくありますが、私の場合、そのお相手は豚バラブロックの大きなかたまり肉でした。

昔からどうもこのほとんど脂身じゃねーかコノー、という巨大なかたまりに対しては、憎しみと共に愛情、あるいはほのかな憧れを抱いていたのです。憎たらしい脂身だけれども、いったいどうやったらこれを美味しく頂けるんだろう。スーパーでカナダ産やアメリカ産の豚バラブロック肉を見るにつけ、なんだか挑発されているような煽られているような気がしてならなかったのです。

妻は飽和脂肪酸とか動物性油脂や加工油脂とかを嫌がるので、ブロックだろうが薄切りだろうが豚バラ肉を嫌います。実は私だって脂身はさほど好きではない。豚ならバラよりヒレ肉。鳥ならモモ肉よりもムネやささみを好むほうです。しかしどうにもこの白い脂のかたまりは、私の本能を突き動かしてしょうがない。DNAレベルで揺さぶられている気すらしてしまう。

一部で話題の飽和脂肪酸。体によくないらしいけれども、沖縄では皮付きの豚ばら肉を泡盛で煮込んで黒糖としょうゆで煮付け、美味しい角煮を作っているらしい。つまり、沖縄では豚バラの角煮が食べられている。しかるに沖縄は長寿で有名である。すなわち豚バラの角煮を食べているにも関わらず、沖縄の人は長生きする。つまり、角煮を食べてもそれほど体に悪いことはないかしんない、という三段論法がここに成立するわけです。

昔テレビでやっていた「どっちの料理ショー」でも、よく肉からタラーリタラリと垂れる脂を見て歓声が上がっていたような気がします。しかも脂をたっぷり口に含んで「ジューシーで美味しい!」とか「甘~い」とか。脂舐めてうまいとは何ごとであるか、と半分怒りながらも見ていた私でしたが、豚ばら肉のレシピを見ると「最初にしっかり焼いて余分な脂を落とす」とか「酒を入れたお湯でしっかり茹でてから調理する」という記載があり「余分な脂が抜けて*サッパリした*美味しい豚バラの角煮」というものが存在しているかのように信じてしまい、いつかはそのような角煮を作って見たいものだ、などと考えていました。

で、一度作ってみたんですよ。もうしばらく昔になりますけど。しっかり焼いて脂も落としたし(60%くらいの大きさに縮まった)、味つけする前にお湯で1時間以上茹でたりもしました。でも、結果はやっぱり豚の脂。脂は脂。冷えるとにっちゃり。確かに美味しかったですけどね。でもニチャニチャ。

これに懲りてしばらくこの人には手を出さなかったんですが、またこの間また挑戦してみたのです。塩豚に。しかも塩豚を燻製にして自家製ベーコン を作ってみようと。よりによってというかアチャーという行為ですが、片思いから逃れられない私。恋は盲目です。

結論から言いますと、これが大失敗。いや、多分料理としては正当な手順でそこそこ正当な結果が得られたと思うんですが、その出来上がったものがまさに煙で燻されたしょっぱい豚脂。もう一口で勘弁って感じでした。頑張って半分ほど使いましたが、一週間ほど経過して消費できなかった残りは迷わずゴミ箱へダンクシュート。普段は食べ物を捨てるなんてことはしないんですよ。賞味期限+1、2日経過のものは平気だし、ものによっては味噌や塩に漬け込んで延命を図り、ちゃんと消費する性格です。食べ物は粗末にしたくない。でも塩豚の燻製はアウトでしたね。もう二度と作らない。

というわけで、ようやく豚バラの呪いから開放されたと思った私ですが、この前、業務用スーパーで巨大な豚バラかたまり肉(2,3kgもあろうかというやつ)を見かけて瞳孔を拡張しながら足を止めてしまいました。なんて懲りない私でしょうか。もう二度と自分で買うことはないでしょう。ないと思います。しかし、この豚バラのかたまりは、当分の間私を悩ませ続けるに違いない。巨大な生白いエロティックな脂肪の塊から目を離すことができないまま、私はそのように思ったのであります。

以上。
.

2008年11月25日火曜日

RAD v7.5 試用版 を使ってJSFとEJB3.0・・・(7)

手順

1. サンプルDBを作成
2. DBをWAS v7のデータソースに登録
3. JPAプロジェクトを作成
4. EJBを作成(findメソッドのみ)
5. JSFを作成
6. EJBに挿入メソッドを作成する
7. JSFを作成(挿入)
8. EJBに更新メソッドを作成する
(前回)
9. JSFを作成(更新)
(今回はここまで)
10. EJBに削除メソッドを作成する
11. JSFを作成(削除)

9. JSFを作成(更新)

JSFに更新ページを作成します。

■MySampleBeanにロジックを追加します。
 public String updateValue() {
try {
mySampleSession.updateMySample(id, value);
} catch(Exception e) {
return "failure";
}
return "success";
}

これだけ。

■次はJSP。
以前に作った検索(search.jsp)、結果表示ページ(/WEB-INF/results/success.jsp)を再利用します。

まず旧結果表示ページを/WEB-INF以外の場所に移動し、編集します。
なぜ移動するか。
以前に紹介したチュートリアルのPage-Navigation-1.pdfに以下の記述がありました。
Strategies
- You cannot put input-form JSP pages in WEB-INF
- Because URL must correspond directly to file location

そのまんまですね。この人はJSFの欠陥だ、と言い切ってます。私もそう思うな。

idはそのまま表示し(hidden要素に入れておく)、valueはテキストボックスに表示します。
Found bean.<br>
id -> ${ mySampleBean.id } <h:inputHidden value="#{mySampleBean.id}"/>
value -> <h:inputText value="#{mySampleBean.value}"/><br>
<h:commandButton value="更新"
action="#{mySampleBean.updateValue}" />

こんな感じで。

次に新しい方の結果表示ページ(更新結果を表示するページ)を作成します。
/WEB-INF/results/success3.jspとします。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><%@page
language="java" contentType="text/html; charset=windows-31j"
pageEncoding="windows-31j"%>
<html>
<head>
<title>exists</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
</head>
<body>
Updated bean.<br>
id -> ${ mySampleBean.id }
value -> ${ mySampleBean.value }
</body>
</html>

次にfaces-config.xmlです。以下のタグを適切な場所に追加します。
  <navigation-rule>
<!-- この success.jsp が /WEB-INF/results の下にあるとダメな分けです -->
<from-view-id>success.jsp</from-view-id>
<navigation-case>
<from-outcome>failure</from-outcome>
<to-view-id>/WEB-INF/results/failure.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/WEB-INF/results/success3.jsp</to-view-id>
</navigation-case>
</navigation-rule>

以上。
.

RAD v7.5 試用版 を使ってJSFとEJB3.0・・・(6)

手順

1. サンプルDBを作成
2. DBをWAS v7のデータソースに登録
3. JPAプロジェクトを作成
4. EJBを作成(findメソッドのみ)
5. JSFを作成
6. EJBに挿入メソッドを作成する
7. JSFを作成(挿入)
(前回)
8. EJBに更新メソッドを作成する
(今回はここまで)
9. JSFを作成(更新)
10. EJBに削除メソッドを作成する
11. JSFを作成(削除)

8. EJBに更新メソッドを作成する

EJBに更新メソッドを作成します。

以下のメソッドをMySampleSession セッションBeanに追加します。
 public void updateMySample(int myid, String value) throws MySampleException {
if (value.length() > 1) {
throw new MySampleException();
}
MySample target = (MySample)em.find(MySample.class, myid);
if (target == null) {
throw new MySampleException();
}
target.setMyFlag(value);
em.persist(target);
em.flush();
}

アウトラインでupdateMySampleメソッドを右クリックし、Java EE ツール > メソッドのプロモート を実行します。(忘れがちなので注意しましょう)

これでUTCからテストが出来ます(テスト方法このシリーズで何度か書いたので割愛します)。ちゃんと動くか確認してみて下さい。(サクサク行きますねえ)
.

プリキュア映画版は面白かった

やはり面白いですね。映画版プリキュア。普段は地味な主役もちゃんと活躍してたし。カッコよかったぜ、ていい歳ぶっこいて何言ってんだ、という照れもありつつも。

やはりおっさんが若い女子の活躍する熱血青春アニメを面白がるというのも恥ずかしいもんで、そこは勘弁して頂きたい。

しかし、今回は結構感情移入して見て楽しかったですよ。これまでは「よく出来ている」「面白い」とは思いつつも、客観的スタンスを崩していなかったのが、映画では結構引き込まれた。どうしてかな。悪役がどっかマヌけなモチベーションを持っていたのが良かったと思うな。

まあ、小さい子供を持つお父さんもぜひ一緒に楽しんできてください。面白いから。多分ね。

.

RAD v7.5 試用版 を使ってJSFとEJB3.0・・・(5)

手順

1. サンプルDBを作成
2. DBをWAS v7のデータソースに登録
3. JPAプロジェクトを作成
4. EJBを作成(findメソッドのみ)
5. JSFを作成
6. EJBに挿入メソッドを作成する
(前回)
7. JSFを作成(挿入)
(今回はここまで)
8. EJBに更新メソッドを作成する
9. JSFを作成(更新)

7. JSFを作成(挿入)

挿入を受け付けるJSF画面を追加します。(行き当たりばったりの画面設計・・・)

JSFの観点から言えばNothing Specialです。淡々と行きます。

■insert.jsp
<%@page language="java" contentType="text/html; charset=windows-31j"
pageEncoding="windows-31j"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<f:view>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>挿入</title>
<meta http-equiv="Content-Type"
content="text/html; charset=windows-31j">
</head>
<body>
<CENTER>
<h2>挿入</h2>
<h:form>
myid:
<h:inputText value="#{mySampleBean.id}" />
value:
<h:inputText value="#{mySampleBean.value}" />
<h:commandButton value="挿入"
action="#{mySampleBean.insertValue}" />
</h:form>
</CENTER>
</body>
</html>
</f:view>

■/WEB-INF/result/failure2.jsp(重複キーエラーの時に表示させます)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><%@page
language="java" contentType="text/html; charset=windows-31j"
pageEncoding="windows-31j"%>
<html>
<head>
<title>exists</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
</head>
<body>
Duplicate key exists. Could not insert.
</body>
</html>

■/WEB-INF/results/failure2-1.jsp(その他のシステムエラー用です。UTできませんが念のため)

/WEB-INF/result.failure2.jspの"Duplicate key exists. Could not insert."を"System error"に変えるだけ

■/WEB-INF/reusults/success2.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><%@page
language="java" contentType="text/html; charset=windows-31j"
pageEncoding="windows-31j"%>
<html>
<head>
<title>exists</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
</head>
<body>
Inserted bean.<br>
id -> ${ mySampleBean.id }
value -> ${ mySampleBean.value }
</body>
</html>

■faces-config.xml
以下を追加
  <navigation-rule>
<from-view-id>/insert.jsp</from-view-id>
<navigation-case>
<from-outcome>failure2</from-outcome>
<to-view-id>/WEB-INF/results/failure2.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>failure2-1</from-outcome>
<to-view-id>/WEB-INF/results/failure2-1.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>success2</from-outcome>
<to-view-id>/WEB-INF/results/success2.jsp</to-view-id>
</navigation-case>
</navigation-rule>

■MySampleBeanに太字部分を追加
package test.beans;

import java.io.Serializable;

import javax.ejb.EJB;

import test.MySample;
import test.MySampleException;
import test.MySampleSessionLocal;

public class MySampleBean implements Serializable {

private static final long serialVersionUID = 1L;

int id;
String value;

@EJB(name="MySample")
private MySampleSessionLocal mySampleSession;

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}

public String checkInput() {
MySample ms = mySampleSession.findMySample(id);
if (ms != null) {
this.setValue(ms.getMyFlag());
return "success";
} else {
return "failure";
}
}

public String insertValue() {
try {
mySampleSession.addMySample(id, value);
} catch(MySampleException mse) {
return "failure2";
} catch(Exception e) {
return "failure2-1";
}
return "success2";
}
}

以上です。いや、簡単ですね。
.

RAD v7.5 試用版 を使ってJSFとEJB3.0・・・(4)

手順

1. サンプルDBを作成
2. DBをWAS v7のデータソースに登録
3. JPAプロジェクトを作成
4. EJBを作成(findメソッドのみ)
5. JSFを作成
(前回)
6. EJBに挿入メソッドを作成する
(今回はここまで)
7. JSFを作成(挿入)
8. EJBに更新メソッドを作成する
9. JSFを作成(更新)

6. EJB Session Beanに挿入メソッドを作成する
findメソッドの次にinsertMySampleメソッドを作成します。

最初に例外を追加します。想定していないデータが入力されたときにスローするためです。

MySampleEJBClientフォルダ(注意!EJBClientフォルダです) < ejbModule < test パッケージを右クリック < 新規作成 < クラス として「新規Javaクラス」ウィンドウを起動します。名前に「MySampleException」スーパークラスに「java.lang.Exception」を指定します。以上です。

MySampleEJBプロジェクト < MySampleEJB < セッションBean < MySampleSession.java を開きます。

以下のメソッドを追加します。
    public void addMySample(int myid, String value) throws MySampleException {
if (value.length() < 1) {
throw new MySampleException();
}
try {
MySample ms = new MySample();
ms.setMyid(myid);
ms.setMyFlag(value);
em.persist(ms);
em.flush(); // これがないと例外がキャッチできません。重要!
} catch(EntityExistsException e3) {
throw new MySampleException();
}
}

em.flush()が重要です。これをしないとアプリケーションから到達できないクラスで例外が発生するため、ハンドリングができなくなります。

次にメソッドのプロモートを忘れずに実行します。(アウトライン ビューでaddMySampleメソッドを右クリック、Java EE < メソッドのプロモート)

WAS v7テスト環境を起動します。

サーバーを右クリックしてUniversal Test Clientを起動、ログインします。

1.JNDI エクスプローラーリンクをクリック
2.右ペインに表示される[ローカル EJB Bean]フォルダを展開
3.test.MySampleSessionLocalリンクをクリック
4.左ペインに表示されるMySampleSessionLocalの'+'を展開
5.void addMySample(int, String) をクリック
6.int と String(1文字のみ) に適宜値を入れて起動

「メソッドが正常終了しました。」と表示されれば成功です。テーブルの中を見るとデータが追加されていることが確認できます。

6のステップで二文字以上を入力し、例外のロジックを確認します。MySampleExceptionが発生すれば成功です。

.

2008年11月22日土曜日

買出しは楽し

本日、朝からわが家の子供たちがおばあちゃん(義母)のところに遊びに行きました。妻も外出。すなわち私は半日の自由をゲット。

というわけで以前から気になっていた練馬の城北流通センター(大江戸河岸城北卸売市場)に行ってきました。Webにはほとんど情報はありません。それでも頑張って(というかダラダラ?)調べたところ、どうやら「安いらしい」「プロ用の店らしい」「でも一般客も歓迎らしい」「定休日が不明。営業時間はAM6:00~午前いっぱいらしい」とのこと。果たして土曜日は営業しているのか。無駄足となる懸念はありつつも、安くて新鮮な魚が手に入るのでは、と期待して朝9:00頃出かけてみました。

寒い中、リュックを担いで自転車で遠征。環八(笹目通り)から練馬高野台方面に入って目的地に到着。大丈夫。ちゃんと営業してました。

自転車置き場にはママチャリが10~20台ほど並んでます。結構一般の人も来てるのかな。自転車を止めて恐る恐る入ってみました。子連れのお父さんを何人か確認。一般人も普通に来ているようです(実はプロかもしれないけどね)。ざっと周りを散歩。そんなに広くはありません。中堅スーパーくらいかな。昔の魚屋さんとか乾物屋さんという店が並んでます。ダンボールや発泡スチロールが通路にせり出している。市場(いちば)って感じがします。でもちょっと活気がないか。全体に高齢化している模様。

一番元気なのが入り口近くの魚屋さんです。元気なおばさんと若いあんちゃんが店先で仕事してます。おばさんが主に客の相手をしていて、あんちゃんは力仕事担当。店の奥ではおっさんが黙々と魚を捌いている。うん。好ましいなー。なぜだか昭和の感じがする。

発泡スチロールの中には魚が氷に埋もれています。サンマ、サバ、アジ、太刀魚、キンメ、イカ、スズキ。いいねえ。値札を見るとサンマ一本80円とある。まあまあだな。子供の手のひら大の小アジ一匹70円。う~ん、ちょっと小さすぎる。アジの中くらいのがグラム150円(うろ覚え)。グラムじゃあ安いんだか高いんだか分からない。でも値札にご丁寧に「今日は高いです!」と書いてある。

「高い!」はないだろうと不審に思っていたら、あんちゃんが別の客に説明しているのが聞こえてきました。「今日はシケだったから高いよ~。それに量もなくてね~」。

はたと気がつく。そうか。この「高い!」は不本意の「高い!」だったのか。安くて新鮮な魚を期待するお客さんに申し訳ない、普段はもっと安いんだという忸怩たる「高い!」だったのだ。う~ん。ますます好ましい。というわけで「サンマ5本くださーい」とおばちゃんに言ってみる。一本は刺身に、残りの4本はパン粉焼き+自家製ピクルスのタルタルソースにしよう。サンマを5本袋に詰めてもらって支払い。「月曜はやってますか?」とおばちゃんに聞いたら「カレンダー通りだから休みなのよ。ごめんね」とのこと。了解。また来週来てみよう。

それから野菜を仕入れにスーパーに行きました。その前にTully'sに寄って熱いコーヒーを一杯。本日のコーヒー/ショートサイズ290円。こんなに高かったっけ?でもコーヒーは美味い。

スーパーでまずは野菜売り場に。葉尽きの大根、小松菜、ほうれん草、にんじんを買い物カゴへ投入。大根の葉っぱは茹でて刻んでしょうゆと削り節と砂糖少々で炒めると旨いのですよ。大根本体はどうやって食べようかと悩んでいたら、養殖鯛のおかしらが目に留まりました。つぶらな瞳が僕を見ている。しばらく目を合わせていると「こいつと大根を酒と薄口しょうゆで煮付けたら旨いんじゃないか」という、地平線上に漂う黒い雲のような「これは来るんじゃないか」という予感がしてきました(なんちゅう喩えか)。すなわち鯛の頭購入。それからアメリカ産豚肩ロース塊が安かったので購入。これは焼き豚にしよう。魚と肉とでは明らかに子供のテンションが違うのです。料理を作る以上、喜んでもらいたい。というわけで豚塊肉。

これから材料の仕込みを始めます。よい休日になりそうな予感。

(夕方追記)
パン粉焼きにしようと思ったんですが、子供を迎えに行っていろいろ用事を済ますうちに疲れてきちゃったので、からあげに変更。美味かった。
でも子供には不評。箸もつけずに「不味い」「嫌い」とのたまう。いいから食べてみ。美味いから。「イヤ。食べたくない」。じゃあ食べないとオヤツなしにする、と脅して無理やり食べさせる。と、一口、二口。一切れ食べて、二切れ目、タルタルソースをたっぷりつけて美味しそうに食べ始めた。な、美味いだろ!と聞いてみると、タルタルソースが美味しい。タルタルソースが魚の味を消してくれる、とのこと。やれやれ。

不思議と腹が立たないのは、オレも昔母親の料理に文句つけてたなあ、という記憶がよみがえるからか・・・。子供は親の料理にケチをつけるもの。親は子供にケチをつけられても(食べてさえくれれば)腹は立たない。そんなものかも知れません(食べないと怒るけどね)。

私の母親も、子供に文句を言われて悲しかったのかな。親になってみるとそうは思えないんだな。とにかく子供が食べてくれさえすれば、文句は言われても気にならないもん。母親の本当の気持ちは分かりませんがそんな気がしました。たまに「美味しかった!」と言ってくれれば、それでいいんだ。不味いとか美味いとか言いながら家族で食べる、それだけで。

刺身用と思ったサンマ一本。これも取りやめ。むしろ酢締めが食べたい。疲れたらやっぱり酢っぱいものでしょ。で、大名おろしした後に塩をまぶしたはいいものの(酢で〆る前の手順です)、そのまま忘れて夕食完了(ありがち)。しょうがないので塩を水で流して水分をキッチンペーパーで拭き取り、冷凍しました。末は蒸しサンマか焼きサンマ?

鯛のおかしら(アラだけ)+大根は旨かった。これでダシを取っておでん作ったら旨いんじゃないかな。今度試してみよう。

焼き豚いい感じ。ちょっと焦がしてしまいましたが、味は悪くない。明日が楽しみです。

(2008/11/28追記)
冷凍したサンマは燻製にして美味しく頂きました。中華鍋で燻製。使えますよ。おっちゃんのママゴトかとおもったら十分実用的。ちょっとしたたんぱく質を軽く熱燻すれば、燻製風味の深い味に速変わり。簡単・美味しい・後片付けが楽。こんなお遊びみたいな調理法が定番化するとはね。
.

2008年11月21日金曜日

RAD v7.5 試用版 を使ってJSFとEJB3.0・・・(3)

手順

1. サンプルDBを作成
2. DBをWAS v7のデータソースに登録
3. JPAプロジェクトを作成
4. EJBを作成(findメソッドのみ)
(前回はここまで)
5. JSFを作成
(今回はここまで)
6. EJBに挿入メソッドを作成する
7. JSFを作成(挿入)
8. EJBに更新メソッドを作成する
9. JSFを作成(更新)

5. JSFを作成(参照のみ)

以下の3ページを作成します。
(1) mysampleのキー(myid)入力画面
(2) キーを持つレコードが存在した場合、それを表示するページ
(3) 存在しない場合に表示するページ

テスト環境(WAS v7サーバー)が起動していたら停止して下さい(私の環境ではハングすることがありました。重すぎるんでしょう)。

ファイル > 新規 > 新規動的 Web プロジェクトをクリックします。
ウィンドウが現れるので、プロジェクト名に MySampleJSF と入力し、EARメンバーシップに MySampleEar があることを確認して 終了 をクリックします。

例によってモジュールの依存関係を定義します。 MySampleJSFを右クリック > プロパティ > Java EE モジュール依存関係 でMySampleEJBClient.jarとMySampleJPA.jarにチェックを入れます。

JSF用のBeanを作成します。

MySampleJSF を右クリックし、新規作成 > クラス を選択します。
新規Javaクラス ウィンドウに

パッケージ:test.beans
名前:MySampleBean

と入力し「終了」をクリックします。

MySampleBean を以下のように作成します。
package test.beans;

import java.io.Serializable;

public class MySampleBean implements Serializable {

private static final long serialVersionUID = 1L;

int id;
String value;

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}

/* まずはスタブを作成します */
public String checkInput() {
if (id == 0) {
return "success";
} else {
return "failure";
}
}
}

■web.xmlにFacesサーブレットを定義します。
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>
MySampleJSF</display-name>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

■/WEB-INF/faces-config.xml を作成します。
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE faces-config PUBLIC
"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
"http://java.sun.com/dtd/web-facesconfig_1_1.dtd">

<faces-config>
<managed-bean>
<managed-bean-name>mySampleBean</managed-bean-name>
<managed-bean-class>test.beans.MySampleBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<navigation-rule>
<from-view-id>/search.jsp</from-view-id>
<navigation-case>
<from-outcome>failure</from-outcome>
<to-view-id>/WEB-INF/results/failure.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/WEB-INF/results/success.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>

次にJSPです。

■index.jsp
<% response.sendRedirect("search.faces"); %>

■search.jsp
<%@page language="java" contentType="text/html; charset=windows-31j"
pageEncoding="windows-31j"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<f:view>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>検索</title>
<meta http-equiv="Content-Type"
content="text/html; charset=windows-31j">
</head>
<body>
<CENTER>
<h2>検索</h2>
<h:form>
myid:
<h:inputText value="#{mySampleBean.id}" />
<h:commandButton value="検索実行"
action="#{mySampleBean.checkInput}" />
</h:form>
</CENTER>
</body>
</html>
</f:view>

■/WEB-INF/results/success.jsp (パスに注意)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><%@page
language="java" contentType="text/html; charset=windows-31j"
pageEncoding="windows-31j"%>
<html>
<head>
<title>exists</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
</head>
<body>
OK
</body>
</html>

■/WEB-INF/results/failure.jsp
success.jspの"OK"を"NG"に変更するだけ。

これで動作確認をしてみましょう。"0"を入力すれば画面に"OK"が、"0"以外だと"NG"が表示されるはずです。

次にEJBと連携させます。

実はここまでくれば簡単です。

MySampleBean を以下のように変更します(太字部分)。
package test.beans;

import java.io.Serializable;

public class MySampleBean implements Serializable {

private static final long serialVersionUID = 1L;

int id;
String value;

@EJB(name="MySample")
private MySampleSessionLocal mySampleSession;

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}

public String checkInput() {
MySample ms = mySampleSession.findMySample(id);
if (ms != null) {
this.setValue(ms.getMyFlag());
return "success";
} else {
return "failure";
}
}
}

次にsuccess.jspを変更します。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><%@page
language="java" contentType="text/html; charset=windows-31j"
pageEncoding="windows-31j"%>
<html>
<head>
<title>exists</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
</head>
<body>
Found bean.<br>
id -> ${ mySampleBean.id }
value -> ${ mySampleBean.value }
</body>
</html>

以上。
.

RAD v7.5 試用版 を使ってJSFとEJB3.0・・・(2)

今回の手順は以下のページほとんどそのままです。
Building Java EE applications with IBM Rational Application Developer V7.5 ... (IBM dWの記事)
が、若干違う+dWの手順では上手く行かないところがあるのでご注意ください。

手順

1. サンプルDBを作成
2. DBをWAS v7のデータソースに登録
(前回はここまで)
3. JPAプロジェクトを作成
4. EJBを作成(findメソッドのみ)
(今回はここまで)
5. JSFを作成(参照のみ)
6. EJBに挿入メソッドを作成する
7. JSFを作成(挿入)
8. EJBに更新メソッドを作成する
9. JSFを作成(更新)

3. JPA/EJBを作成(findメソッドのみ)
一旦サーバーは止めましょう。重いし(私の環境ではハングすることもありました)。
(1)JPAプロジェクトを作成します。
ファイル > 新規 > その他 > JPAプロジェクト をクリックし 「新規 JPA プロジェクト」ウィンドウを起動します。以下の情報を入力します(MySampleEARは存在していません)
プロジェクト名:MySampleJPA
EARメンバーシップのEAR プロジェクト名:MySampleEAR
「終了」をクリックします。

(2)エンティティ(≒DBとマップされたBean)を作成します
「MySampleJPA」フォルダを右クリックし、新規作成 > エンティティー を選択します。
「新規 JPA エンティティー・エンティティークラス」ウィンドウで以下の情報を入力します。
Java パッケージ:test
クラス名:MySample
「次へ」をクリックします。
「新規 JPA エンティティー・エンティティープロパティ」のエンティティ・フィールドを埋めます。
追加をクリックしてエンティティーフィールドダイアログを表示
タイプ:int
名前:myid
としてOKをクリック。
「鍵」カラムにチェックを入れておきます。
再び「追加」をクリック。以下の情報を入力します。
タイプ:String 要注意!!タイプは char ではありません!!
名前:myFlag (敢えてテーブルのカラム名と異なるものを使っています)

「終了」をクリックすると以下のソースが生成されます。
package test;

import java.io.Serializable;
import javax.persistence.*;

/**
* Entity implementation class for Entity: MySample
*
*/
@Entity

public class MySample implements Serializable {


@Id
private int myid;
private char myFlag;
private static final long serialVersionUID = 1L;

public MySample() {
super();
}
public int getMyid() {
return this.myid;
}

public void setMyid(int myid) {
this.myid = myid;
}
public char getMyFlag() {
return this.myFlag;
}

public void setMyFlag(char myFlag) {
this.myFlag = myFlag;
}

}

アノテーションを追加します。(太字部分)
package test;

import java.io.Serializable;
import javax.persistence.*;

/**
* Entity implementation class for Entity: MySample
*
*/
@Entity
// ここでスキーマ名を指定します
@Table(schema="learning", name="mysample")
public class MySample implements Serializable {


@Id
private int myid;
// ここで変数名とは異なるカラム名を指定します。
@Column(name="mychar")
private char myFlag;
private static final long serialVersionUID = 1L;

public MySample() {
super();
}
public int getMyid() {
return this.myid;
}

public void setMyid(int myid) {
this.myid = myid;
}
public char getMyFlag() {
return this.myFlag;
}

public void setMyFlag(char myFlag) {
this.myFlag = myFlag;
}

}

次にJPAのpersistent.xmlにデータソース名指定します。
「MySampleJPA」フォルダ > 「JPAコンテンツ」 > persistence.xml をクリックします。
左ペインの「パーシスタンス・ユニット」を選択し、右側のペインのJTAデータ・ソースに 2.(3)で指定した jdbc/learning を入力します。

以上でJPAの作成が完了です。(残念ながらここでは動作検証は出来ません)

次にEJBプロジェクトを作成します。

ファイル > 新規 > EJBプロジェクト を選択して「新規 EJB プロジェクト・EJB プロジェクト」ウィンドウを起動します。
プロジェクト名に MySampleEJB と入力します。EJBモジュールバージョンが3.0であること、EARメンバーシップのEARプロジェクト名が MySampleEAR であることを確認し、「終了」をクリックします。

MySampleEJBフォルダを右クリックし プロパティ > Java EE モジュール依存関係 をクリックします。「選択可能な従属JAR」で「MySampleJPA.jar」にチェックを入れ、OKをクリックします。

MySampleEJB > MySampleEJB > セッションBean を右クリックします。コンテキストメニューから 新規 > セッションBean を選択します。

「EJB 3.0 セッションBean の作成ウィンドウ」に以下の情報を入力し、「ビジネス・インターフェースの作成」が「ローカル」になっていることを確認して終了をクリックします。
Javaパッケージ:test
クラス名:MySampleSession

生成されたEJBソースに下記の太字部分を追記します。
package test;

import javax.ejb.Stateless;

/**
* Session Bean implementation class MySampleSession
*/
@Stateless
public class MySampleSession implements MySampleSessionLocal {

/**
* Default constructor.
*/
public MySampleSession() {
// TODO Auto-generated constructor stub
}

@PersistenceContext(unitName="MySampleJPA")
EntityManager em;

public MySample findMySample(int myid) {
MySample ret = (MySample)em.find(MySample.class, myid);
return ret;
}

}

次にメソッドをプロモートします。

(重要 dWの記事には欠けていますが、以下の手順が必要です)
MySampleEJBClientフォルダを右クリックして、コンテキストメニューからプロパティを選択、次にJava EE モジュール依存関係 をクリックして「選択可能な従属JAR」で「MySampleJPA.jar」にチェックを入れます。

次に「アウトライン」ビューでfindMySampleを右クリック、コンテキストメニューから「Java EEツール」>「メソッドのプロモート」を実行します。

以上でEJBの作成が完了しました。

ここでユニバーサルクライアントを利用して動作確認をしてみます。

RADのサーバービューでWASサーバを起動します。

サーバーを右クリックし プロジェクトの追加および除去 を選択、MySampleEARを追加します。

公開が完了し、同期済みステータスになったら、サーバーを右クリックし、Universal Test Client > 実行 をクリックします。UTCが起動するのでログインします。(プロジェクトの追加以前にUTCを起動したことがあれば、一度「再起動」をクリックしてください)

「JNDIエクスプローラー」リンクをクリックします。

test.MySampleSessionLocalが表示されるのでクリックします。
※ そもそも表示されない場合はどこかで手順を間違っている可能性があります。プロジェクトがサーバに追加(デプロイ)されているかどうか等、手順を確認して下さい。
※ リンクではなく単にテキストが表示されている場合は、UTCの再起動を試して下さい。それでも出来ない場合はやはり手順を見直して下さい。

左側のペインに
MySampleSessionLocal
- MySampleSessionLocal
- メソッドの可視性
- MySample findMySample(int)
と表示されます。MySample findMySample(int) をクリックして下さい。

右側のペインに
test.MySample findMySample(int)

パラメーター 値
int: 0

と表示されています。そのまま「起動」をクリックします。

test.EJSLocal0SLMySampleSession_af305597.findMySample()
test.MySample@1ee01ee0 (test.MySample)

などと表示されれば成功です。「オブジェクトの使用」> String getMyFlag() ('a'が返る)などで遊んでみてください。

以上でEJBの作成が完了です。(しかしEJB簡単になったなあ)

RAD内のサーバーを止めてから、次のステップに行きます。

.

Rational Application Developer(以下RAD) v7.5 試用版 でJSFとEJB3.0を使ってCRUDアプリ作成(1)

試用版のダウンロード先です。IBM IDが必要です。(誰でも作成できると思います。)ダウンロードには丸一日かかります。

DBはMySQL v5.0を使いました。

IBM dWのBuilding Java EE applications with IBM Rational Application Developer V7.5 and WebSphere Application Server V7.0をベースに発展させて行きます。

手順

1. サンプルDBを作成
2. DBをWAS v7のデータソースに登録
(初回はここまで)
3. JPA/EJBを作成(findメソッドのみ)
4. JSFを作成(参照のみ)
5. EJBに挿入メソッドを作成する
6. JSFを作成(挿入)
7. EJBに更新メソッドを作成する
8. JSFを作成(更新)

1. サンプルDBを作成
create database learning;
use learning; -- このコマンドはMySQL独自。
drop table mysample;
create table mysample(
myid integer primary key,
mychar char(1)
);

delete from mysample;
insert into mysample values(0,'a');
insert into mysample values(1,'b');
insert into mysample values(2,'c');

2. WAS v7のデータソースに登録
RAD上にまだWAS v7 サーバが存在しない場合は作成して下さい。サーバービューを右クリックして「新規作成」を選択してテキトーに入力すれば出来ると思います。
WASを起動します。(重い・・・)
起動が完了したらServer view の サーバーを右クリックして「管理」>「管理コンソール」をクリック。
管理コンソールにログインします。

(1)JDBCを登録
メインメニュー Resources > JDBC > JDBC Providers > New >
ウィザード形式で入力を求められます。以下の情報を入力。
Database type -> User-defined
Implementation class name -> com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
Name -> MySQL JDBC Driver
次のページで
ClassPath: c:/path/to/mysql-connector-java-5.1.6-bin.jar
以上。

(2)認証情報を登録
メインメニュー Security > Global security > Authenticationエリアにある Java Authentication and Authorization Service を展開 > J2C authentication data > New
ここで
Alias -> MySQL_Learning
Userid, password -> DBに接続するときのID、パスワード
と入力する。

(3)データソースを作成
メインメニュー Resources > JDBC > Data source
Scope : 適切なスコープを設定(分からなければ'...Server = server1'とあるものを選びます)
> New
ウィザード形式で入力を求められるので下記情報を適宜入力します。
Data source name -> MySQL_Learning
JNDI name -> jdbc/learning(JPAに教えるので覚えておくこと)
> Next
Select an existing JDBC provider で MySQL JDBC Driver を選択
> Next
(何もしない)
> Next
Component-managed authentication alias
で、先ほど作成した認証情報(ノード名/MySQL_Learning)を選択。(JPAから参照するため、その他のaliasは不要。多分ServletからID/Passwordなしで利用するときはContainer-managed...が必要。Mapping-configurationは何か知らない・・・)
> Next
内容を確認
> Finish

次にカスタムプロパティを設定します。
JDBC providers > MySQL JDBC Driver > Data sources > MySQL_Learning > Connection pools > Custom properties > New
以下の情報を入力します。
Name -> url
Value -> jdbc:mysql://localhost:3306/learning
テスト接続。Warningが出ますが無視して問題ありません。
(注意:カスタムプロパティを設定しなくてもテスト接続は成功する。でもこのプロパティを定義しておかないと実際には使えない。認証情報をContainer-managedのみにしても成功するが、JPAからは使えない。すなわちテスト接続が成功しても油断しないで下さい)

まずは以上です。
.

2008年11月20日木曜日

SQL私観

はい。負け犬の遠吠えです。

SQL。難しすぎです。

もちろん単体のテーブルをSelectしたり、2,3個のテーブルをJOINする分には難しいことなどなにもありません。しかし、ちょっと細かい選択をSQLで作りこもうとすると、大変です。まるでクイズ。つーか実際SQLで課題を解くクイズが、実際にありますよね(しかもかなりたくさん)。今仕事してんだよ。クイズ解いてる暇ねーんだよ。とちょっと野蛮な言葉遣いになってしまいましたが、SQLのクイズとか見ていると何でこんなに難しいんだ、と思います。つーかクイズが成り立つ言語ってどうよ?

難しい理由は何か。それはSQLだから。つまりSQLだから難しい。すなわち難しさはSQLの本質的な属性と言えましょう。(この辺、論理大丈夫かな)

あまりにも難しいがために、便宜的で安直な解釈/用法がまかり通り、今ではそれがほとんど常識となっています。つまりデータを正規化してテーブルに落とし、キーでJOINして、SELECTしてINSERTして・・・。実際には非常に複雑で非直感的な仕組みの上に成り立っているのですが、それをばっちり隠蔽してしまっている。

いや、結果的にそうならざるを得ないと思いますし、それが正しいと思うんですよ。正面からSQLを理解するのってもはや研究者の仕事としか思えません。

初心者が何となくJOINを使って実装してしまうと(何となく実装するな、というツッコミを貰いそうですが、そうならざるを得ないプロジェクトがいかに多いことか)、内部の動きが隠蔽されている分、やってみなければパフォーマンスに問題がないか分からない。いや、やってみても分からない。十分にデータを突っ込んで試さない限りは。すなわちパフォーマンスチューニングが非常に難しい(統計情報に依存/実行計画の確認が必要)のがSQLです。初心者がSQLを作りこめばそこにパフォーマンスのリスクが生じる。脅しや極論ではなく、プロジェクトを運営する人が身に染みている分かっていることだと思います。

Java、Cなどの言語に毒された発想かもしれませんが、少なくとも処理が逐次的に理解できる手続き的な解法の方が、パフォーマンスへの影響を見極めやすいなど、メリットが多いと思えます。しかし複雑なSQLで解こうとすると、実行計画を実際に見ない限りどうなっているのか分からないし、しかもそれも統計情報に依存するわけで。

初心者はJOIN原則禁止。副照会厳禁。別に恥じることはないですよ。これでいいんじゃないか、と私は思います。え?それじゃ学習しないって?やっぱりダメか。

以上
.

ダイエットその後

ダイエット順調ですね。うむ。胃が軽くなって来た感じがあります。体重もゆるゆると落ちてきました。

酒は一日2~3合あたりで安定してます。最近は飲み過ぎてないです。食事中にワイン1合。食後にチーズなど軽くつまみながらウィスキー60~100ml。なんだか物足りないな、という日は寝る前にウィスキーか焼酎を少々(~60ml)。休日はいつも飲み過ぎてしまうんですが(夕方から一杯やり始めてだらだらと止めどなく飲む)先週末は3合未満で押さえました。素晴らしい!!よくやった!!

私の場合「食べる→飲む」という流れが強いようです。「飲む→食べる」というベクトルはさほど強くない。また、「飲む→飲む」という展開にはなりにくい。すなわち「飲む」の後に「食べる」をかまさなければ、だらだら飲み続けるということがなくなるようです。なんか食べたいなあ、でも我慢しなきゃ、などと思いつつ昔買ったDancyu(グルメ雑誌)や料理本など眺めながら、今度は何を食べてやろう、とチビチビ酒をすするのが至福の時です(しかし食欲強いな)。

さあこの調子で頑張ろう。
.