yamicha.com's Blog - Presented by yamicha.com
Blog yamicha.com's Blog - 2018/09 の記事
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30]

yamicha.com's Blog
 諸事情により、現在更新休止中。ご了承ください。もし今後ブログを再開することがあるとすれば、その際にはこのブログスクリプトではなく、新しく開発したものによるかもしれません。
 当ブログ管理者についてはこちらをご参照。
開発魔法(737)
社会問題(733)
お知らせ(11)
質問・バトン回答(15)
ゲスト出演(8)
経済・知的財産(150)
ゲーム開発(182)
[Ada] 勝手に補足
- Note
- 金配りの次の一手


- Endless Ultimate Diary
- 銃世界

漢字バトン
- うるる雑記帳
- 漢字接力棒

ツキアイゲノムバトン
- ブログ@うにうに画像倉庫
- あぶ内閣

縺イ縺セ縺、縺カ縺励ヰ繝医Φ
- 月夜のボヤキ
- 騎士サーラバトン
パスワードを使う
名無し (2012/02/27)


開発者解放裁判
yamicha.com (2010/03/14)
Winnyに関しては、私も「純白」とまでは考えておりませんし、使用し..

開発者解放裁判
通りすがり (2010/03/08)
winnyに関しては「ダウンロードソフト板」なんてところを拠点に開発..

新型インフルエンザの恐怖
いげ太 (2009/11/03)
> C#などの「int Some(int , int)」は、F#では「(int * int) ->..

時効に関する思考
yamicha.com (2009/08/31)
>いげ太さんコメントありがとうございます。手元にドキュメントが少..
Homepage
Blog Top
Normal View
List View
Search
Calendar
Comment List
Trackback List
Blog RSS
Send Trackback
Phone Mode
Administrator
yamicha.com
Blog
るううるる。
Source
法令データ提供システム
FindLaw
Development
Java2 Platform SE 6
Java EE 6 API
MySQL Developer Zone
PHP Reference
MSDN Library
Ada Reference Manual
Objective Caml
Python Documentation
Erlang
Prolog Documents
特別死刑論
2007/08/23(Thu)19:06:33
 死刑囚3人に対して死刑執行。当サイトは必ずしも死刑を廃止せよという立場ではありませんので、これに対して強く抗議するなどといったことはありませんが、どういうわけか日本では死刑についての議論が極めて低調であり、実質的には死刑が執行された時点しか議論をする機会がないも同然ですから、ここで死刑に関しての考えをいくつか述べておきます。
 まず死刑の理念ですが、死刑に限らず刑罰は「一般予防論」及び「特別予防論」のいずれか、または両方を理由として論じられる(目的刑論)ことが多いようです。この辺りは刑罰の基本ですから、ご存知の方も多いでしょう。「一般予防論」は刑罰の威嚇効果によって犯罪者に犯罪を踏みとどまらせ、かつ一般市民に対しては法への信頼性を持たせるもので、「特別予防論」は犯罪者を更生させて再犯を防ぐと同時に、犯罪者を社会から隔離して外部に被害が及ぶのを防ぐ意味があります。死刑や仮釈放なしの終身刑の場合、犯罪者は永久に出所することができませんので、特別予防論は「犯罪者を収容して被害を防ぐ」効果のみを持ちます。
 それでは実際のところどうかといいますと、一般予防論の「死刑の威嚇による犯罪抑制効果」はほとんど存在しないと考えて間違いなさそうです。死刑の犯罪抑制効果を有意に示す資料は今のところ存在しないとされており、威嚇効果は全くないか、あっても誤差の範囲内と見るべきです。「威嚇効果が全くないと裏付ける資料もない」との意見もあるでしょうが、これは「悪魔の証明」ですから、やはり威嚇効果があるとする理由にはなりません。
 では特別予防論の観点ではどうでしょう。前述の通り、死刑及び終身刑の受刑者に更生による再犯抑止効果は関係ありませんので、社会からの隔離のみがその効果となりますが、社会から隔離するには死刑でも終身刑でもどちらでも良いのです。つまり、特別予防論に死刑の正当性を見つけることはできません
 刑罰を行う理由としては、これらの他に「被害者や遺族、社会の報復感情を満たす」「国家が犯罪者を裁いて敵討ちを防止する」といったものも存在しますが、これらは刑罰の理論というより感情論の色彩が強いものです。しかし、もし死刑を存続する理由があるとすれば、最も大きな理由となるのはこれではないでしょうか。ただ、こうした感情論にも近い理由を掲げることは、死刑をまじめに議論する上では得策ではないと考えられます。強いて言えば「凶悪犯罪者への死刑執行により、国民の法への信頼性が担保される」と考えることはできそうですが。
 なお、死刑肯定論の中には「あなたの大切な人が殺されても死刑廃止を言い続けられるか」というものがありますが、この言い分はフェアではありません。「あなたの大切な人が冤罪で死刑執行され、または死刑判決を受けても死刑存続を言い続けられるか」とセットで考えなければ適切ではありません。前者では死刑存続、後者なら廃止という虫のいい考えはできませんので、これをセットにして死刑存続か廃止かの結論を出すことが必要です。
 このように、死刑を論じる上では冤罪も避けては通れません。終身刑の後に冤罪が分かれば自由になれますが、死刑執行後ではどうしようもありません。終身刑でも失った時間は戻りませんが、「命は全地球より重い」と判例でも述べられている(後述)通り、両者の差は極めて大きいのです。さらに、これは決して他人事ではなく、日本人は誰しも冤罪で投獄されて死刑判決を受けるリスクを(微細ながら)背負っているのです。
 ここまで徹底して考えると、死刑を軽々しく肯定も否定もできなくなります。しかし、これほど根が深い問題にもかかわらず、議論が盛り上がるとしてもせいぜい死刑執行時に限る上、感情論による非建設的な議論になりがちなテーマでもあります。しかし、これはもっと掘り下げて論じられるべきテーマでしょう。
 ちなみに、死刑が日本国内で最も重い刑罰であることは言うまでもありませんが、これが「憲法で禁じる残虐な刑罰」であるかどうかを判断した有名な判例があります。(こちらは現在では違憲となった)尊属殺人を含む殺人事件について、弁護側が死刑を「公務員による残虐な刑罰を禁じる憲法36条違反」と主張し、これに対する判決として下されたものです。
 詳細が気になる方は判例集を入手して読んでみてください、と言いたいところですが、現代においてそのようなものは不要です。判例検索システム判例を読めば良いのです(利便性のために判例に直接リンクをしておきますが、「昭和22(れ)119」をフォームに入力して探すこともできます)。お役所のサイトらしく少々いい加減なのは否めないのですが、とりあえず使えて便利です。
 上記裁判の判決によれば、「一人の生命は全地球より重い」と(日本赤軍ハイジャック事件や「太陽にほえろ!」で出てきたような文言を)述べながらも、「憲法13条公共の福祉を侵害しない限り個人の権利が最大限尊重される」「31条で法の定める手続きによって生命を奪う刑罰を科せられることが定められている」「火あぶり、はりつけ、さらし首、釜ゆでなどは残虐な刑罰であり、これらがもし制定されることがあれば36条違反である」として、死刑は合憲であるとしています。ただし、「将来において死刑の威嚇が必要ないとみなされたら、死刑は残虐な刑罰として排除される」という可能性にも言及しています(60年後の今日まで死刑は存続し、運用されているわけですが)。全文中には一般予防論及び特別予防論に触れたとみられる箇所もあり、なかなか興味深いものです。
 しかし、前述の通り「死刑による威嚇効果」は否定されこそすれ肯定される材料に乏しく、犯罪者の排除による特別予防論効果は終身刑においても同様に可能です。やはり最終的には「国民が死刑に置く期待」、言うなれば「死刑の威嚇効果による犯罪抑止を期待するという偽薬効果」と「国民の義憤・報復感情を満たす効果」による法への信頼性の向上効果、それと表裏一体となる冤罪死刑の可能性などといった死刑のリスク、これらを天秤にかけて死刑の是非を判断する必要があるのでしょう。

 ところで、死刑が難しい問題であることは当然なのですが、目的刑論や冤罪などといった論点ではなく、もう少し簡素な論点による議論もたまに見受けられます。それらをここで簡単に取り上げて、私の考えを述べておきます。
 まず「終身刑には費用がかかる。凶悪犯罪者に無駄なコストをかける必要はないから死刑存続すべき」という意見が一部に存在します。しかし、年に10万人が死刑になるわけでもあるまいし、これもある意味「犯罪者が国費を潰す」ことをあえて強調し、「犯罪者を優遇するな」という感情論に結びつける論点に見えます。そもそも死刑は判決が出るまでに時間がかかり、執行までにも時間がかかりますので、その間に天寿を全うする者もしばしば。大した意味はないでしょう。それなら裁判と死刑執行を迅速化すべき?費用ケチって冤罪死刑を出すのでは、笑い話にもならないでしょう
 また、「日本の死刑は果たして本当に残虐でないのか」と称して死刑に疑問を呈する意見もありますが、それなら絞首刑なら残虐であって、睡眠薬後の薬殺なら残虐ではないのでしょうか。ちなみに、日本の絞首刑はおそらく首の骨を折るか外すかする即死型とみられます。苦痛はないと考えられていますが、おそらく床が落ちてから死ぬまでの落下の感覚は存在するでしょう。これを残虐とするか、別に残虐でないとするか、多少残虐であっても死刑に至る罪を考えればやむを得ないとするかは皆様の判断にお任せします。
 刑罰に関する論には「目的刑論」の他、上記で取り上げなかった「応報刑論」というものもあります。これは「刑罰は犯罪の代償である」とする考え方で、刑罰を明確に応報として位置づけるものです。いわば「ペナルティ」方式であり、犯罪に対する国民感情はこれに近いのではないでしょうか(単純な「あだ討ち」とは異なります)。ただし、これをペナルティのためだけに行うという論と、ペナルティと同時に犯罪防止も行われるべきという論が存在します。これらは似て非なるものと考えた方が良さそうです。そもそも刑罰は「刑罰で犯罪者を威嚇し、一般市民には法の信頼性を誇示し、犯罪者にはペナルティを与えると同時に、社会から隔離して市民の安全を確保し、教育を行って再犯を防止する」といった具合に複合的な要素を持ち合わせており、支持する論を1つだけ決めろという方が無理なのですが。
 応報という論点で考えれば、人によっては「死刑よりも死ぬまで自由を奪われる終身刑の方が苦痛が大きい」と考える場合もあり、これもまた難しいところです。

 総務省が来年にも迷惑メール規制法を強化する予定とか。これまでは件名への「未承諾広告※」の記述とメール送信解除手続きの記述が義務付けられていたところを、許諾なきメールは一切禁止にするとのこと。当然というより遅すぎたと考えるべきです。
 迷惑メールなど送りつけてくるような連中に、いちいち「未承諾広告※」を使うようなある意味まともな業者はほとんど存在しませんし、そもそも「送信解除手続きの記述」が全く意味を成していません。送信解除の手続きを行おうものなら、そのメールアドレスによって受信されるメールが「読まれている」ことを知られてしまい、より迷惑メールが殺到する可能性が極めて高いため、誰も手出しができないのです。
 そればかりか、日本の取り決めは海外メールには全く通用しません。メールフィルタなどで「^[a-zA-Z0-9]$」に当てはまるメールを全部弾ければ楽なのでしょうが、そのような設定ができるメーラーは見たことがありません。仮に存在したとしても、私のような英語オンチなら使えるでしょうが、英文メールをやり取りする人は使えません。
 しかし、気になることがひとつ。総務省はこれの実効性をどのようにして担保するのでしょうか。「未承諾広告※」の1つ守らせることができないお役人がいかに立派な法律を作ったところで、ないも同じです。日本のものについては摘発強化などで多少はマシに対応できるとしても、海外となると全く何ともならないでしょう。
 迷惑メールが非常にはた迷惑な代物であることは言うまでもありません。受信者に不快感を与え、メール選別の効率を大幅に落とすばかりか、ネットワークのトラフィックを増大させてサーバーの負荷を増大し、ディスク容量・メールボックス容量を圧迫し、様々な意味で多くの人に被害を与えます。これを本当に取り締まれるのであれば、大変良いことではあるのですが、現実には難しいです。
 現実的には、「発信元や文面その他のデータベースを作り、メールをフィルタリングする技術を導入するようISPなどの業者に依頼する」程度しかできないでしょう。発信元や文面によるフィルタリングはいたちごっこになりがちであり、推測型のフィルタは正常なメールすらゴミ箱に放り込んでくれますが、現時点ではその程度しか対策がありません。利権団体・情報処理推進機構辺りが「日の丸フィルタリングを開発する」などと称して、くだらない予算を組んでしゃしゃり出てこなければ良いですが。
 実効性を担保した迷惑メール対策ができるなら、規制強化にも手放しで喜べるのですが、果たしてお役人がそこまで考えているのかどうか。
 全く関係ありませんが、迷惑メールを送りつけてくるようなふざけた業者に「未承諾広告※」をつけてくるような律儀な者はほとんど存在しないものの、以前に西武の不正が発覚した際、担当者がそれをしっかり帳簿につけていたことは以前に取り上げた通りです。恐るべし帳簿魂。ある意味見習いたいです。しかし一体どういう仕訳にしたのやら。当然ですが、迷惑メール業者にこのような騎士道を求めてはいけません。つまり規制の実効性の担保は必須です。

 以前に散々な目にあったものの、このままでは終われないのがcatalinaのCometProcessor。強引に読み込みを止めておいて、必要な時にそのつど再開しようとしたのが悪かったのであって、1回送るたびに接続を切っていけば動くでしょう。きっと。このような大変いい加減な理由により、Cometを再び書くことになりました。
 しかし、これが前回の2番煎じでは面白くありません。せっかくですからAjaxを使ってみましょう。Ajaxを使うならJSONも使いたいところです。ただ、AjaxもJSONもライブラリを1から作っていては大変ですし、他人のものを使うのも理解の妨げになりますから、例によってYAjaxとEoD型JSONParserを使うことにします。それにしても、なぜJSPでCometが使えないのでしょうか。Servlet 3.1辺りで標準APIとしてサポートして欲しいものですが。仕方がないのでServletを使っています。
/examples
 /WEB-INF
  /classes
   /com
    /yamicha
     /comet
      Comet.java
      @JSON Name.java
      @JSON Prefix.java
      @JSON Equipment.java
     /json
      JSON.java
      JSONMember.java
      JSONPropertyEncode.java
      JSONParser.java
      JSONException.java
  web.xml
 json.jsp
 jsonwriter.jsp
 やたら構成が複雑ですが、ほとんどはJSONパーサによるものです。そのうちJAR化してlibにでも投げ入れておきましょうか。
 まずはパーサから。
// WEB-INF\classes\com\yamicha\json\JSON.java
package com.yamicha.json;

import java.lang.annotation.*;
import static java.lang.annotation.RetentionPolicy.*;
import static java.lang.annotation.ElementType.*;

@Retention(value=RUNTIME) @Target(value={TYPE})
	public @interface JSON{
	boolean enable() default true;
}

// WEB-INF\classes\com\yamicha\json\JSONMember.java
package com.yamicha.json;

import java.lang.annotation.*;
import static java.lang.annotation.RetentionPolicy.*;
import static java.lang.annotation.ElementType.*;

@Retention(value=RUNTIME) @Target(value={FIELD , METHOD})
	public @interface JSONMember{
	boolean enable() default true;
	String name() default "";
	JSONPropertyEncode property() default 
		JSONPropertyEncode.LOWERCASE;
}

// WEB-INF\classes\com\yamicha\json\JSONPropertyEncode.java
package com.yamicha.json;

import java.lang.annotation.*;
import java.util.Locale;
import static java.lang.annotation.RetentionPolicy.*;
import static java.lang.annotation.ElementType.*;

public enum JSONPropertyEncode{
	NOCHANGE , JAVABEAN , HEADUPPER , LOWERCASE , UPPERCASE;
	// そのまま , メソッドは先頭を小文字に , 
	// 先頭を大文字に , 変数名を小文字に , 変数名を大文字に
	public String propertyExchange(String str , boolean method){
		switch(this){
		case NOCHANGE:
			return str;
		case JAVABEAN:
			if(method){
				return str.substring(0 , 1).
					toLowerCase(Locale.ENGLISH) + 
					str.substring(1 , str.length());
			}else{
				return str;
			}
		case HEADUPPER:
			return str.substring(0 , 1).
				toUpperCase(Locale.ENGLISH) + 
				str.substring(1 , str.length());
		case LOWERCASE:
			return str.toLowerCase(Locale.ENGLISH);
		case UPPERCASE:
			return str.toUpperCase(Locale.ENGLISH);
		}
		return null;
	}
}

// WEB-INF\classes\com\yamicha\json\JSONParser.java
package com.yamicha.json;

import java.lang.annotation.*;
import static java.lang.annotation.RetentionPolicy.*;
import static java.lang.annotation.ElementType.*;
import java.lang.reflect.*;
import java.util.*;

public class JSONParser{
 public JSONParser(){
 }
 public String parse(Object o) throws JSONException{
  return parseData(o , new ArrayList());
 }
 private String parseData(Object o , List nest) throws JSONException{
  // 型によって処理を変える
  if(o == null){
   return null;
  }else if(o instanceof Integer || o instanceof Short || 
   o instanceof Long || o instanceof Float || 
   o instanceof Double || o instanceof Boolean){
   // プリミティブ型のラップの場合
   return o.toString();
  }else if(o instanceof String){
   // 文字列の場合
   return "\"" + ((String)o).replaceAll("\"" , "\\\\\"") + "\"";
  }else{
   // 配列/リスト・オブジェクトの場合
   return parseNest(o , nest);
  }
 }
 private String parseNest(Object o , List nest) throws JSONException{
  for(Object n : nest){
   if(o == n)
    throw new JSONException("オブジェクト " + o.toString() + 
     " が循環参照されています。");
  }
  List newnest = new ArrayList(nest);
  newnest.add(o);

  if(o instanceof Collection || o.getClass().isArray()){
   // リスト・配列の場合
   return parseArray(o , newnest);
  }else{
   // オブジェクトの場合
   return parseObject(o , newnest);
  }
 }
 private String parseArray(Object o , List nest) throws JSONException{
  Class c = o.getClass();

  Object array[] = null;
  if(c.isArray()){
   array = (Object[])o;
  }else{
   array = ((Collection)o).toArray();
  }

  // 配列をパース
  String str = "[";
  for(int i = 0; i < array.length; i++){
   if(i > 0)
    str += ",";

   str += parseData(array[i] , nest);
  }
  str += "]";
  return str;
 }
 private String parseObject(Object o , List nest) throws JSONException{
  Class c = o.getClass();

  JSON json = (JSON)c.getAnnotation(JSON.class);
  if(json == null || !json.enable())
   return "";

  Field fields[] = c.getFields();
  Method methods[] = c.getMethods();

  List<JSONParseData> jpds = new ArrayList<JSONParseData>();

  // メソッド
  for(Method method : methods){
   JSONMember jm = method.getAnnotation(JSONMember.class);

   // 無効なら処理しない
   if(jm == null || !jm.enable())
    continue;

   // メソッド名から JavaBean プロパティ名を生成
   String mname = method.getName();

   // 処理する JavaBean メソッド名は必ず4文字以上
   // (get.+/set.+/is は処理しない)になる
   String mtype = "";
   String prop = "";

   if(mname.length() >= 4){
    mtype = mname.substring(0 , 3); // get/set
    prop = mname.substring(3 , mname.length()); // プロパティ名
   }

   Method getter = null;
   Method setter = null;
   Class type = null;

   // 頭に get/set がつかないものは無効
   // ついていても JavaBean 条件を満たさないものは無効
   try{
    if(mtype.equals("get")){
     if(method.getParameterTypes().length == 0 && 
      method.getReturnType() != void.class){
      type = method.getReturnType();
      getter = method;
      // Setter があるかを調べる
      setter = c.getMethod("set" + prop , type);
     }
    }else if(mtype.equals("set")){
     Class params[] = method.getParameterTypes();
     if(params.length == 1){
      type = params[0];
      setter = method;
      // Getter があるかを調べる
      Method m = c.getMethod("get" + prop);
      // 返り値をチェック
      if(type.equals(m.getReturnType()))
       getter = m;
     }
    }
   }catch(NoSuchMethodException e){
   }

   // メソッドがない
   if(getter == null || setter == null)
    throw new JSONException("JavaBean ではないメソッド " + mname + 
     " に @JSONMember アノテーションがつけられています。");

   // アクセスできないなら処理しない
   if(!Modifier.isPublic(getter.getModifiers()) ||
    !Modifier.isPublic(setter.getModifiers())){
    String method_name = "";
    if(!Modifier.isPublic(getter.getModifiers()))
     method_name = getter.getName();
    if(!Modifier.isPublic(setter.getModifiers()))
     method_name = setter.getName();
    throw new JSONException("メソッド " + method_name + 
     " は public ではありません");
   }

   // 名前(あるなら)を取得
   // jm == null は continue であるため本来は不要だが
   // 拡張に備えて jm == null でも例外にならないようにしておく
   String name = "";
   JSONPropertyEncode pe = JSONPropertyEncode.LOWERCASE;
   if(jm != null){
    name = jm.name();
    pe = jm.property();
   }

   // 名前がないなら自動生成
   // PropertyEncode の値は name 指定による命名時は無効
   if(name.length() == 0)
    name = pe.propertyExchange(prop , true);

   // プロパティの値を処理
   String data = "";
   try{
    data = parseData(getter.invoke(o) , nest);

    // 登録
    jpds.add(new JSONParseData(name , data));
   }catch(IllegalAccessException e){
   }catch(IllegalArgumentException e){
   }catch(InvocationTargetException e){
   }
  }

  // フィールド
  for(Field field : fields){
   Class type = field.getType();
   JSONMember jm = field.getAnnotation(JSONMember.class);

   // 無効なら処理しない
   if(jm == null || !jm.enable())
    continue;

   // アクセスできないなら処理しない
   if(!Modifier.isPublic(field.getModifiers()))
    throw new JSONException("フィールド " + field.getName() + 
     " は public ではありません。");

   // 名前を取得
   String name = "";
   JSONPropertyEncode pe = JSONPropertyEncode.LOWERCASE;
   if(jm != null){
    name = jm.name();
    pe = jm.property();
   }

   // 名前がないなら自動生成
   // PropertyEncode の値は name 指定による命名時は無効
   if(name.length() == 0)
    name = pe.propertyExchange(field.getName() , false);

   // プロパティの値を処理
   String data = "";
   try{
    data = parseData(field.get(o) , nest);

    // 登録
    jpds.add(new JSONParseData(name , data));
   }catch(IllegalAccessException e){
   }catch(IllegalArgumentException e){
   }catch(ExceptionInInitializerError e){
   }
  }

  // データの変換
  String str = "{";
  int regist = 0;

  // フィールド名の重複チェック用
  List<String> names = new ArrayList<String>(); 

  for(int i = 0; i < jpds.size(); i++){
   JSONParseData jpd = jpds.get(i);

   // プロパティの重複がないことを確認
   if(names.indexOf(jpd.getName()) != -1)
    throw new JSONException("変数 " + jpd.getName() + 
     " が重複しています。");
   names.add(jpd.getName());

   // フィールドとプロパティを登録
   if(i > 0)
    str += ",";

   str += parse(jpd.getName()) + ":" + jpd.getData();
  }
  str += "}";
  return str;
 }
}

// WEB-INF\classes\com\yamicha\json\JSONException.java
package com.yamicha.json;

public class JSONException extends Exception{
	public JSONException(){
	}
	public JSONException(String msg){
		super(msg);
	}
}
 後はアノテーションを使ってプログラムを組んでいくだけです。オブジェクトの中にオブジェクトを入れることもできるのですが、Entityと違ってManyToOneに当たるメンバ(及びOneToOneの片方)は必要ありません。ちなみにManyToOne(及び相互接続のOneToOne)はJAX-WSでも使えないことになっており、Entityと併用する場合はOneToManyのみをデータ変換対象にし、ManyToOneは変換対象から外さなければ、循環参照によって変換に失敗してしまいます。それはこのJSONParserクラスでも同じであり、もしこのライブラリをEntityと併用するのであれば、ManyToOneやOneToOneの片方のJavaBeanプロパティに@JSONMemberをつけないか、または@JSONMember(false)を指定する必要があります。
 それではJSON対応JavaBeanを。少し手直しするだけでEntityにもできますので、興味のある方はどうぞ。
// WEB-INF\classes\com\yamicha\comet\Name.java
package com.yamicha.comet;

import java.util.List;
import java.util.ArrayList;
import com.yamicha.json.*;

@JSON public class Name implements java.io.Serializable{
	private int number;
	private String name;
	private String notes;
	private Prefix prefix;
	private List<Equipment> equipments;

	public Name(){
		equipments = new ArrayList<Equipment>();
	}
	public Name(int number , String name , 
		String notes , Prefix prefix){
		this();
		this.number = number;
		this.name = name;
		this.notes = notes;
		this.prefix = prefix;
	}
	public Name(int number , String name , String notes , 
		Prefix prefix , List<Equipment> equipments){
		this(number , name , notes , prefix);
		this.equipments = equipments;
	}

	@JSONMember public int getNumber(){
		return number;
	}
	@JSONMember public String getName(){
		return name;
	}
	@JSONMember public String getNotes(){
		return notes;
	}
	@JSONMember public Prefix getPrefix(){
		return prefix;
	}
	@JSONMember public List<Equipment> getEquipments(){
		return equipments;
	}

	public void setNumber(int n){
		number = n;
	}
	public void setName(String n){
		name = n;
	}
	public void setNotes(String n){
		notes = n;
	}
	public void setPrefix(Prefix p){
		prefix = p;
	}
	public void setEquipments(List<Equipment> e){
		equipments = e;
	}
}

// WEB-INF\classes\com\yamicha\comet\Prefix.java
package com.yamicha.comet;

import com.yamicha.json.*;

@JSON public class Prefix implements java.io.Serializable{
	private int number;
	private String name;

	public Prefix(){
	}
	public Prefix(int number , String name){
		this.number = number;
		this.name = name;
	}

	@JSONMember public int getNumber(){
		return number;
	}
	@JSONMember public String getName(){
		return name;
	}

	public void setNumber(int n){
		number = n;
	}
	public void setName(String n){
		name = n;
	}
}

// WEB-INF\classes\com\yamicha\comet\Equipment.java
package com.yamicha.comet;

import com.yamicha.json.*;

@JSON public class Equipment implements java.io.Serializable{
	private int number;
	private String name;
	private String notes;

	public Equipment(){
	}
	public Equipment(int number , String name , String notes){
		this.number = number;
		this.name = name;
		this.notes = notes;
	}

	@JSONMember public int getNumber(){
		return number;
	}
	@JSONMember public String getName(){
		return name;
	}
	@JSONMember public String getNotes(){
		return notes;
	}

	public void setNumber(int n){
		number = n;
	}
	public void setName(String n){
		name = n;
	}
	public void setNotes(String n){
		notes = n;
	}
}
 別にシリアル化などしないのに、java.io.Serializableなどインプリメントして何をしようというのでしょうか、私は。EJBで使う時にはこれが必要ですので、その時のクセといいましょうか。
 残るはComet.javaとJSPだけです。
// WEB-INF\classes\com\yamicha\comet\Comet.java
package com.yamicha.comet;

import org.apache.catalina.*;
import java.io.*;
import java.util.List;
import java.util.ArrayList;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.resource.*;
import com.yamicha.json.*;
import static org.apache.catalina.CometEvent.EventType.*;

public class Comet extends HttpServlet implements CometProcessor{
 private List<ResponseSet> connects;
 private List<Name> names;
 private int name_index;
 private int prefix_index;
 private int equip_index;

 public void init() throws ServletException{
  connects = new ArrayList<ResponseSet>();
  names = new ArrayList<Name>();

  name_index = 0;
  prefix_index = 0;
  equip_index = 0;
 }
 synchronized public void destroy(){
  connects.clear();
 }
 synchronized public void event(CometEvent e) 
  throws IOException , ServletException{
  HttpServletRequest request = e.getHttpServletRequest();
  HttpServletResponse response = e.getHttpServletResponse();
  request.setCharacterEncoding("UTF-8");

  switch(e.getEventType()){
  case BEGIN:
   if("write".equals(request.getParameter("mode"))){
    regist(request , response);
    e.close();
   }else{
    int number = 0;
    try{
     number = Integer.parseInt(request.getParameter("number"));
    }catch(NumberFormatException ex){
    }

    ResponseSet rs = new ResponseSet(response , number);
    connects.add(rs);
    try{
     send(rs); // 必要があればデータを送る
    }catch(JSONException ex){
     e.close();
    }catch(IOException ex){
     e.close();
    }
   }
   break;
  case READ:
   break;
  case END:
  case ERROR:
   connects.remove(response);
   e.close();
   break;
  }
 }
 synchronized private void regist(
  HttpServletRequest request , HttpServletResponse response) 
  throws IOException , ServletException{
  List<Equipment> equipments = new ArrayList<Equipment>();

  for(int i = 0; i < 5; i++){
   String fname = "e_name_" + i;
   String fnotes = "e_notes_" + i;

   String ename = request.getParameter(fname);
   String enotes = request.getParameter(fnotes);

   if(!ename.isEmpty()){
    equip_index ++;
    equipments.add(
     new Equipment(equip_index , ename , enotes));
   }
  }

  prefix_index ++;
  Prefix prefix = new Prefix(prefix_index , 
   request.getParameter("prefix"));

  name_index ++;
  Name name = new Name(name_index , 
   request.getParameter("name") , 
   request.getParameter("notes") , prefix , equipments);

  names.add(name);

  // JSON 生成
  String json = "";

  try{
   JSONParser p = new JSONParser();
   json = p.parse(names);
  }catch(JSONException e){
  }

  response.setCharacterEncoding("UTF-8");
  response.setContentType("text/html;charset=UTF-8");
  PrintWriter out = response.getWriter();
  out.println("<html><head><title>Comet</title></head>");
  out.println("<body><b>送信状況</b><br>");

  int succeed = 0;
  int fails = 0;
  for(ResponseSet rs : connects){
   try{
    send(rs , json);
    succeed ++;
   }catch(IOException e){
    fails ++;
   }
  }
  out.println("送信成功 : " + succeed + "<br>");
  out.println("送信失敗 : " + fails + "<br><br>");

  out.println(json);
  out.close();
 }
 private void send(ResponseSet rs) 
  throws JSONException , IOException{
  JSONParser p = new JSONParser();
  String json = p.parse(names);

  send(rs , json);
 }
 private void send(ResponseSet rs , String json) 
  throws IOException{
  // 配信(以前の配信後に更新された場合のみ)
  if(rs.getNumber() < name_index && names.size() > 0){
   HttpServletResponse res = rs.getResponse();
   res.setCharacterEncoding("UTF-8");
   res.setContentType("text/plain;charset=UTF-8");
   PrintWriter out = res.getWriter();
   out.print(json);
   out.close();

   connects.remove(rs);
  }
 }

 static public class ResponseSet{
  private HttpServletResponse response;
  private int number;
  public ResponseSet(HttpServletResponse r , int n){
   response = r;
   number = n;
  }
  public HttpServletResponse getResponse(){
   return response;
  }
  public int getNumber(){
   return number;
  }
  public boolean equals(Object o){
   return response.equals(o);
  }
  public int hashCode(){
   return response.hashCode();
  }
 }
}
 機能としては簡単です。データが送られてきたら、引数として添付されている「読み込み済みの番号」と現在登録されているデータを照合し、更新があったら即座にデータを送り返します。更新がなければ接続を待機し、何らかの更新がなされた場合にそのつど番号を照合し、データを送り返します。これによって擬似リアルタイム更新を実現しようという寸法です。
 しかし、一体どうすれば接続を閉じられるのでしょうか。CometEventにはclose()がありますが、リストにCometEventを保持して後から使うとおかしなことになります(getHttpServletResponse()するとnullを返すなど)。PrintWriterをclose()すれば良さそうなものですが、どうやら接続は閉じないようです。
 残るはAjaxとデータ登録フォーム、それにweb.xmlによるServletの登録ですが、web.xmlの編集は前回のものをそのまま使います。同時に、Cometを使うためにはserver.xmlを事前に編集しておくことが必要です。このサンプルではポート8090にCometを割り当てています。これらの編集の方法は前回のブログをどうぞ。
// json.jsp
<%@page contentType="text/html;charset=UTF-8" 
pageEncoding="Shift_JIS" %>

<html>
<head>
<title>Comet</title>
<script language="JavaScript">
function YAjax(){
 this.YAjax();
}
YAjax.prototype.YAjax = function(){
 this.createRequest();
 this.headers = new Array();
 this.headers.unshift(new RequestHeader(
  "Content-type" , "application/x-www-form-urlencoded"));
 this.active = false;
}
YAjax.prototype.getRequestHeaders = function(){
 return this.headers;
}
YAjax.prototype.setRequestHeaders = function(h){
 this.headers = h;
}

YAjax.prototype.createRequest = function(){
 try{
  this.ajax = new ActiveXObject("Microsoft.XMLHTTP");
 }catch(e){
  this.ajax = new XMLHttpRequest();
 }
}
YAjax.prototype.init = function(){
 if(this.ajax == null){
  this.createRequest();
  if(this.ajax == null){
   return false;
  }
 }else if(this.active){
  this.ajax.abort();
 }

 if(this.ajax != null)
  this.prepareCallback();

 this.active = true;

 return true;
}
YAjax.prototype.cancel = function(){
 if(this.ajax != null && this.active){
  this.ajax.onreadystatechange = function(){}
  this.ajax.abort();
  this.active = false;
 }
}
YAjax.prototype.isSucceed = function(){
 if(this.ajax.readyState == 4 && this.ajax.status == 200)
  return true;
 return false;
}
YAjax.prototype.getAjax = function(){
 return this.ajax;
}
YAjax.prototype.requestCallback = function(){
 this.onStateChange();
 if(this.isSucceed()){
  this.onResponse(this.ajax.responseText);
 }else if(this.ajax.readyState == 4){
  this.onError();
 }
}
YAjax.prototype.onResponse = function(text){
}
YAjax.prototype.onError = function(){
}
YAjax.prototype.onStateChange = function(ready , status){
}
YAjax.prototype.prepareCallback = function(){
 if(this.ajax != null){
  var selfobj = this;
  this.ajax.onreadystatechange = function(){
   selfobj.requestCallback();
  }
 }
}
YAjax.prototype.request = function(url , args , method , async){
 this.init();
 this.onRequest(url , args , method , async);
}
YAjax.prototype.onRequest = function(url , args , method , async){
 if(method == null || method == "")
  method = "GET";
 if(url == null)
  url = "";
 if(args == null)
  args = "";
 if(async == null)
  async = true;

 var urls = this.parseURL(url , args , method)

 var ajax = this.getAjax();

 if(!async)
  ajax.onreadystatechange = function(){}

 ajax.open(method , urls[0] , async);

 for(var i = 0; i < this.headers.length; i++)
  ajax.setRequestHeader(this.headers[i].getName() , 
  this.headers[i].getValue());

 ajax.send(urls[1]);

 if(!async)
  this.requestCallback();
}
YAjax.prototype.parseURL = function(url , args , method){
 var send_url = url;
 var send_args = "";

 if(method == "POST"){
  send_args = args;
 }else{
  if(send_url.charAt(send_url.length - 1) != "?" && args != "")
   send_url += "?";
  send_url += args;
 }

 return new Array(send_url , send_args);
}

function RequestHeader(name , value){
 this.RequestHeader(name , value);
}
RequestHeader.prototype.RequestHeader = function(name , value){
 this.name = name;
 this.value = value;
}
RequestHeader.prototype.getName = function(){
 return this.name;
}
RequestHeader.prototype.getValue = function(){
 return this.value;
}

var ajax = new YAjax();
var number = 0;
YAjax.prototype.onResponse = function(text){
 if(text){
  var names = eval("(" + text + ")");

  var str = "";
  var max_number = -1;
  for(var l = 0; l < names.length; l++){
   var name = names[l];

   str += '<table border="1">';
   str += '<tr>';
   str += '<td bgcolor="#CCDDFF">名前</td>' + 
    '<td>' + name.prefix.name + name.name + '</td>';
   str += '</tr>';

   str += '<tr><td bgcolor="#CCDDFF">装備</td>' + '<td>';

   str += '<ul>';
   for(var i = 0; i < name.equipments.length; i++){
    var equip = name.equipments[i];
    str += '<li>';
    str += '<b>' + equip.name + '</b><br>';
    str += equip.notes;
    str += '</li>';
   }
   str += '</ul>';

   str += '</td></tr>';

   str += '<td bgcolor="#CCDDFF">備考</td>' + 
    '<td>' + name.notes + '</td>';
   str += '</tr>';

   str += '</table>';

   str += '<hr>';

   if(name.number > max_number)
    max_number = name.number;
  }

  if(max_number != -1)
   number = max_number;

  document.getElementById("result").innerHTML = str;
 }

 request();
}
function request(){
 ajax.request('http://www.localyamicha.com:8090/examples/servlet/Comet' , 
  'number=' + number);
}
</script>
</head>

<body onload="request()" 
onunload="ajax.cancel()">
<b>Comet Program</b><br>
<br>

<span id="result"></span>

</body>
</html>

// jsonwriter.jsp
<%@page contentType="text/html;charset=UTF-8" 
pageEncoding="Shift_JIS" %>

<html>
<head>
<title>Comet Writer</title>
</head>

<b>データ登録</b>

<form method="POST" 
action="http://www.localyamicha.com:8090/examples/servlet/Comet">
<input type="hidden" name="mode" value="write">

<b>個人情報</b>
<table border="1">
<tr><td>名前</td><td>
<input type="text" name="name" size="40"></td></tr>
<tr><td>接頭辞</td><td>
<input type="text" name="prefix" size="40"></td></tr>
<tr><td>備考</td><td>
<input type="text" name="notes" size="60"></td></tr>
</table>
<br>

<b>Equipments</b>
<table border="1">
<tr><td>装備名</td><td>備考</td></tr>
<%
for(int i = 0; i < 5; i++){
%>
<tr>
<td><input type="text" name="e_name_<%=i%>" size="20"></td>
<td><input type="text" name="e_notes_<%=i%>" size="40"></td>
</tr>
<%
}
%>
</table>

<input type="submit" value="送信">
</form>

</body>
</html>
 最初にjson.jspを開いておき、jsonwriter.jspでデータを書き込むと、json.jspにリアルタイムに更新が反映されます。
 しかし、この操作を何度か繰り返していくと、なぜか接続が異常に蓄積します。終いにはエラーが出てしまい、動作しなくなってしまいます。Tomcatのドキュメントのサンプルを見る限り、間違いはなさそうなのですが。つまり実用は不可能です。普通にJSPなり何なりでCometを実装した方が良いでしょう。今回特筆すべきは「CometとAjaxとJSONの連携」ということで。別に目新しくもありませんが、コードの多くを以前に書いたライブラリに頼り、新しい実装部分をできるだけ簡素に(EoD)してみました。
 これのおかげで
// 1.JavaBean Model
@JSON public class Name{
	// ...
	@JSONMember public String getName(){
		// ...
	}
	// ...
}

// 2.Java
// ...
JSONParser p = new JSONParser();
String json = p.parse(name);
// ...
out.println(json);

// 3.JavaScript
var name = eval("(" + text + ")");
// ...
str += name.prefix.name;
 このような一連の書き方が可能になっています。非常に簡単です。
 しかし、JSONといいWSDLといい循環参照の1つもサポートしないとは何たること、という怒りの声はまたの次の機会としましょう。
カテゴリ [開発魔法][社会問題] [トラックバック 0][コメント 0]
<- 前の記事を参照 次の記事を参照 ->

- Blog by yamicha.com -