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/28(Tue)22:20:10
 安倍改造内閣が発足したものの、今ひとつ盛り上がりません。それもそのはず、本来最も取り替えられるべき人物が取り替えられていないのですから。その人物はおよそ1年前、小泉内閣が終わってからずっと同じポストに居座り続け、自分の思想の赴くままに好きなだけ法案を強行採決した挙句、それが原因で参院選で敗北しても自分だけはポストから退かないという相当なつわものです。これを取り替えるのであれば、世論は(良くも悪くも)もう少し盛り上がってくれるでしょう。
 今回の内閣改造は、決して「サプライズで支持率回復」や「能力のある人を入れて改革を実行」といったものではなく、強いて言えば邪魔な人間や「お友達」を切っただけというなかなか壮絶な人事です。当然、これで支持率が大きく変化することは期待できません。また、野党が参院選で過半数を取ってしまった以上、これまでのようにバカげた法案を強行採決することはできず、対話と守りを中心にしなければ法案が全く通らなくなるという現実的な考えもあるのでしょう。いずれにしても自業自得です。
 しかし、いくら人間を入れ替えてみたところで、やはり安倍内閣は限界です。対米関係では共和党だけでなく民主党とのパイプを持っておく必要も出てきており、次の大統領が誰になるにせよ、大統領が変わるとなれば変化は避けられないにもかかわらず、一連の慰安婦決議問題などのくだらない騒動で日本の株を落とす始末です。
 安倍氏は北朝鮮を最大限プロパガンダに利用して首相となったにもかかわらず、首相となって以後は拉致問題を完全無視。北朝鮮と日本の間には「拉致問題」「核問題」「ミサイル問題」など色々な問題がありますが、仮にこれらの問題に順位をつけるのであれば、安倍内閣ではどれが最も優先されるのでしょうか。私は拉致を最重視します。もし拉致を重視するのであれば、核そっちのけで拉致問題を解決する程度の覚悟が必要であるにもかかわらず、実際は核問題によって支援を獲得された上、拉致問題はかすんでしまいました。安倍内閣が拉致を最重視するならこのような態度は極めて疑問ですし、拉致よりも核やミサイルを重視するのならそれを堂々と公言すべきでしょう。
 国内の問題では、強行採決や教育荒廃策、法人優遇などが極めて強く見られます。野党が勝ったおかげで教育バウチャーや無賃残業合法化、消費税を上げての法人税引き下げは何とか見送りになるとしても、これはあくまで野党が勝利したためであり、まさか安倍内閣が個人重視や労働者保護に動くとは考えられませんから、安倍内閣かつ参院野党過半数が続く限り、悪化はしないが改善もしないと考えるべきでしょう。
 もう1つ例を出せば、少子化担当者が安倍路線を読みきれずに困っているとのこと。少子化対策に女性の負担を軽減しようというのがこれまでの基本政策となっていたようですが、安倍内閣は「伝統的家族感」を重視しているのだとか。聞こえは良いですが、要するに「男女平等参画などクソ食らえ」ということです。果たして今後の安倍内閣はどちらの方針でいくのでしょうか。ちなみに、先進国各国の出生率などの資料を見れば一目瞭然ですが、くだらない伝統的家族とやらの邪念を捨て去った方が少子化が改善される可能性は高いです。これに付随して、安倍内閣である限り772条問題の解決と夫婦別姓はあり得ません。
 結論を言えば、いくら人事で夢を見ようとしたところでレームダックである事実は変わりません。かといって解散を行った場合、公明党の活躍で与党が何とか過半数を握ったとしても、2/3の確保は極めて困難ですから、野党が反対する法案は絶対に成立しなくなります。結局、遅かれ早かれ退陣しかありません。人事がイマイチ盛り上がらないのも、与党の政治家がその辺りを見越しているためと考えられます。

 さて、対する民主党はといえば、「さくらパパ」が賭けゴルフ。本人はその事実の一部を認め、厳重注意を受けています。
 この件について私が民主党に何かを述べるなら、「ザマ見ろ」の一言です。自民党は丸山弁護士やヤンキー先生、ヒゲの隊長といったタレント候補を大量に投入し、参院選をも人気投票や低IQ選挙に持ち込もうとしていたようですが、このような国民を愚弄する行為を許せないと考える人は多いと考えられます。こうした怒りの受け皿となるためにも、民主党はタレント選挙を行わない方針を採り、純粋な政策で勝負を挑むべきなのです。
 ところが、民主党はよりによってこのようなタレント候補を立ててしまいました。結果、そのタレントに不祥事が発覚し、民主党のイメージを落とすに至りました。選挙の人気投票化に加担しようとするから、このような痛い目にあうのです。最初からタレントなど担ぎ上げなければ良かったものを、自業自得でしょう。
 二大政党制では双方の対立軸がかすんでしまい、政策の違いをアピールすることが難しくなるため、タレントに頼りたくなる気持ちは分からないでもありません。しかし、自民党は郵政選挙で組織票を粉々に破壊してしまった上、露骨な低IQ選挙で反感を増やしてしまったため、安定して票を確保できる保証がなくなり、今後いっそうタレント路線・低IQ路線にシフトしてくることが考えられます。そこで重要になるのが民主党の行動です。同じくタレント路線で応じるか、愚直なまでに政策を訴え続けるか。愚直に政策を訴えた場合、その時には敗北を喫するかもしれませんが、国民はそこをしっかり見ています
 これに懲りて、民主党はタレントを担ぎ上げるのは控えるべきでしょう。また、仮にタレントを候補に立てるのであれば、その熱意と能力を十分に審査した上でなされるべきです。「立候補さえできれば自民でも民主でもどちらでも良い」といったタレントは当然排除すべきです。

 REST。Web 2.0などというバズワードによって注目されている技術とのことです。私はこういうバズワードには興味がありませんので、正直あまり気乗りはしないのですが、大抵のバズワードには(それが最初からバズワードとして作成された場合を除き)「拡大解釈される前の意味」が存在します。それを探求するとしましょう。
 それにしても、なぜバズワードはこうも普及してしまうのでしょうか。JavaScriptを使ってもいないのに「Ajax」、少々データをやり取りして「SOA」など明らかに詐欺でしょう。どこもかしこもSOAだRESTだと言葉が氾濫した挙句、そのうち休憩所の壁にまでRESTと書かれたりしないでしょうか。タレント候補とバズワードを追放しようではありませんか。
 本題に戻ります。RESTは実のところ極めて普遍的な概念であり、これ自体はSOAPのように何か仕様の定義があるわけではありません。データのリストを問い合わせ、取得し、そのリストから1つのデータを選んで詳細を表示したり、またはデータを追加または更新したりといったものであり、例えばRSSもデータへのリンクの集団ということでRESTの1形態とみなされるようです。
 ただし、RESTではHTTPリクエストメソッドによって行える動作が定められており、一般的にCGIやServletで行われている「POSTメソッドでデータを受け渡し、内容を登録・修正・削除」といった行為は厳密にはルール逸脱となるようです(とはいえ、RESTに標準仕様が存在しない以上、何でもありといえばありなのでしょう)。
 あまり知られていませんが、HTTPにはPOSTとGET以外にもいくつかのメソッドが存在します。RESTではPOST、GETの他にPUTとDELETEを使うようです。ServletにはしっかりdoPut及びdoDeleteメソッドが存在しますので、受け取る際にはこれらをオーバーライドしましょう。また、JSPでの判定にはrequest.getMethod()、PerlやPHPならREQUEST_METHOD環境変数を使いましょう。
 これらの対応は以下の通りです。

Method操作備考
GETデータ取得データには一切変更を加えない
POST新規登録データを修正・削除しない
PUT更新データを更新・修正するPOST
DELETE削除データを削除するのみ

 実際にはPOSTで何でもやることが多いのですが、RESTでは上記の通り定められています。
 データの受け渡し方法としては、RESTそのものには厳密な定めはないようですが、XMLやHTMLが使われることが多いようです。XMLを使う場合、GETやDELETEは引数なり拡張パスでデータを与えるとして、POSTやPUTの場合はそのままデータを送ってしまうようです。通常のPOSTであればrequest.getParameter("name")を使ってパラメータを取得するところですが、この時に送られてくるデータはXMLですので、InputStreamから読み込むことになります。Perlなら標準入力から読み出したデータをパースせずにそのまま使いましょう。
 これだけでは分かりづらいですから、ここは実際に実装してみましょう。まずはRESTを受け付けるサーバーから。使用するデータは、記事番号、タイトル、作成日時、本文を持つものであるとします。
// restserver.jsp
<%@page import="java.util.* , java.sql.* , javax.sql.* , 
javax.annotation.* , javax.xml.transform.* , java.io.* , 
javax.xml.transform.stream.* , javax.xml.parsers.* , 
org.w3c.dom.* , javax.xml.transform.dom.* , org.xml.sax.SAXException" 
contentType="text/xml;charset=UTF-8" pageEncoding="Shift_JIS" %>

<%!
@Resource(name="jdbc/MySQL") private DataSource ds;
%>

<%
request.setCharacterEncoding("UTF-8");

Connection c = null;

try{
 String method = request.getMethod();

 c = ds.getConnection();
 c.setAutoCommit(false);

 if("GET".equalsIgnoreCase(method)){
  DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  dbf.setNamespaceAware(true);
  DocumentBuilder db = dbf.newDocumentBuilder();
  Document d = db.newDocument();
  boolean error = false;

  String type = request.getParameter("type");
  if("list".equals(type)){
   int first = 0;
   int max = 10;

   if(request.getParameter("page") != null){
    try{
     first = Integer.parseInt(request.getParameter("page"));
    }catch(NumberFormatException e){
    }
   }
   if(request.getParameter("max") != null){
    try{
     max = Integer.parseInt(request.getParameter("max"));
    }catch(NumberFormatException e){
    }
   }

   PreparedStatement ps = c.prepareStatement(
    "SELECT number , title , dated FROM rest_title " + 
    "ORDER BY number DESC LIMIT " + first + " , " + max);
   ResultSet rs = ps.executeQuery();

   Element root = d.createElement("Root");
   d.appendChild(root);

   while(rs.next()){
    Element content = d.createElement("Content");
    content.setAttribute("link" , 
     "?type=detail&number=" + rs.getString("number"));

    Element number = d.createElement("Number");
    number.appendChild(d.createTextNode(rs.getString("number")));
    content.appendChild(number);

    Element title = d.createElement("Title");
    title.appendChild(d.createTextNode(rs.getString("title")));
    content.appendChild(title);

    Element dated = d.createElement("Date");
    dated.appendChild(d.createTextNode(rs.getString("dated")));
    content.appendChild(dated);

    root.appendChild(content);
   }

   ps.close();
  }else if("detail".equals(type)){
   String num = request.getParameter("number");

   PreparedStatement ps = c.prepareStatement(
    "SELECT number , title , dated , message " + 
    "FROM rest_title WHERE number = ?");
   ps.setString(1 , num);
   ResultSet rs = ps.executeQuery();

   if(rs.next()){
    Element root = d.createElement("Root");
    d.appendChild(root);

    Element number = d.createElement("Number");
    number.appendChild(d.createTextNode(rs.getString("number")));
    root.appendChild(number);

    Element title = d.createElement("Title");
    title.appendChild(d.createTextNode(rs.getString("title")));
    root.appendChild(title);

    Element dated = d.createElement("Date");
    dated.appendChild(d.createTextNode(rs.getString("dated")));
    root.appendChild(dated);

    Element message = d.createElement("Message");
    message.appendChild(d.createTextNode(rs.getString("message")));
    root.appendChild(message);
   }else{
    response.sendError(404 , num + " Not Found");
    error = true;
   }

   ps.close();
  }

  if(!error){
   Transformer tf = TransformerFactory.newInstance().newTransformer();
   DOMSource ds = new DOMSource();
   ds.setNode(d);
   StreamResult sr = new StreamResult(out);
   tf.transform(ds , sr);
  }
 }else if("POST".equalsIgnoreCase(method) || 
  "PUT".equalsIgnoreCase(method)){
  String number = null;
  String title = null;
  String dated = null;
  String message = null;

  try{
   DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
   dbf.setNamespaceAware(true);
   InputStream is = request.getInputStream();
   DocumentBuilder db = dbf.newDocumentBuilder();
   Document d = db.parse(is);
   is.close();

   Element base = d.getDocumentElement();
   try{
    number = base.getElementsByTagName("Number").item(0).
     getFirstChild().getNodeValue();
   }catch(Exception e){
   }
   title = base.getElementsByTagName("Title").item(0).
    getFirstChild().getNodeValue();
   dated = base.getElementsByTagName("Date").item(0).
    getFirstChild().getNodeValue();
   message = base.getElementsByTagName("Message").item(0).
    getFirstChild().getNodeValue();

   // PUT の場合は項目が存在していなければならない
   // POST の場合は存在していてはならない
   boolean error = false;

   PreparedStatement ps = c.prepareStatement(
    "SELECT COUNT(*) AS 'value' FROM rest_title WHERE number = ?");
   ps.setString(1 , number);
   ResultSet rs = ps.executeQuery();
   rs.next();

   if(rs.getInt(1) >= 1){ // 存在する
    if("POST".equalsIgnoreCase(method)){
     error = true;
     response.sendError(500 , "Column ID " + number + " Exist");
    }
   }else{ // 存在しない
    if("PUT".equalsIgnoreCase(method)){
     error = true;
     response.sendError(404 , "Column ID " + number + " Not Found");
    }
   }

   if(!error){
    ps = c.prepareStatement(
     "REPLACE INTO rest_title VALUES(? , ? , ? , ?)");
    ps.setString(1 , number);
    ps.setString(2 , title);
    ps.setString(3 , dated);
    ps.setString(4 , message);
    ps.executeUpdate();
    ps.close();

    c.commit();
   }
  }catch(Exception e){
   response.sendError(500 , e.toString());
  }
 }else if("DELETE".equalsIgnoreCase(method)){
  int number = -1;
  if(request.getParameter("number") != null){
   try{
    number = Integer.parseInt(request.getParameter("number"));
   }catch(NumberFormatException e){
   }

   // 指定された記事を削除
   PreparedStatement ps = c.prepareStatement(
    "DELETE FROM rest_title WHERE number = ?");
   ps.setInt(1 , number);
   ps.executeUpdate();
   ps.close();
   c.commit();
  }
 }
}catch(SQLException e){
 response.sendError(500 , e.toString());
}

if(c != null)
 c.close();
%>
 極めていい加減なプログラムではありますが、何とか動きます。SQLを使っていますので、以下のテーブルもあわせて作成します。
CREATE TABLE rest_title(number INT AUTO_INCREMENT , 
title VARCHAR(256) , dated DATETIME , message MEDIUMTEXT , 
PRIMARY KEY(number));
 使用するテーブルはこれ1つだけです。記事にカテゴリなども設けて擬似ブログ風味にしようとも考えたのですが、ややこしくなると理解の邪魔になりますので省きました。私の方針からして、ブログは単一カテゴリにはなりえませんので、カテゴリを導入するだけで2つテーブルが増えます(カテゴリ定義テーブルとマップテーブル)。
 さて、このプログラムですが、GETであればブラウザでも確認できます。まず手動でデータを登録しておいて、
INSERT INTO rest_title VALUES(NULL , 'Title' , NOW() , 'Message');
 「restserver.jsp?type=list」にアクセスすればデータの一覧が、「restserver.jsp?type=detail&number=number」にアクセスすれば記事のデータが取得できます。しかし、POSTならフォームを作るなどの工夫が必要ですし、PUTやDELETEは対応していないブラウザが多い上、これらのフォームデータはクエリ文字列として送信されてしまうため、データに直接XMLを送ることはできません。
 ここはデータを送るプログラムも書いてしまいましょう。
// restsender.jsp
<%@page import="java.util.* , java.net.* , java.io.*" 
contentType="text/html;charset=UTF-8" pageEncoding="Shift_JIS" %>

<%
request.setCharacterEncoding("UTF-8");

if(request.getParameter("uri") != null){
	String method = request.getParameter("method");

	HttpURLConnection c = (HttpURLConnection)
		new URL(request.getParameter("uri")).openConnection();
	c.setDoInput(true);
	c.setDoOutput(true);
	c.setRequestMethod(method);

	try{
		if(method.equals("POST") || method.equals("PUT")){
			OutputStream os = c.getOutputStream();
			os.write(request.getParameter("data").
				getBytes("UTF-8"));
			os.close();
		}

		c.connect();
		InputStream is = c.getInputStream();

		byte b[] = new byte[4096];
		is.read(b);
		out.println(new String(b , "UTF-8"));

		is.close();
	}catch(IOException e){
		out.println("Response code : " + c.getResponseCode() + 
			"<br><br>");

		InputStream is = c.getErrorStream();
		byte b[] = new byte[4096];
		is.read(b);
		is.close();

		out.println(new String(b , "UTF-8"));
	}

	c.disconnect();
}
%>

<html>
<head>
<title>REST Sender</title>
</head>
<body>

<b>REST Sender</b>

<form method="POST" action="?">
送信するデータと送信先を記入してください。<br>
<br>

送信先<br>
<input type="text" name="uri" size="60" 
value="http://www.localyamicha.com/restserver.jsp"><br>
"DELETE"の場合 : restserver.jsp?number=<i>num</i><br>
<br>

送信するメソッド<br>
<select name="method">
<option value="POST">POST
<option value="PUT">PUT
<option value="DELETE">DELETE
</select><br>
<br>

送信するデータ<br>
<textarea cols="60" rows="8" name="data">
<Root>
 <Number>1</Number>
 <Title>タイトル</Title>
 <Date>2007-08-28 20:30:40</Date>
 <Message>本文</Message>
</Root>
</textarea><br>
<br>

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

</body>
</html>
 作成・修正・削除用の専用インターフェイスをそれぞれ書いても構わなかったのですが、送信されるデータを理解しやすくするためにこのような書き方になっています。
 それでは使い方を。まず、ドロップダウンで「POST」を選んだ場合、「送信するデータ」部分に登録すべきデータをXMLで書きます。構造は次の通りです。
<Root>
 <Number>記事番号(INT)</Number>
 <Title>タイトル(VARCHAR(256))</Title>
 <Date>SQL で認識できる日付(DATETIME)</Date>
 <Message>本文(MEDIUMTEXT)</Message>
</Root>
 ただし、POSTの場合はNumberノードを省略することができます(他のノードは必須)。また、既存の記事番号を指定すると500エラーが発生します。
 ドロップダウンで「PUT」を選択した場合でも、記述方法はおおむねPOSTと同じです。ただし、これは既存の記事を修正するものであるため、Numberノードも必須になります。Numberノードで存在しない記事番号を指定すると404エラーが発生します。
 「DELETE」に関しては、「送信するデータ」の記述は無視されます。ただし、「送信先」のURLに続けて「?number=number」と記述することで、指定した記事を削除することができます。URLで記事番号が指定されていなかったり、存在しない記事番号が指定されていたりすると、404エラーが発生します。
 GETに関しては前述の通りブラウザから使用します。引数のリストは以下の通りです。

1.type
 必須。値は"list"か"detail"のいずれかになります。前者は記事のリストを表示し、後者は記事の詳細を表示します。

1.1.max
 任意。type=listの場合のみ有効です。デフォルト値は10です。取得する記事の最大数を指定します(10であれば1度に最大10件まで取得)。

1.2.page
 任意。type=listの場合のみ有効です。デフォルト値は0です。何件目の記事から見始めるかを指定します。例えば「type=list&max=10&page=0」の状態で、データが10件を超えていて表示しきれない場合、「type=list&max=10&page=10」で次のページを表示できます。

2.1.number
 必須。type=detailの場合のみ有効です。詳細表示する記事の番号を指定します。

 つまり、リスト表示の場合は「?type=list」のみでも動作しますし、「?type=list&page=40&max=20」のような指定方法も可能です。詳細表示なら「?type=detail&number=1」の書き方になります。
 これがRESTの基本概念とのこと。PUTやDELETEメソッドが必要であり、ブラウザから直接使うのには不向きですが、専用のアプリケーションを作って利用したり、RESTにアクセスするCGIなりServletなりを書いて、これを経由してブラウザからアクセスするなど、色々使い道はありそうです。実際RESTを提供しているサイトは結構存在するようです。
 しかし、それ以前にRSSすらRESTとみなせることを忘れてはいけません。このブログも知らぬ間にRESTらしきものに対応していることになります。抽象的なワード(SOA、Web 2.0、RESTなど)は声高に叫ばれる割に定義がはっきりせず、いざ学習しようとした時にはすでに実現済みであったりして、なかなかややこしいです。
カテゴリ [開発魔法][社会問題] [トラックバック 0][コメント 0]
<- 前の記事を参照 次の記事を参照 ->

- Blog by yamicha.com -