2010年11月アーカイブ



 

 

 HTML5という言葉が登場して、もうかなりの時間が経つと思うが、

まだまだ海のものか山のものかもわからない人も多いのではと思う。

※実際にHTML5は、2008年1月22日にドラフト(草案)が出されているが、

勧告については、まだ先で、2012年3月頃に正式に勧告予定とのこと。

 

私自身もまだまだ、HTML5についての理解も低いので、今後ますます

ホットになっていくだろう、HTMLを少しづつ勉強していきたいと思う。

 

今回は、「Webの3つの問題を解決する「HTML5」とは何なのか」という

記事があったので、こちらを参考にして、HTML5を利用して、Web上の

どんな問題が解決するのか見ていきたい。

 

まず私の知っている範囲でのHTML5は、これまでのHTML4.01に

取って代わるものであること、さらに仕様が大きく拡張され、

マルチメディアの情報も扱えるようになるなど、様々な機能が取り込まれ

ているというイメージです。

 

上記のページでも、

「オープンな標準をベースとした、Webのプラットフォーム化」という動きの

すべてが、最近「HTML5」と呼ばれている」というあいまいな表現が用い

られています。CSSやSVGもまとめてHTML5と呼ばれてしまうこともあるとか・・

 

さて、今回は、このあいまいさについては許容したままで、本題の、

HTML5が解決する、Webの3つの問題を見ていきたい。

 

3つの問題とは??

 

1.Webブラウザ間の互換性が低い

2.文書内に埋め込まれた「意味」が不明確

3.Webアプリの機能が制限されている

と書かれている。

 

1.については、過去から常に問題となってきていましたが、

HTML5では、既存の各ブラウザの機能をしっかり分析し、

Webブラウザ間の互換性を向上させる活動を行っている

ということです。

 

2.については、それゆえにHTMLが単純になり、普及した

原因でもあると思うが、HTML5では、文書内に埋め込まれた

「意味」を明確にするセマンティックな要素が追加され、

アクセシビリティの向上が図られるようです。

 

3.については、最近普及しているリッチなWebアプリですが、

HTMLの貧弱さを様々な技術を駆使して補ってきた結果です。

今後HTML5では、様々なAPIが事前に検討され、よりリッチな

Webアプリを構築可能にし、Webアプリと通常のアプリの違いが

きわめて少なくなるとされています。

 

今後も、これ以外にも、HTML5の特徴や機能について、

いろいろと勉強していきたいと思います。

 

 

 

2010年11月に、ドコモ、ソフトバンク、auの各社から、Android携帯の

新製品発表、新製品予約受付が立て続けに行われた。

 

スマートフォンでは、iPhone4がダントツのシェアを伸ばしてきたが、

今回の新製品発売により、よりオープンであるAndroid端末のシェアが

確実に伸びてくるだろう。

 

また、Android Marketへのアプリの登録も、アプリの開発環境についても

Appleよりも敷居が低いため、今まで以上にアプリの市場が活性化すると

思われる。

 

Androidアプリの作成については、様々なサイトや、書籍でも詳しく書かれているが、

今回は、以下のサイトを紹介したいと思う。

 

「Androidアプリ作成入門」

 

※連載形式で以下の内容でAndroidアプリの作成が学べるようになっています。

また、最後に最新の連載である、「Androidアプリでアニメーションするための基礎知識」

の内容を簡単に説明したいと思う。

 

Android Market配布を目指しEclipseでHelloWorld!

ドコモやauでも端末が発売されるAndroid。

その前に自分が作ったアプリを配布できるようにしておくと大きなチャンスです!

 

Androidアプリ作成の基本"Activity"とは何か?

開発者向け実機の登場やOHA参加14社増などの話題で盛り上がるAndroid。

今回は、基本Activityとライフサイクルについて

 

ブラウザや地図、ストリートビューの基、Intentとは?

オープンソースのためネットブックなどへの移植も期待されるAndroid。

今回は、さまざまな機能の基Intentについて

 

簡単でワクワクするAndroidウィジェット10連発!

携帯Javaアプリの見た目はiPhoneに比べてカッコ悪いなぁ、と思っている方へ。

同じJavaでもAndroidだとここまで違うんです!

 

Androidアプリの使いやすさを左右する5つのレイアウト

カッコいいウィジェットを配置するにはAndroidのレイアウトという仕組みを利用します。

分かりやすさや入力しやすさも追究しましょう

 

AndroidでSQLiteのDB操作をするための基礎知識

ついに日本でもドコモからHTC製端末の発売が決定したAndroid。

今回はデータベースを操作するための基礎を解説します

 

常駐アプリが作成できるAndroidの"サービス"とは

先日、日本語のサポートを開始したAndroid Market。

アプリ配信に向けて、今回は非常に便利な"サービス"について解説します

 

アプリを国際化してAndroid Marketから世界へ発信

作成したアプリを多くのユーザーに使ってもらうために国際化する方法を学びましょう。

実は、多種端末対応にもつながります

 

Netbookにも広まるAndroidで、かつてないWeb体験を

WebKitを搭載しているAndroidでは、アプリの一部としてHTMLを表示でき、

JavaScriptとアプリを連動させることも可能です

 

Androidのホーム画面に常駐するアプリを作るには

待ち受けウィジェットを作るには、これまで学んできたレイアウトやサービス、

インテントの知識を総動員する必要があります

 

Android 1.6のジェスチャーとテキスト読み上げを使う

ジェスチャーに得点を付けて比較するサンプルや、

テキスト読み上げ機能で「しゃべるAndroid」を実現するサンプルを作ります

 

SurfaceViewならAndroidで高速描画ゲームが作れる

Androidのグラフィックスに関する重要なポイントや、

SurfaceViewでUIイベントを駆使した描画と背景を透過させる方法を解説

 

iPhoneより多彩なAndroidのセンサをアプリで操作

Androidで使えるさまざまなセンサを紹介し、

加速度や磁気、方位、温度などの値を取得して使うアプリの作り方を解説します

 

Android 2.1の新機能で作る、美しく燃える"待ち受け"

新しく追加された、動く壁紙「Live Wallpaper」機能のサンプル動画を表示し、

構成、設定ファイル、実装の仕方を解説します

 

Android NDKでJNIを使用してアプリを高速化するには

C/C++やOpenGL ESといったネイティブコードを使うためのNDKとJNIを紹介し、

その使い方や注意点を徹底解説します

 

地図/位置情報/GPSを使うAndroidアプリを作るには

Android Maps API Keyの取得方法を紹介し、

その使い方やさまざまな設定/表示と操作方法、注意点を徹底解説します

 

もはやケータイに必須のカメラをAndroidで制御しよう

標準カメラ制御用APIを解説します。起動・終了・撮影だけでなく

フォーカスやフラッシュなども制御できます。ARに応用も!?

 

開発者が知っておきたいAndroid 2.2の新機能12連発

先日ソースコードが公開されたFroyoの新機能を紹介します。

Flash PlayerやJITコンパイラ、マルチタッチのサポートなど

 

XMLレイアウトでAndroidアプリに"設定画面"を追加

アプリには欠かせない設定画面について、基になるクラスや使えるコンポーネント、

XMLによるレイアウトの仕方を解説します

 

Androidアプリで"アニメーション"するための基礎知識

さまざまなアニメーション機能のサンプル動画を表示し、

実装の仕方やオプション、設定ファイル、使う際の落とし穴を解説します

 

*

 

さて、最新回の「Androidアプリで"アニメーション"するための基礎知識」ですが、

Androidでは、アニメーションのための機能が最初から充実されています。

これは、アプリを使用するにあたり、アニメーションの機能がかなり重要視されている

に他なりません。アプリ選択の基準にアニメーションが大きく影響を与えているともいえます。

 

Androidのアニメーションの機能には、4つの基本動作と合成の機能があります。

  • フェイドイン/アウト
  • 回転
  • 拡大・縮小
  • 移動
  • 複数のアニメーションを合成

※これらの詳細と、サンプルコードは、上記リンクのページに書かれていますので参照ください。

 

次に、Animationの代表的なオプション8つですが、以下のとおりとなります。

  • キャンセル
  • アニメーション時間の設定
  • 繰り返し回数の設定
  • 繰り返しモードの設定
  • 開始時間の設定
  • Zオーダーの設定
  • アニメーション後の状態の制御
  • リスナの設定

※これらはアニメーションの動作を細かく設定するためのものになりますので、

しっかり内容と設定方法を把握しましょう。

 

最後に、アニメーションに変化をもたらす「Interpolator」について紹介されています。

Interpolatorを「Animation#setInterpolator(Interpolator)」メソッドで設定することによって、

アニメーションの動作に変化を加えられます。

 

以上、詳細は連載記事を参照いただき、作成したアプリに是非とも、センスの良い

アニメーションを加えることにより、アプリの質を高めてください。

 

 

 

Google App Engine上でStruts2.2を動かす場合には、

Struts2を単独で動かす場合よりも、いくつかの設定変更や、

対応が必要になります。

 

今回は、GAE(Google App Engine)上で、Twitter連携アプリの

サンプルをStruts2.2で作成してみました。

Twitter連携には、Twitter4Jというライブラリを利用しています。

 

開発環境は、Eclipse3.4.2

Struts2.2.1

Google App Engineは、1.3.8

GWT(Google Web Toolkit)は、2.1.0

Twitter4J 2.1.6

を利用しています。

 

サンプルアプリは、ここを参照してください。

 パブリックタイムラインまたは、入力したIDのユーザタイムラインを表示します。

これらは全て認証の不要なAPIで作成可能になっています。

 

正常に稼働させるまでには、大きく2点障害がありました。

ローカル環境で、動作させるまでに必要な対応、

そしてデプロイ時のエラーを回避するための対応です。

 

1点目のローカル環境で動作させるまでの対応については、

先日のブログ記事で詳細を記述していますので、

そちらを参照してください。

※そしてさらに 追加でもう一点対応が必要になりました。

これは、<s:form action="sample"></s:from>のタグで、

フォーム入力の画面を作成する場合にGAEではエラーが発生するようです。

原因の詳細は不明なのですが、実行時に以下のエラーが発生します。

 

java.lang.NoClassDefFoundError: javax.swing.tree.TreeNode is a restricted class. Please see the Google  App Engine developer's guide for more details.


対応方法は、上記で見つからないと言われるクラスをソースフォルダに定義する必要があるようです。

詳細は、以下の記事の"Struts2を使うには"の2点目の対応が必要になります。

ふじやん雑記帳 - Google App Engine

 

追加するクラスのソースを念のため、以下にも載せておきます。

/*
 * Copyright (c) 2003 The Visigoth Software Society. All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above
copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowledgement:
 *       "This product includes software developed by the
 *        Visigoth Software Society (http://www.visigoths.org/)."
 *    Alternately, this acknowledgement may appear in the software
itself,
 *    if and wherever such third-party acknowledgements normally
appear.
 *
 * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names
of the
 *    project contributors may be used to endorse or promote products
derived
 *    from this software without prior written permission. For written
 *    permission, please contact visigo...@visigoths.org.
 *
 * 5. Products derived from this software may not be called
"FreeMarker" or "Visigoth"
 *    nor may "FreeMarker" or "Visigoth" appear in their names
 *    without prior written permission of the Visigoth Software
Society.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Visigoth Software Society. For more
 * information on the Visigoth Software Society, please see
 * http://www.visigoths.org/
 */

package freemarker.core;

import java.io.IOException;

/**
 * A TemplateElement representing a block of plain text.
 *
 * @version $Id: TextBlock.java,v 1.17 2004/01/06 17:06:42 szegedia Exp $
 */
public final class TextBlock extends TemplateElement {
 private static final char[] EMPTY_CHAR_ARRAY = new char[0];
 static final TextBlock EMPTY_BLOCK = new TextBlock(EMPTY_CHAR_ARRAY, false);
 // We're using char[] instead of String for storing the text block because
 // Writer.write(String) involves copying the String contents to a char[]
 // using String.getChars(), and then calling Writer.write(char[]).By
 // using Writer.write(char[]) directly, we avoid array copying on each
 // write.
 private char[] text;
 private final boolean unparsed;

 public TextBlock(String text) {
 this(text, false);
 }

 public TextBlock(String text, boolean unparsed) {
 this(text.toCharArray(), unparsed);
 }

 private TextBlock(char[] text, boolean unparsed) {
 this.text = text;
 this.unparsed = unparsed;
 }

 /**
 * Simply outputs the text.
 */
 public void accept(Environment env) throws IOException {
 env.getOut().write(text);
 }

 public String getCanonicalForm() {
 String text = new String(this.text);
 if (unparsed) {
 return "<#noparse>" + text + "</#noparse>";
 }
 return text;
 }

 public String getDescription() {
 String s = new String(text).trim();
 if (s.length() == 0) {
 return "whitespace";
 }
 if (s.length() > 20) {
 s = s.substring(0, 20) + "...";
 s = s.replace('\n', ' ');
 s = s.replace('\r', ' ');
 }
 return "text block (" + s + ")";
 }

 TemplateElement postParseCleanup(boolean stripWhitespace) {
 if (text.length == 0)
 return this;
 int openingCharsToStrip = 0, trailingCharsToStrip = 0;
 boolean deliberateLeftTrim = deliberateLeftTrim();
 boolean deliberateRightTrim = deliberateRightTrim();
 if (!stripWhitespace || text.length == 0) {
 return this;
 }
 if (parent.parent == null && previousSibling() == null)
 return this;
 if (!deliberateLeftTrim) {
 trailingCharsToStrip = trailingCharsToStrip();
 }
 if (!deliberateRightTrim) {
 openingCharsToStrip = openingCharsToStrip();
 }
 if (openingCharsToStrip == 0 && trailingCharsToStrip == 0) {
 return this;
 }
 this.text = substring(text, openingCharsToStrip, text.length
 - trailingCharsToStrip);
 if (openingCharsToStrip > 0) {
 this.beginLine++;
 this.beginColumn = 1;
 }
 if (trailingCharsToStrip > 0) {
 this.endColumn = 0;
 }
 return this;
 }

 /**
 * Scans forward the nodes on the same line to see whether there is a
 * deliberate left trim in effect. Returns true if the left trim was
 * present.
 */
 private boolean deliberateLeftTrim() {
 boolean result = false;
 for (TemplateElement elem = this.nextTerminalNode(); elem != null
 && elem.beginLine == this.endLine; elem = elem
 .nextTerminalNode()) {
 if (elem instanceof TrimInstruction) {
 TrimInstruction ti = (TrimInstruction) elem;
 if (!ti.left && !ti.right) {
 result = true;
 }
 if (ti.left) {
 result = true;
 int lastNewLineIndex = lastNewLineIndex();
 if (lastNewLineIndex >= 0 || beginColumn == 1) {
 char[] firstPart = substring(text, 0,
 lastNewLineIndex + 1);
 char[] lastLine = substring(text, 1 + lastNewLineIndex);
 if (trim(lastLine).length == 0) {
 this.text = firstPart;
 this.endColumn = 0;
 } else {
 int i = 0;
 while (Character.isWhitespace(lastLine[i])) {
 i++;
 }
 char[] printablePart = substring(lastLine, i);
 this.text = concat(firstPart, printablePart);
 }
 }
 }
 }
 }
 if (result) {
 }
 return result;
 }

 /**
 * Checks for the presence of a t or rt directive on the same line. Returns
 * true if the right trim directive was present.
 */
 private boolean deliberateRightTrim() {
 boolean result = false;
 for (TemplateElement elem = this.prevTerminalNode(); elem != null
 && elem.endLine == this.beginLine; elem = elem
 .prevTerminalNode()) {
 if (elem instanceof TrimInstruction) {
 TrimInstruction ti = (TrimInstruction) elem;
 if (!ti.left && !ti.right) {
 result = true;
 }
 if (ti.right) {
 result = true;
 int firstLineIndex = firstNewLineIndex() + 1;
 if (firstLineIndex == 0) {
 return false;
 }
 if (text.length > firstLineIndex
 && text[firstLineIndex - 1] == '\r'
 && text[firstLineIndex] == '\n') {
 firstLineIndex++;
 }
 char[] trailingPart = substring(text, firstLineIndex);
 char[] openingPart = substring(text, 0, firstLineIndex);
 if (trim(openingPart).length == 0) {
 this.text = trailingPart;
 this.beginLine++;
 this.beginColumn = 1;
 } else {
 int lastNonWS = openingPart.length - 1;
 while (Character.isWhitespace(text[lastNonWS])) {
 lastNonWS--;
 }
 char[] printablePart = substring(text, 0, lastNonWS + 1);
 if (trim(trailingPart).length == 0) {
 // THIS BLOCK IS HEINOUS! THERE MUST BE A BETTER
 // WAY! REVISIT (JR)
 boolean trimTrailingPart = true;
 for (TemplateElement te = this.nextTerminalNode(); te != null
 && te.beginLine == this.endLine; te = te
 .nextTerminalNode()) {
 if (te.heedsOpeningWhitespace()) {
 trimTrailingPart = false;
 }
 if (te instanceof TrimInstruction
 && ((TrimInstruction) te).left) {
 trimTrailingPart = true;
 break;
 }
 }
 if (trimTrailingPart)
 trailingPart = EMPTY_CHAR_ARRAY;
 }
 this.text = concat(printablePart, trailingPart);
 }
 }
 }
 }
 return result;
 }

 /*
 * private String leftTrim(String s) { int i =0; while (i<s.length()) { if
 * (!Character.isWhitespace(s.charAt(i))) break; ++i; } return
 * s.substring(i); }
 */
 private int firstNewLineIndex() {
 String content = new String(text);
 int newlineIndex1 = content.indexOf('\n');
 int newlineIndex2 = content.indexOf('\r');
 int result = newlineIndex1 >= 0 ? newlineIndex1 : newlineIndex2;
 if (newlineIndex1 >= 0 && newlineIndex2 >= 0) {
 result = Math.min(newlineIndex1, newlineIndex2);
 }
 return result;
 }

 private int lastNewLineIndex() {
 String content = new String(text);
 return Math.max(content.lastIndexOf('\r'), content.lastIndexOf('\n'));
 }

 /**
 * figures out how many opening whitespace characters to strip in the
 * post-parse cleanup phase.
 */
 private int openingCharsToStrip() {
 int newlineIndex = firstNewLineIndex();
 if (newlineIndex == -1 && beginColumn != 1) {
 return 0;
 }
 ++newlineIndex;
 if (text.length > newlineIndex) {
 if (newlineIndex > 0 && text[newlineIndex - 1] == '\r'
 && text[newlineIndex] == '\n') {
 ++newlineIndex;
 }
 }
 if (new String(text).substring(0, newlineIndex).trim().length() > 0) {
 return 0;
 }
 // We look at the preceding elements on the line to see if we should
 // strip the opening newline and any whitespace preceding it.
 for (TemplateElement elem = this.prevTerminalNode(); elem != null
 && elem.endLine == this.beginLine; elem = elem
 .prevTerminalNode()) {
 if (elem.heedsOpeningWhitespace()) {
 return 0;
 }
 }
 return newlineIndex;
 }

 /**
 * figures out how many trailing whitespace characters to strip in the
 * post-parse cleanup phase.
 */
 private int trailingCharsToStrip() {
 String content = new String(text);
 int lastNewlineIndex = lastNewLineIndex();
 if (lastNewlineIndex == -1 && beginColumn != 1) {
 return 0;
 }
 String substring = content.substring(lastNewlineIndex + 1);
 if (substring.trim().length() > 0) {
 return 0;
 }
 // We look at the elements afterward on the same line to see if we
 // should strip any whitespace after the last newline
 for (TemplateElement elem = this.nextTerminalNode(); elem != null
 && elem.beginLine == this.endLine; elem = elem
 .nextTerminalNode()) {
 if (elem.heedsTrailingWhitespace()) {
 return 0;
 }
 }
 return substring.length();
 }

 boolean heedsTrailingWhitespace() {
 if (isIgnorable()) {
 return false;
 }
 for (int i = 0; i < text.length; i++) {
 char c = text[i];
 if (c == '\n' || c == '\r') {
 return false;
 }
 if (!Character.isWhitespace(c)) {
 return true;
 }
 }
 return true;
 }

 boolean heedsOpeningWhitespace() {
 if (isIgnorable()) {
 return false;
 }
 for (int i = text.length - 1; i >= 0; i--) {
 char c = text[i];
 if (c == '\n' || c == '\r') {
 return false;
 }
 if (!Character.isWhitespace(c)) {
 return true;
 }
 }
 return true;
 }

 boolean isIgnorable() {
 if (text == null || text.length == 0) {
 return true;
 }
 if (!isWhitespace()) {
 return false;
 }
 // trick here
 boolean atTopLevel = true;
 TemplateElement prevSibling = previousSibling();
 TemplateElement nextSibling = nextSibling();
 return ((prevSibling == null && atTopLevel) || nonOutputtingType(prevSibling))
 && ((nextSibling == null && atTopLevel) || nonOutputtingType(nextSibling));
 }

 private boolean nonOutputtingType(TemplateElement element) {
 return (element instanceof Macro || element instanceof Assignment
 || element instanceof AssignmentInstruction
 || element instanceof PropertySetting
 || element instanceof LibraryLoad || element instanceof Comment);
 }

 private static char[] substring(char[] c, int from, int to) {
 char[] c2 = new char[to - from];
 System.arraycopy(c, from, c2, 0, c2.length);
 return c2;
 }

 private static char[] substring(char[] c, int from) {
 return substring(c, from, c.length);
 }

 private static char[] trim(char[] c) {
 if (c.length == 0) {
 return c;
 }
 return new String(c).trim().toCharArray();
 }

 private static char[] concat(char[] c1, char[] c2) {
 char[] c = new char[c1.length + c2.length];
 System.arraycopy(c1, 0, c, 0, c1.length);
 System.arraycopy(c2, 0, c, c1.length, c2.length);
 return c;
 }

 boolean isWhitespace() {
 return text == null || trim(text).length == 0;
 }

}

 

次に、2点目ですが、ローカルでの実行に問題が無くても

デプロイ時に不明なエラーが発生しました。エラーの内容は、

こちらの内容を参照してください。

私の環境では、web.xml および、appengine-web.xml で、

ファイルがパース出来ないという意味合いのエラーの内容です。

 

Received IOException parsing the input stream for <該当のxmlファイルのパス>

 

上記のページでも最後の方で解決していますが、

結局のところ、該当のxmlファイル内のコメントを全て削除することで解決しました。

納得はいきませんが、とりあえず動作させる事を優先で対応しました。

 

以上で、やっと・・なんとか、

GAE+Struts2.2(Zero Configuration、Convention-Plugin)+Twitter連携アプリが

完成しました。

サンプルのアプリは、ここを参照してください。

 

Twitter連携用のAPIとしては、

 Twitter4J#getPublicTimeline メソッド

 Twitter4J# showUser メソッド

 Twitter4J#showStatus メソッド

を利用しています(いずれも認証不要)

 

今後も、様々なTwitter連携APIのサンプルを作成してみたいと思います。

 

Struts2の大きな特徴である、

Zero Configuration(アクションマッピング等の設定をファイルに記述しない)

ですが、2.0系で使用していた、Codebehindプラグインが非推奨なった。

 

現時点(2010/11/3)での、Struts2のバージョンも2.2.1であるため、

最新のバージョンへの更新および、Codebehindプラグインをやめ、

Conventionプラグインへの切換えをおこなってみた。

しかも、GAE(Google App Engine)環境で。

 

仕組み自体はそう大して変わっていないと思っていたが・・・

やはり簡単には移行できなかったので、あれこれと調べる羽目になり、

時間も結構かかったが、なんとか動作するようになり、

Conventionプラグインについても見えてきた。

 

とりあえず、つまづき度が大きかった順に・・・

※これはGAE(Google App Engine)でテストしていたのも重なり、障害の

切り分けに時間がかかってしまったような気もするが・・・

障害としては、URLで該当のアクションwp呼び出すURLを入力しても、

「アクションが見つからない」とのメッセージ。

 

"There is no Action mapped for namespace /"

 

Codebehindプラグインを使用していた時にもハマったエラー。

その際は次のように、"actionPackages"というパラメータを

web.xmlで設定すれば、指定した名前空間の下にあるアクションクラスを

参照してくれるようになった。

 

 <filter>
  <filter-name>struts2</filter-name>
  <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
  <init-param>
   <param-name>actionPackages</param-name>
   <param-value>com.pg2se.sample.action</param-value>
  </init-param>
 </filter>

しかし、今回は同様のパラメータ設定をしていてもうまくアクションが呼び出されない・・

そして、どこかのページで、Struts.xml の配置が必要で、

"<constant name="struts.devMode" value="true" />"の設定をしろ!のようなページを

見てしまったため、

"access denied (java.io.FilePermission jar:file:\C:\Projects\gae\war\WEB-INF\lib\struts2-core-2.2.1.jar read)"

というエラーも出るようになったりで、Struts2が悪いのか、GAEか、どこがどう悪いかが見当つかなくなった。

 

もう1点が、Resultのjspファイルが見つからない旨のエラーが発生したこと。

 

"No result defined for action "のエラーメッセージ。

 

アクションは実行されたようだだが、ビューファイルが見つからない。

これも今までは普通にjspを配置すれば動いていたのだが・・・

 

結局、順番にひとつづつ試していってわかったこと。

1.Struts.xml は無くても動作はした。

 

2.Struts.xml の"<constant name="struts.devMode" value="false" />"でvalueはfalseにする。

  ※これはGAE環境だからかもしれない・・

 

3.GAE環境では、セキュリティマネージャを無効にする必要がある

  ※web.xmlにリスナーを設定してセキュリティマネージャを無効にする。

 

     <listener><!-- GAE関連で必要なListenerを設定 -->

         <listener-class>com.pg2se.google.InitListener</listener-class>

     </listener>

 

    public class InitListener implements ServletContextListener, HttpSessionListener, HttpSessionAttributeListener {

        @override

        public void contextInitialized(ServletContextEvent arg0) {
            OgnlRuntime.setSecurityManager(null);
        }

    }

  こんな感じ

 

4.これもGAE環境でのエラーのはず。

  クラスが見つからない旨のエラー。→xalan関連のクラス。

  こちらはxalanのライブラリをダウンロードして手動で追加する必要があった。

 

5.アクションクラスが見つからない件

  上記の設定で、すべて再起動、再ビルドするとアクションは実行されるようになった。

  ※因みにConventionプラグインでは、web.xmlでの、"actionPackages"パラメータは

  記述しなくて良く、アクションクラスの格納されたパッケージは自動検出される。

  →action,actions,struts,struts2の名前のついたパッケージが対象

 

6.ビューファイルが見つからない件

  Conventionプラグインでは、ビューファイル(jsp等)の配置場所もデフォルトで設定

  されている。それが、/WEB-INF/content/ です。

  ※そりゃ、今までどおりでは、resultのファイルが呼び出されない訳だ。

  取り急ぎ、jspのビューファイルを、上記場所に移動。

 

以上で、なんとかこれまでどおり、Struts2が動作するようになった。(しかもGAE上で)

Conventionプラグインン・・・なかなか癖があり、手ごわかった。

 

さて、これまでは、Conventionプラグインのデフォルトの設定を生かしたが、

カスタマイズするには?だけど・・・、方法はあります。

 

struts.xmlファイルに<constant>タグで設定を記述することによって、既定値を変更することが可能です。

例)<constant name="struts.convention.result.path" value="/WEB-INF/jsp/"/>

  <constant name="struts.convention.package.locators" value="act"/>

 

他にも設定変更の詳細や、今回ハマった内容のほとんどについては、

以下のページで詳しく解説されています。

Struts 2入門(6)?XML不要のZero Configuration?

Google App Engine/Struts2の連携

 

今後、Struts2.2、GAEを連携させてより深いところまで、

Struts2を追及していこうと思います。