2009年4月30日木曜日

久し振りの間隔

最近JDOを触っています。
GAEを触ることがきっかけで行い始めましたけど、
電子工学を習ったころ、
プログラミング初のオブジェクト指向、
Webを触り始めた頃、
ハードウェアを触り始めたころ。。。
その辺りの新鮮さを感じているところです。

新しいというより、、、
なんか今まで習ってきたことを復習させられていて
楽しい感じに近いです。

ん。。。いいっすね。

2009年4月29日水曜日

GAEのバージョン管理

ローカルでの動作確認もありますが
デプロイした状況での確認も必要になります。

そこでデプロイのバージョニングを行います。

http://appengine.google.com/にアクセスすると
自分のアプリケーションの管理画面になると思います。
対象のアプリケーションを選択して
右にあるVersionsをクリックすると




という画面が出力されます。
ここのバージョンはeclipseのプロジェクトの設定で
[Google]→[App Engine]でデプロイメントのバージョンになります。
※1~の整数を指定します。

指定したバージョンでデプロイして
上記画面で右にあるURLでテストが行えます。
ちなみに初めて試した時に例外を見事発生させました。

JDOの1対多

結構はまりました。
ここにあるようにバグにより
親のオブジェクト、子のオブジェクトのキーはKeyにする必要があるようです。
※子は仕様にも記述してありますけどね。

まず親側のクラスを


class Shop {

@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;

@Persistent(mappedBy = "shop")
private List<Image> imageList;

・・・getter,setterは未記述

}


と記述します。
Keyは通常のIDENTITY(Longで自動設定した場合)と同一で、特に登録などの際に
設定する必要はありません。

そして子のクラスを


class Image {

@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;

@Persistent
private Shop shop;
・・・getter,setterは未記述
}


というように記述します。

で登録は以下のようにします。


PersistenceManager pm = SessionManager.get().getPersistenceManager();
Transaction tx = null;
try {
String id = mogForm.getId();
Long lngId = Long.valueOf(id);

tx = pm.currentTransaction();
tx.begin();

log.warning(id);
Shop oldShop = pm.getObjectById(Shop.class, lngId);

List<Image> imageList = oldShop.getImageList();
if ( imageList == null ) {
imageList = new ArrayList<Image>();
}

imageList.add(image);
image.setShop(oldShop);

tx.commit();
} finally {
if ( tx != null && tx.isActive() ) {
tx.rollback();
}
pm.close();
}


Shopを検索してImageListを取得して
そこに追加を行ってコミットをかけています。
トランザクションをかけてあげないとエラーになります。
このクラスを登録するというより、親に追加して更新しているイメージです。
※子の追加でも可能かは検証してません。

扱っているShopクラスのidの値は
KeyクラスのgetId()により取得したLong値(Stringで使ってる)になります。
そのまま使用すると"Shop(4)"みたいな値になって検索の際にエラーになります。


・・・あぁ。。。やっとこさ関連が実装できた!

2009年4月26日日曜日

JDOで削除

JDOのオブジェクトを削除します。


PersistenceManager pm = SessionManager.get().getPersistenceManager();
try {
Shop oldShop = pm.getObjectById(Shop.class, Long.valueOf(id));
pm.deletePersistent(oldShop);
} finally {
pm.close();
}


更新時と同じくgetObjectById()でオブジェクトを取得して
deletePersistent()で削除を行います。

簡単ですね。

JDOで更新

JDOで更新を行います。



String id = mogForm.getId();
PersistenceManager pm = SessionManager.get().getPersistenceManager();
try {
Shop oldShop = pm.getObjectById(Shop.class, Long.valueOf(id));

String name = mogForm.getName();
String detail = mogForm.getDetail();
String url = mogForm.getUrl();

oldShop.setName(name);
oldShop.setDetail(detail);
oldShop.setUrl(url);
oldShop.setEditor(user);
oldShop.setEditDate(new Date());

} finally {
pm.close();
}


getObjectById()を利用して検索を行います。

取得してきたオブジェクトを更新してclose()するだけです。
簡単ですね。

Keyを利用した検索もあるみたい。※それは後日

2009年4月25日土曜日

GAEでオブジェクト比較

GAEでログインしたユーザだけのデータを表示したい。

そう思い立ちました。。。がっ!
JDOのオブジェクト比較にSQLってどうするんだろ?
と思い立ちしばし、、、

でできました。


UserService userService = UserServiceFactory.getUserService();
User user = userService.getCurrentUser();

PersistenceManager pm = SessionManager.get().getPersistenceManager();
Query query = pm.newQuery(Shop.class);

query.setFilter("creator == crrentUser");
query.declareParameters("com.google.appengine.api.users.User crrentUser");

try {
List<Shop> shopList = (List<Shop>)query.execute(user);
for ( Shop oldShop : shopList ) {
Shop newShop = new Shop(oldShop);
shopForm.addShop(newShop);
}
} finally {
query.closeAll();
}


Query::setFilter()でSQLイメージを作って
Query::declareParameters()で型を宣言します。
Query::execute()で比較するオブジェクトを設定しておきます。

これでOKですね!
ログインしたユーザのみの機能もこれでOKですね。

ひとまずSingleton!

さぁ、ひとまず、GoogleAppEngineの
データベースを使うにはSingletonパターンを勉強しましょう!

データベースのリソースを極力使わないのが
システム設計の基本なんですが
まず最初に行わなければいけないのは


package bz.ziro.test.jdo;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;

public class SessionManager {
private static final PersistenceManagerFactory pmfInstance =
JDOHelper.getPersistenceManagerFactory("transactions-optional");
public static PersistenceManagerFactory get() {
return pmfInstance;
}
}




です。
こうすることで


PersistenceManagerFactory pmf = SessionManager.get();


使うことができます。



いやぁ。。。JDOを真面目に勉強してたらびっくりしました。
JDOってJavaの規格なんですね。。。
それでjarを拾いにいったらApacheで
そのjarだけでは実装できませんって言われて
やばっ!って思ってたら
googleもdatanucleusっていうやつを使ってるんですね。
※ってよく見たらreferenceにも書いてありました!!

ん~やっぱGoogleさんの考えてることはわからん、、、

2009年4月23日木曜日

GAEでStruts

さて我が心のフレームワークStruts1.3を
GAEで展開したいと思います。

えーと。。。ひとまずblankアプリからですかね?
まずStrutsのappをおとしてきて、blankのwarを展開します。

で、、、展開したディレクトリから
jspはプロジェクトのwar下に置きます。

index.jspとpages/Welcome.jsp

WEB-INF下のlibとxmlファイル3つ。。。
web.xmlは上書きするかはお任せします。

これらを展開します。

でsrc下にある「MessageResources.properties」をsrcにコピーします。

でappengine-web.xmlに

<sessions-enabled>true</sessions-enabled>

を追記します。


でデプロイ。これで動作します。


。。。素直にGWTにするべきか悩ましい。。。

2009年4月21日火曜日

JDOで保存と検索

さぁGAEでデータベースを取り扱ってみましょう!

JDOを利用して行うことができるようです。
JDOは。。。平たく言うとオブジェクトを保存できる仕組みとでもいいましょうか。。。

下準備は「src/META-INF」に
jdoconfig.xmlを準備する必要があります。

これはeclipseのプラグインでプロジェクトを作成した場合に
勝手にできあがってるみたいです。


それとパーシスタントクラスを準備する必要があります。
このクラスはPOJOと呼ばれる単純なクラスを準備します。


import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import com.google.appengine.api.users.User;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Project {

@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
@PrimaryKey
private Long id;
@Persistent
private String name;
@Persistent
private String detail;
@Persistent
private User creator;
@Persistent
private User editor;

・・・・(getter と setterも準備)
}


残したいデータを準備するのですが
大事なのは「@~」っていう記述方法です。
アノテーションと呼ばれるコメントにより、特性を決める技術です。


@PersistenceCapable(identityType = IdentityType.APPLICATION)
でこのアプリケーションで一意であるデータということ

@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
保存対象でありIDENTITY(一意)であるということ

@PrimaryKey
プライマリーであるということ

@Persistent
保存対象である

ということを意味します。


さぁこれで保存する準備はできました。
画面を作成して保存して一覧に出すようにしてみましょう!

まず実行するJSPを準備


<form name="testForm" action="./JDOServlet" method="post">
<input type="text" name="name">
<textarea name="detail"></textarea>
<input type="submit">
</form>


まぁ普通のHTMLファイルでもOKですね。
でJDOServletをweb.xmlに記述します。


<servlet>
<servlet-name>JDOTestServlet</servlet-name>
<servlet-class>bz.ziro.test.servlet.JDOServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>JDOTestServlet</servlet-name>
<url-pattern>/secure/JDOServlet</url-pattern>
</servlet-mapping>

※servletタグとservlet-mappigタグは順番に並べてください。

で少し長いですがサーブレットは以下のようになります。
※折りたたんであるので広げてください。


public class JDOServlet extends HttpServlet {

/**
*
*/
private static final long serialVersionUID = 1234131818439520488L;

private static PersistenceManagerFactory pmfInstance = JDOHelper.getPersistenceManagerFactory("transactions-optional");
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {

UserService userService = UserServiceFactory.getUserService();
User user = userService.getCurrentUser();

String name = req.getParameter("name");
String detail = req.getParameter("detail");

Project project = new Project();
project.setName(name);
project.setDetail(detail);
project.setCreator(user);

PersistenceManager pm = pmfInstance.getPersistenceManager();
try {
pm.makePersistent(project);
} finally {
pm.flush();
}

Query query = pm.newQuery(Project.class);
try {
List<Project> projectList = (List<Project>)query.execute();
PrintWriter writer = resp.getWriter();
resp.setContentType("text/html");
if (projectList.iterator().hasNext()){
writer.println("<table>");
writer.println("<tr>");
writer.println("<td>id</td>");
writer.println("<td>name</td>");
writer.println("<td>detail</td>");
writer.println("</tr>");
for ( Project pro : projectList ) {
writer.println("<tr>");
writer.println("<td>" + pro.getId() + "</td>");
writer.println("<td>" + pro.getName() + "</td>");
writer.println("<td>" + pro.getDetail() + "</td>");
writer.println("</tr>");
}
writer.println("</table>");
} else {
}
} finally {
query.closeAll();
}
}
}


まずProjectのオブジェクトを準備しています。
でPersistenceManagerでmakeします。
これがinsert行為になります。

まだpmは使うのでflushだけして
Queryクラスで一覧を取得してきます。
このQueryクラスでwhere文やlimit文、Order文などを記述します。

でPrintWriterでHTMLに記述をしています。


さぁベタな文法ですがいかがだったでしょうか?
※PMFなどをSingletonパターンを利用しないと
 もう一回インスタンスを取得するとExceptionになります。

http://gae.ziro.bz/secure/test.jsp

でお試しできます。
※ごめんなさい。Creatorユーザでユーザを保存していますので
 試すとアカウントの情報が残ります。
 間違っても何かに使ったりしませんのでお試しください。

※ごめんなさい。現在はStrutsによる実装になってしまいました。

・・・さてトランザクションとかもやらんとな。。。

2009年4月20日月曜日

GAEで認証アプリケーション(続)

さて情報の取得方法は前項でやりました。

大事なのはアプリケーションを認証で守ることです。
※前項では認証を行っただけ

日頃からJ2EEの認証JAAS使っといてよかった。


<security-constraint>
<web-resource-collection>
<url-pattern>/secure/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
</security-constraint>


とweb.xmlに記述すると
/secure/~というURLは認証を通らないといけなくなります。

JSPなどにこのURLに関係するリンクを貼り、押下してみると
認証されないと行けないことがわかります。

なお、role-nameで「*」を指定していますが
アプリケーションの設定で管理者になっている「admin」というロールと
世間一般のGoogleアカウントがあるようです。(こっちは不明)

GAEでGroovy!

はてさて知人がGroovyを使いたいということだったので
何となくですがやってしまいました。

まずweb.xmlに

<servlet>
<servlet-name>GroovyServlet</servlet-name>
<servlet-class>groovy.servlet.GroovyServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>GroovyServlet</servlet-name>
<url-pattern>*.groovy</url-pattern>
</servlet-mapping>



を追加します。
って独自クラスを作ろうとしてたら
GroovyにServletてびっくりしました。

servletとservlet-mappingは
まとめて記述する必要があるので
独自のサーブレットがある場合は気をつけて編集しましょう。

でwar/WEB-INF/groovyというディレクトリを作成して
test.groovyファイルを作成します。


html.html {
head {
title "Hello"
}
body {
p "Hello Groovy World!"
}
}




これでデプロイして「ドメイン/test.groovy」でアクセスすれば
Groovy動作が完了しますね。簡単です。


このソースの前にScriptEngineManagerを使って
文字列実行できるかテストしたんですけど。。。。
ScriptEngineManagerってJava6標準なんですね。驚きました。




・・・あー忘れてました。Groovyのjarが必要ですね。
Groovyの本家からバイナリでもダウンロードして
jarをクラスライブラリに追加しないと動作しません。

2009年4月19日日曜日

GAEでJDO

すっごい!!!JDOまで行っちゃうなんて!!!

PersistenceManagerFactoryをSingletonで作成して


・・・ってこれをパッケージングして
自分のクラス構造してたらQueryがあった。。。スゴイ。



うん。すごい。
またJDOについては書きます!

GAEで認証アプリケーション

GoogleAppEngineで認証できるアプリケーションを作成してみましょう!

サンプルにもある通り、

UserService userService = UserServiceFactory.getUserService();
User user = userService.getCurrentUser();


と行うとユーザがログインしているかどうかがわかります。
userがnullの場合はログインしてない状況です。


userService.createLogoutURL(request.getRequestURI()
userService.createLoginURL(request.getRequestURI())


と行うとログイン、ログアウトのパスを取得できるので
それに対してAタグを書いてあげます。

引数の「request.getRequestURI()」は
ログイン後に戻るページですので変更すれば画面の遷移を変えることができます。


ここで彷徨ってしまったのは
独自ドメイン時のログイン画面です。
私の作ったアプリケーションの認証を作成しようとしたら
GoogleAppsの認証画面が出てきました。

「えー独自ドメインは自分のドメインの認証なのっ!」


・・・と勘違いしちゃいましたが違いました。
どうやらアプリケーションを作成した時に


でドメイン認証をチェックしたみたいです。
・・・1時間位さまよいました。。。
逆に考えれば独自認証のアプリも作れるってことですね!




はてさて情報の取得やログインへの遷移は可能になりましたが
大事なのはアプリケーションのセキュリティです。

・・・ってJAASを使用した方なら簡単ですね。
web.xmlを設定してあげればOKです。


ほぼ
ここと同じだと思いますが
server.xmlはいらないし「manage」っていうロールはないですね。
adminはアプリの管理者がそういう扱いになりそうです。

GoogleAppEngine for Java

先日一部公開されましたGoogleAppEngineのJava版を使ってみましょう。

まずEclipseにプラグインをインストールします。
3.4の場合は以下のサイトでOKです。
http://dl.google.com/eclipse/plugin/3.4

GAEのサイトに行って登録を行います。
※最新情報だとJavaは2万5千人先着?
 まぁどんどん増やしそうですけど。

GAEのサイトで「CreateProject」を行います。
ここで登録するIDはアプリケーションIDとして唯一ですが
後で独自ドメインでの運用も可能になりますので
唯一なら何でもよいでしょう。


その後eclipseでGoogleのアイコンの

「Web Application Project」を作成します。


その次の画面でプロジェクト名はeclipse上での名称、
Packageは作成を行うパッケージを指定します。

GWTを使う時と使わない時で下のチェックボックスをはずして
作成を行います。GWTかGAEのどちらかは必須のようです。
GAEを使わないと。。。多分ただのWebアプリケーションになります。


その後「Deploy App Engine Project」で
公開するわけですが、プロジェクトの設定で
アプリケーションIDを指定して開始する必要があります。
「アプリケーションID」はGAEのサイトで発行したIDです。
認証にはプロジェクトを作成した時のGoogleのIDを指定します。

これだけで完了です。

StrutsのBlankApplicationは動作しましたけど、
Tilesを組み入れたアプリはいまのところ動作を確認していません。
load-on-startupが効かないみたいだからその辺なのかな?

log4jなのも少しダサい気もしますけど。。。
JDOも触ってみましょう!



月間500万PVまで大丈夫。。。って
どれだけのものを作らせようとしているのか。。。
まぁ今のところ使い道はないですけど
何か作りたいですね。

2009年4月10日金曜日

忘れそうなコマンド

ファイルの中身を検索して探すコマンドです。


find ./ -type f | xargs grep localhost


ファイル内部にlocalhostの記述があるファイルを探しています。