appengineでmixiのsimplepostのリダイレクトを作成しようとすると、
「java.lang.ArrayIndexOutOfBoundsException: 4096」
が発生してしまいました。
リダイレクト時のヘッダーが長すぎるようです。
mixiのsimplepostの日記は本文が全角1万文字まで
リダイレクトが可能なのですが、
どうもjettyの制限(現在は設定可能)やバグの部分が関係しているようです。
http://jira.codehaus.org/browse/JETTY-1093
http://jira.codehaus.org/browse/JETTY-1112
http://groups.google.co.jp/group/google-appengine-java/browse_thread/thread/488d57c223b8173c?pli=1
http://code.google.com/p/googleappengine/issues/detail?id=2413
設定を追加する可能性は低いとみて、
直接リダイレクトせずにクライアントからJavaScriptでリダイレクトをかけようと思います。
2010年4月24日土曜日
ExCellaで帳票
最近ExCellaを触ってます。
実際はGoogleAppEngineでサービスを目標にしていて、
一部ExCella自体をいじりながらの開発です。
一応Excelが出力できたのでブログを起こしておきましょう。
やっぱりGAEでネックになるのは
ファイルシステムとの問題です。
まず、core側にあるExporterのTextExporterとWorkbookExporterを
削除しました。
単純にいうとまずはスモールスタートの為に
ExcelOutputStreamExporterのみをサポートする事にしました。
本来はXLS周りを使用してみたかったのですが
POI自体(ExCella内部で使っている)が、テンプレートの読み込み途中で
xmlファイルを保存している感じがあって、落ちてしまいます。
なので一旦はExcelに集中。
ExCella自体がファイルへの出力重視的な実装になっていて
親クラスで「出力ファイル名」等を持つことが多いみたいなので
ひとまずそのあたりを削除。
エラーになるところを潰していくような作業になりました。
・・・実際は根本の部分でOutputStreamの設定する仕組みに変えるべきだと
。。。ブログ書いてる途中に思いまして、ちょっと実装しなおそうかな?
って思っています。
現状だと
こんな感じで動作します。
GAEでの動作なのでinputStreamは
テンプレート用のファイルをDataStore上に溜め込んでいます。
コードネームはdon-gabachoです。
あー。。。もういっかい書き直して
全部書き留めないとなぁ。。。
※2010/4/25 でリファクタリング完成
http://code.google.com/p/don-gabacho/wiki/ExCella?ts=1272171109&updated=ExCella
streamをBookから引き渡すように変更しています。
実際はGoogleAppEngineでサービスを目標にしていて、
一部ExCella自体をいじりながらの開発です。
一応Excelが出力できたのでブログを起こしておきましょう。
やっぱりGAEでネックになるのは
ファイルシステムとの問題です。
まず、core側にあるExporterのTextExporterとWorkbookExporterを
削除しました。
単純にいうとまずはスモールスタートの為に
ExcelOutputStreamExporterのみをサポートする事にしました。
本来はXLS周りを使用してみたかったのですが
POI自体(ExCella内部で使っている)が、テンプレートの読み込み途中で
xmlファイルを保存している感じがあって、落ちてしまいます。
なので一旦はExcelに集中。
ExCella自体がファイルへの出力重視的な実装になっていて
親クラスで「出力ファイル名」等を持つことが多いみたいなので
ひとまずそのあたりを削除。
エラーになるところを潰していくような作業になりました。
・・・実際は根本の部分でOutputStreamの設定する仕組みに変えるべきだと
。。。ブログ書いてる途中に思いまして、ちょっと実装しなおそうかな?
って思っています。
現状だと
ByteArrayInputStream inStream = new ByteArrayInputStream(template.getBytes());
ReportBook outputBook = new ReportBook(inStream,ExcelOutputStreamExporter.FORMAT_TYPE);
ReportSheet outputSheet = new ReportSheet("テンプレート","請求書");
outputBook.addReportSheet(outputSheet);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ReportProcessor reportProcessor = new ReportProcessor();
stream = reportProcessor.process(outputBook);
response.setHeader("Content-Disposition","attachment; filename=excel.xls");
response.setContentType("application/msexcel");
response.setContentLength(stream.toByteArray().length);
OutputStream out = new BufferedOutputStream(response.getOutputStream());
out.write(stream.toByteArray());
out.close();
こんな感じで動作します。
GAEでの動作なのでinputStreamは
テンプレート用のファイルをDataStore上に溜め込んでいます。
コードネームはdon-gabachoです。
あー。。。もういっかい書き直して
全部書き留めないとなぁ。。。
※2010/4/25 でリファクタリング完成
http://code.google.com/p/don-gabacho/wiki/ExCella?ts=1272171109&updated=ExCella
streamをBookから引き渡すように変更しています。
2010年3月14日日曜日
FlashLiteで引数を埋め込む(Java)
先日友人がFlashLiteでの引数埋込に苦戦してた。
携帯で個体識別番号などの引数を元にFlashを書き換えたいらしい。
ここに方式はあるんだけど、、、って事でJava化してみた。
public static String inCode = "utf-8";
public static String outCode = "shift-jis";
/**
* FlashLite への引数の埋込
* @param oldFile
* @param argMap
* @return
*/
public static byte[] createArgEmbedSwf(byte[] oldFile, Map<String, String> argMap) {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
//rectbitの3ビット右シフト
byte rectbit = (byte)((int)oldFile[8] / 8);
//$headlen = ceil(((( 8 - (($rb*4+5)&7) )&7)+ $rb*4 + 5 )/8) + 12 + 5;
double dblHead = ((( 8 - ((rectbit*4+5)&7) )&7)+ rectbit*4 + 5 ) / 8;
//ヘッダ長を取得
int headLen = (int)Math.ceil(dblHead) + 12 + 5;
//引数部分の作成
byte[] doActionTag = createArg(argMap);
//$newsize = $oldsize+strlen($doactiontag);
int oldSize = oldFile.length;
//新しいサイズを取得
int newSize = oldSize + doActionTag.length;
//新しいヘッダを作成
byte[] newHeader = createHeader(oldFile,headLen,newSize);
//ヘッダ後のデータを取得
byte[] tail = createTail(oldFile,headLen);
try {
//タグ部分の書き込み
byteStream.write(newHeader);
byteStream.write(doActionTag);
byteStream.write(tail);
} catch (IOException e) {
e.printStackTrace();
}
return byteStream.toByteArray();
}
/**
* 新しいヘッダーの作成
* @param buf
* @param headLen
* @param newSize
* @return
*/
private static byte[] createHeader(byte[] buf, int headLen, int newSize) {
//ヘッダの位置を取得
//$head = $headtmp.fread($fr,$headlen-9);
//$newhead = substr($head,0,4).h32($newsize).substr($head,8);
byte[] newHeader = new byte[headLen];
//4バイトでint値をバイトに変換
byte[] newHeadSize = changeBytes(newSize,4);
//長さを変更
for ( int cnt = 0; cnt < headLen; ++cnt ) {
if ( cnt >= 4 && cnt < 8 ) {
newHeader[cnt] = newHeadSize[cnt-4];
} else {
newHeader[cnt] = buf[cnt];
}
}
return newHeader;
}
//3f 03 18 00 00 00 96 08
//00 00 6d 79 6e 61 6d 65
//00 96 08 00 00 74 61 77
//74 61 77 00 1d 00
//3f 03 -- タグはじまり, Type=12(DoActionタグ)
//18 00 00 00 -- タグ長さ 0x18 = 24 byte(ここからタグ終わりまでの長さ)
//96 -- ActionPush
//08 00 -- Pushするものの長さ, 8 byte
//00 -- Pushするものは文字列
//6d 79 6e 61 6d 65 00 -- "myname" (ord("m")=109=0x6d など。00は文字列終端を意味)
//96 -- ActionPush
//08 00 -- Pushするものの長さ
//00 -- Pushするものは文字列
//74 61 77 74 61 77 00 -- "tawtaw"
//1d -- ActionSetVariable
//00 -- タグ終わり
/**
* タグ用のバイトを作成
* @param argMap
* @return
*/
private static byte[] createArg(Map<String, String> argMap) {
List<Byte> byteList = new ArrayList<Byte>();
//タグの始まりを設定
//$tag = "\x3f\x03";
byteList.add((byte)0x3f);
byteList.add((byte)0x03);
//タグの長さを取得
//$taglen = calctaglen($dataarray);
int tagLen = calcArgLen(argMap);
//$tag .= h32($taglen);
add(changeBytes(tagLen,4),byteList);
//foreach($dataarray as $key => $value)
Iterator<Entry<String,String>> itr = argMap.entrySet().iterator();
//キー数回繰り返す
while ( itr.hasNext() ) {
Entry<String,String> entry = itr.next();
//$tag .= "\x96".h16(strlen($key)+2)."\x00".$key."\x00";
addString(entry.getKey(),byteList);
//$tag .= "\x96".h16(strlen($value)+2)."\x00".$value."\x00";
addString(entry.getValue(),byteList);
//$tag .= "\x1d";
byteList.add((byte)0x1d);
}
//終端のバイトを設定
//$tag .= "\x00";
byteList.add((byte)0x00);
return change(byteList);
}
/**
* 引数の長さを取得
* @param argMap 引数用のマップ
* @return
*/
private static int calcArgLen(Map<String, String> argMap) {
int rtn = 0;
Iterator<Entry<String,String>> itr = argMap.entrySet().iterator();
//キー数回繰り返す
while ( itr.hasNext() ) {
Entry<String,String> entry = itr.next();
String key;
String value;
try {
key = new String(entry.getKey().getBytes(inCode),outCode);
value = new String(entry.getValue().getBytes(inCode),outCode);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("文字列コード変換失敗",e);
}
//それぞれのバイト数とタグ用の11バイトを追加
try {
rtn += (key.getBytes(outCode).length + value.getBytes(outCode).length + 11);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("文字列コード変換失敗",e);
}
}
//終端分を追加
return rtn + 1;
}
/**
* ヘッダの後ろを取得
* @param buf
* @param oldSize
* @param headLen
* @return
*/
private static byte[] createTail(byte[] buf,int headLen) {
int oldSize = buf.length - headLen;
byte[] tail = new byte[oldSize];
int idx = 0;
//$tail = fread($fr, $oldsize-$headlen);
for ( int cnt = headLen; cnt < buf.length; ++cnt ) {
tail[idx] = buf[cnt];
++idx;
}
return tail;
}
/**
* リストから配列に変更
* @param byteList
* @return
*/
private static byte[] change(List<Byte> byteList) {
byte[] rtnByte = new byte[byteList.size()];
int idx = 0;
for ( Byte b : byteList ) {
rtnByte[idx] = b;
++idx;
}
return rtnByte;
}
/**
* バイト配列への変更
* @param i
* @param leng
* @return
*/
private static byte[] changeBytes( int i ,int leng){
byte[] b = new byte[leng] ;
if ( leng > 4) {
b[3] = (byte)((i >>> 24 ) & 0xFF);
}
if ( leng > 3) {
b[2] = (byte)((i >>> 16 ) & 0xFF);
}
if ( leng > 2) {
b[1] = (byte)((i >>> 8 ) & 0xFF);
}
if ( leng > 1) {
b[0] = (byte)((i >>> 0 ) & 0xFF);
}
return b;
}
/**
* リストへの追加
*/
private static void add(byte[] byteArray,List<Byte> byteList) {
for ( byte b : byteArray ) {
byteList.add(b);
}
}
/**
* 文字列の追加
* @param key
* @param byteList
*/
private static void addString(String key, List<Byte> byteList) {
String tmp = null;
try {
tmp = new String(key.getBytes(inCode),outCode);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("文字列変換失敗",e);
}
//$tag .= "\x96".h16(strlen($key)+2)."\x00".$key."\x00";
byteList.add((byte)0x96);
//文字列数を設定
byte[] lengByte;
try {
lengByte = changeBytes(tmp.getBytes(outCode).length + 2,2);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("文字列変換失敗",e);
}
add(lengByte,byteList);
byteList.add((byte)0x00);
//文字列を設定
try {
add(tmp.getBytes(outCode),byteList);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("文字列変換失敗",e);
}
byteList.add((byte)0x00);
}
byteのつなげ方とかが独自で、少ない時間での実装なので何か良い方法があるかも。
byte[]での変換に終わってるのは、GAE上での動作確認をしたかったからです。
blob型で保存して動的に変更させる事に成功しました。
byte[] newFile = FlashUtil.createArgEmbedSwf(targetFile.getBytes() , argMap);
try {
response.setContentType("application/x-shockwave-flash");
OutputStream out =
new BufferedOutputStream(response.getOutputStream());
try {
out.write(newFile);
} finally {
out.flush();
out.close();
}
} catch (IOException e) {
ThrowableUtil.wrapAndThrow(e);
}
こんな感じかな?
日本語文字化けしちゃうけど、、、、まぁ方式がわかれば解決するでしょう。
2010年3月2日火曜日
Slim3でクラウド開発を始める。その4
Slim3の開発はこんなブログより正式ドキュメントが有効です。
Slim3サイト
非公式と言われていますが充実した日本語サイトもあります。
Slim3日本語サイト
第二弾をやってから第一弾をやらないと、
アノテーションのところがプロジェクト名に依存しているので
大変な事になりそう(>_<)すみません。
GooglePluginを入れると左上に以下のアイコンが出ます。
それをポチッとな。
するとデプロイの画面がでますので、
プロジェクトを選択して、管理者のメールアドレスとパスワードを入力します。
画像では切れていますが、SDKの設定があるのでそこをクリック!
出てきた画面にGoogleAppEngineにアプリケーションを登録した際の
IDを入力します。
これで設定は完了です。
デプロイしたらURLにアクセスしてください。
おそらくアプリケーションが表示されている。。。。はずです。
2010年2月27日土曜日
Slim3でクラウド開発を始める。その3
Slim3の開発はこんなブログより正式ドキュメントが有効です。
Slim3サイト
非公式と言われていますが充実した日本語サイトもあります。
Slim3日本語サイト
第一弾、第二弾
そういえば少し前にGAEがDataNucleusを使ってるので
それを使って、HSQLDBにつなげた事もありました(>_<)
で本題です。
Slim3は同時にテストも出力してくれます。
なのでその設定をします。
まずプロジェクトを選択して、設定を行います。
その後、Javaのコードスタイル、インポートの編成を選択して
プロジェクト特有の設定を行います。
(java.lang.Math.*)に必要なインポート数を「1」に設定します。
Javaのエディタにコンテンツ・アシストがあります。
そこにお気に入りがあるので選択して
以下を追加します。
org.hamcrest.CoreMatchers
org.junit.Assert
org.junit.matchers.JUnitMatchers
で「一般」を選択すると「ワークスペース」があります。
そこに「自動的にリフレッシュ」がありますんで
選択してください。
2010年2月21日日曜日
Slim3でクラウド開発を始める
Slim3の開発はこんなブログより正式ドキュメントが有効です。
Slim3サイト
非公式と言われていますが充実した日本語サイトもあります。
Slim3日本語サイト
Slim3に飛びついたのは、GoogleAppEngineで
ファイルのアップロードができるし、Strutsだったから
飛びついたのを覚えています。
現在はStrutsには対応していませんが
MVCが整った軽量なフレームワークです。
開発は開始できます。
現在SDKは1.3.1のようですね。
作成されたプロジェクトで右クリックを押して、
コンパイラのアノテーションのファクトリーパスを選択します。
右の画像の画面を出してjarを選択します。
まだアーリーアクセス(EA)なんですね(>_<)
完成度結構高いんですけどね。
んでプロジェクトで「war/WEB-INF/web.xml」を
開きます。
右の部分が自分の名前空間になりますから
何か適当な名前をつけましょう!
その後はプロジェクト直下にある
build.xmlを選択して右クリックで
実行を押して、下のantを選択します。
すると右の画面が出ます。
gen-controllerはコントロールを自動作成
gen-modelはモデルを自動作成してくれます。
gen-controllerで出てくるボックスに
URLを指定してあげるとその指定したURLを
操作するControllerとJSP、プラステストコードを出力してくれます。
modelはそのモデルクラスとテストコードを出力してくれます。
2009年11月21日土曜日
GAEとDoCoMoのアドレス
GAEでメール受信を作りましたが、
初めに試してもらった人が「xxxxx.@docomo.ne.jp」てアドレスでした。
どうも@の前に「.」があるとダメみたいで
getFrom()をやると「local part invalid」ってエラーが出てました。
なのでヘッダーの情報を見て、
Enumeration<?> enu = aMessage.getAllHeaderLines(); //データ数回繰り返す while ( enu.hasMoreElements() ) { String line = (String)enu.nextElement(); if ( line.toLowerCase().indexOf("from") >= 0 ) { //fromを検索して追加 fromAddress.add(line); } else if ( line.toLowerCase().indexOf("to") >= 0 ) { //toを検索して追加 toAddress.add(line); } }
って感じでFromやToを抜き出して、
そこからアドレスを抽出して処理してみました。
まぁシビアにはまずいかもしれませんが
一応これでメールアドレスを判定する事ができました。
2009年11月8日日曜日
GAEでメール受信
GAEにメール受信する機能が増えたんですね。
またまたやってくれます。。。。って思ってたら
やっぱり限定の機能なんですね。
・・・まぁそれだけでもすごい事です。
えーと、、、仕組みはここにあります。
string@appid.appspotmail.com
に送ると、
/_ah/mail/
に届くみたいですね。。。。
Cronとか駆使して、、、って考えてたんですけど、
開発環境でみる限り、メールを送信したら、サーブレットをPostしてくれるみたいです!
なんだ、、、色々楽ですね。。。
まず、appengine-web.xmlに
を追加します。
その後web.xmlに
を追加します。
servletの上はクラス名、下はURLです。
URLは固定ですね。
下のはセキュリティ設定です。
第三者が触れないようにadmin(アプリ管理者)のみ触れるようにしておきます。
その後HttpServletを派生したクラスを作成して
doPost()を実装します。
これで、メールを受け取ったら動作します。
http://localhost:8080/_ah/admin/
にアクセスすると、inboundMailっていうのがあるので
開発環境ではここから送信できます。
先だって話したように、URLとメールアドレスが連動しているので
何のメールかとかをToで判断したりすればOKだと思います。
またまたやってくれます。。。。って思ってたら
やっぱり限定の機能なんですね。
・・・まぁそれだけでもすごい事です。
えーと、、、仕組みはここにあります。
string@appid.appspotmail.com
に送ると、
/_ah/mail/
に届くみたいですね。。。。
Cronとか駆使して、、、って考えてたんですけど、
開発環境でみる限り、メールを送信したら、サーブレットをPostしてくれるみたいです!
なんだ、、、色々楽ですね。。。
まず、appengine-web.xmlに
<inbound-services>
<service>mail</service>
</inbound-services>
を追加します。
その後web.xmlに
<servlet>
<servlet-name>mailHandle</servlet-name>
<servlet-class>bz.ziro.kanbe.servlet.MailHandlerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>mailHandle</servlet-name>
<url-pattern>/_ah/mail/*</url-pattern>
</servlet-mapping>
~
<security-constraint>
<web-resource-collection>
<url-pattern>/_ah/mail/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
を追加します。
servletの上はクラス名、下はURLです。
URLは固定ですね。
下のはセキュリティ設定です。
第三者が触れないようにadmin(アプリ管理者)のみ触れるようにしておきます。
その後HttpServletを派生したクラスを作成して
doPost()を実装します。
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
try {
Properties props = new Properties();
Session session = Session.getDefaultInstance(props, null);
MimeMessage message = new MimeMessage(session, req.getInputStream());
} catch (MessagingException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
これで、メールを受け取ったら動作します。
http://localhost:8080/_ah/admin/
にアクセスすると、inboundMailっていうのがあるので
開発環境ではここから送信できます。
先だって話したように、URLとメールアドレスが連動しているので
何のメールかとかをToで判断したりすればOKだと思います。
2009年10月8日木曜日
GAE+Flex+ファイルアップロード+認証
GAE上でFlexを使用してファイルアップロードを行っているのですが
どうもうまくいかない状態でした。
まぁオススメはできませんが回避策を一応書いておきます。
FireReference.uploadはセキュリティ系の問題なのか
クッキーを送信できません。
ここのにupload()のURLRequestHeaderのところに記述があります。
GAEでは認証アプリ内ではクッキーを使用して認証を確認しているようで
JSESSIONIDとACSIDを送信することが不可能なようです。
なのでGAE上でFileReferenceを利用してアップロードをすると
アップロード時に認証エラーでアップロードができないようです。
そこで、私の場合はアップロードのURLを認証から外して独自認証することで切り抜けました。
Flexが送信するRequestに引数をjsessionidを指定しておいて
認証のかかってないURLの処理上でSessionからIDを取得して同一かを判定して切り抜けました。
まぁセキュリティ上は微妙でしょうけど、
一応認証を受けてないと使えないようにはしました。
で、、、ここで気をつけなければいけないのは
FileReferenceで渡すURLRequest.dataをObjectではなく、
URLVariablesにすることです。
そうしないとRequestでmultipartにならず、
multipartで値を受け取れないからです。(slim3)
どうもうまくいかない状態でした。
まぁオススメはできませんが回避策を一応書いておきます。
FireReference.uploadはセキュリティ系の問題なのか
クッキーを送信できません。
ここのにupload()のURLRequestHeaderのところに記述があります。
GAEでは認証アプリ内ではクッキーを使用して認証を確認しているようで
JSESSIONIDとACSIDを送信することが不可能なようです。
なのでGAE上でFileReferenceを利用してアップロードをすると
アップロード時に認証エラーでアップロードができないようです。
そこで、私の場合はアップロードのURLを認証から外して独自認証することで切り抜けました。
Flexが送信するRequestに引数をjsessionidを指定しておいて
認証のかかってないURLの処理上でSessionからIDを取得して同一かを判定して切り抜けました。
まぁセキュリティ上は微妙でしょうけど、
一応認証を受けてないと使えないようにはしました。
で、、、ここで気をつけなければいけないのは
FileReferenceで渡すURLRequest.dataをObjectではなく、
URLVariablesにすることです。
そうしないとRequestでmultipartにならず、
multipartで値を受け取れないからです。(slim3)
2009年7月25日土曜日
2009年7月18日土曜日
2009年6月28日日曜日
GAEでVelocity
少しテンプレートエンジンを使いたいなぁ。。。って思ったので
Velocityを使うことにしたんだけど、
GAEはファイルシステムはないので、、、って思ってたら
メモリ上で展開できるみたい。
これでwriterに設定できます。
テンプレートを「com.google.appengine.api.datastore.Text」で
保存すれば使えそうですね。
実行にはVelocity、commons-lang、commons-collectionが必要です。
Velocityを使うことにしたんだけど、
GAEはファイルシステムはないので、、、って思ってたら
メモリ上で展開できるみたい。
VelocityContext context = new VelocityContext();
StringWriter writer = new StringWriter();
String template =
"テンプレートのテストです[${name}]" +
"";
context.put("name", "なまえー");
Velocity.evaluate(context, writer, "vecocityTest", template);
これでwriterに設定できます。
テンプレートを「com.google.appengine.api.datastore.Text」で
保存すれば使えそうですね。
実行にはVelocity、commons-lang、commons-collectionが必要です。
2009年6月27日土曜日
GAEで派生データを登録
すでにこの設定などは古くなっている可能性があります。
Slim3の開発はこんなブログより正式ドキュメントが有効です。
Slim3サイト
非公式と言われていますが充実した日本語サイトもあります。
Slim3日本語サイト
少し前に試した時にダメだったので諦めていたのですが、
再度チャレンジしたらOKだったので投稿しておきます。
親クラスを準備します。
package bz.ziro.slim3.test.model;
import java.io.Serializable;
import java.util.Date;
import java.util.logging.Logger;
import javax.jdo.JDOHelper;
import javax.jdo.annotations.Extension;
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 javax.jdo.annotations.Version;
import javax.jdo.annotations.VersionStrategy;
import com.google.appengine.api.users.User;
@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
@Version(strategy = VersionStrategy.VERSION_NUMBER)
public class Parent implements Serializable {
private static final long serialVersionUID = 1L;
@SuppressWarnings("unused")
private static final Logger logger = Logger.getLogger(Parent.class.getName());
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
@Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
private String key;
@Persistent
private User creator;
@Persistent
private Date createDate;
@Persistent
private User editor;
@Persistent
private Date editDate;
/**
* @return the key
*/
public String getKey() {
return key;
}
/**
* @param key
* the key to set
*/
public void setKey(String key) {
this.key = key;
}
public User getCreator() {
return creator;
}
public void setCreator(User creator) {
this.creator = creator;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public User getEditor() {
return editor;
}
public void setEditor(User editor) {
this.editor = editor;
}
public Date getEditDate() {
return editDate;
}
public void setEditDate(Date editDate) {
this.editDate = editDate;
}
/**
* @return the version
*/
public long getVersion() {
return (Long) JDOHelper.getVersion(this);
}
}
でこのクラスを派生した子クラスを準備します。
package bz.ziro.slim3.test.model;
import java.io.Serializable;
import java.util.logging.Logger;
import javax.jdo.JDOHelper;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.Version;
import javax.jdo.annotations.VersionStrategy;
@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
@Version(strategy = VersionStrategy.VERSION_NUMBER)
public class Child extends Parent implements Serializable {
private static final long serialVersionUID = 1L;
@SuppressWarnings("unused")
private static final Logger logger = Logger.getLogger(Child.class.getName());
@Persistent
private String name;
@Persistent
private String detail;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
/**
* @param detail セットする detail
*/
public void setDetail(String detail) {
this.detail = detail;
}
/**
* @return detail
*/
public String getDetail() {
return detail;
}
/**
* @return the version
*/
public long getVersion() {
return (Long) JDOHelper.getVersion(this);
}
}
これで準備完了です。
ChildオブジェクトをGAE(JDO,BigTable)で登録すれば
Parentの属性も登録できます。
実装の通り、登録ユーザや登録日付などテーブル内にある同一の属性を
親クラスを利用することにより、はしょることができます。
まぁ私の場合は更新時などにオブジェクトに都度同一の実装が必要になり
困っていたので助かります。
JDOを知った時に論理モデル(つうかUML記述?)をそのまま使えるなぁ。。。
って思ってやったらダメだったんですけど、やっぱできるんですね。
ちなみにDataNucleusでJDBC(HSQLDB)で同じようにやると
Parentテーブルがにもデータがあって。。。って感じになった記憶があります。
※同一のエンティティではないです。
ちなみにSlim3のDaoでMetaクラスを利用して検索式を作成しても
ChildMetaに存在しないのでエラーになります。
問い合わせるかはもう少し試してからにしようと思ってます。
2009/07/08 ※再度試したらSlim3でも動作しました。
2009/07/26 ※再度試したらSlim3で動作。。。っていうかGAEで動作しませんでした。
ので、Embeddedの話で記述します。
Slim3で開発を始める。
すでにこの設定などは古くなっている可能性があります。
Slim3の開発はこんなブログより正式ドキュメントが有効です。
Slim3サイト
非公式と言われていますが充実した日本語サイトもあります。
Slim3日本語サイト
Slim3はGAE用に最適化され開発されているフレームワークで
すごくシンプルに開発できるように工夫されています。
今回は現在のSlim3での開発をおさらいしてみましょう。
まだ開発中ですので違う手法になっている可能性もあるので
十分に気をつけていただければ幸いです。
※eclipseでの開発です。
まず、「http://slim3.googlecode.com/svn」にSVNでアクセスして
「trunk/slim3-blank」からチェックアウトを行います。
ライブラリを落とし込んで来て作成するのもよいのですが
genで使用しているaptの設定などがあるのでblankで作成しておくのが良いでしょう。
で現在、aptの設定がプロジェクト名に依存しているので
プロジェクト名を変更した場合それを変更する必要があります。
プロジェクトで右クリック「プロパティ」をクリック
「Javaコンパイラー」→「注釈処理」→「ファクトリーパス」において
編集で「lib/slim3-genEA1-SNAPSHOT.jar」を再度指定します。
war/WEB-INF/web.xmlの中にある
<context-param>
<param-name>slim3.rootPackage</param-name>
<param-value>slim3</param-value>
</context-param>
<context-param>
<param-name>slim3.staticPackages</param-name>
<param-value>model</param-value>
</context-param>
の部分をオリジナルの名前空間に変更します。
「slim3.rootPackage」が自分の作成するアプリケーションのルートパッケージで
指定した位置がHotReloadingの対象になります。
「slim3.staticPackages」がHOTの対象外のパッケージを設定します。
カンマ区切りで指定できます。(モデルのパッケージは必ず指定してください)
これで開発の準備は完了です。
プロジェクトのルートにあるbuild.xmlで必要なクラスは
自動生成をして開発を進めていきます。
「gen-model」:エンティティ,Daoとそれぞれのテストクラスを出力してくれます。
「gen-controller」:controller,jspとそれぞれのテストクラスを出力してくれます。
※それぞれを指定する場合、パッケージ名は必要ありません。
build.xmlのprojectタグにある「name="xxxx"」を
自分のプロジェクト名に変更してあげる必要があります。
「gen-controller」で「index」と指定すると
ひとまずアプリケーションとして動作できるようになります。
※既存のjspやcontrollerは消しておいた方が良いかな。。。
2009年5月7日木曜日
JDO関連のデプロイ後の遅延
GAEにデータを保存
すでにこの設定などは古くなっている可能性があります。
Slim3の開発はこんなブログより正式ドキュメントが有効です。
Slim3サイト
非公式と言われていますが充実した日本語サイトもあります。
Slim3日本語サイト
さて、Slim3を手に入れ、Strutsでアップロードを可能にした私は
ついにデータを保存するということに挑戦。
っていうか簡単なんですけどね。
単純にBlobデータを使いました。
ファイルをアップロードして
FormFileからInputStreamを取り出して
ByteArrayOutputStreamを利用して
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream stream = aFile.getInputStream();
byte[] buffer = new byte[8192];
//終了まで読み込み続ける
while (stream.read(buffer) != -1) {
baos.write(buffer);
}
Blob imageBlob = new Blob(baos.toByteArray());
[com.google.appengine.api.datastore.Blob]ですね。
を生成します。
これをJDOで永続化してBigTableに保存します。
でそれを検索して
Image image = getPersistentImage(Long.valueOf(shopId),Long.valueOf(imageId));
Blob blob = image.getImage();
rtnByte = blob.getBytes();
※ImageってのはBlobを保存したパーシスタントクラスです。
取得します。
これをストリームに描きだしたりすると画像が出ます!
Blobを見た時からこれをやりたかったんですけどね。
Slim3の力を借りてここまでたどり着きました!
2009年5月5日火曜日
ひとまずGAEでSlim3を動かしてみよう!
すでにこの設定などは古くなっている可能性があります。
Slim3の開発はこんなブログより正式ドキュメントが有効です。
Slim3サイト
非公式と言われていますが充実した日本語サイトもあります。
Slim3日本語サイト
GAEでSlim3の使い方をやってみます!
みなさん。SVNでアクセスは可能ですか?
http://slim3.googlecode.com/svn
だったら上記にリポジトリパスを設定しましょう。
※eclipseっす。

ここから「slim3-demo」チェックアウトして、
後はGAEでデプロイするだけです。
※アプリケーションIDは自分のアプリケーションに設定して、
バージョンも1つにしましょう。
思いのほかデプロイが簡単でびっくりしています!
2009年5月4日月曜日
押してもだめならSlim3化
GAEにてファイルアップロードを行っていました。
ひがやすを氏がStrutsでのファイルアップロードを成功したようで
オープンソースらしいので少しお知恵を拝借しようと。。。
http://d.hatena.ne.jp/higayasuo/20090411/1239460908
と思い少しソース(+ブログ)を眺めていたら。。。
「俺は一体何をしているのだろうと。。。」
と思ってしまいました。
※ちなみに「S3MultipartRequestHandler」を利用して
やっているみたいですね。
久々にソース(+ブログ)に感動しました。
Seasarには結構前にトライしましたが
客先に乗せるのは困難だと判断して少し離れていましたが
Slim3の考えには賛同できる気がします。
ってことでソースを借りるのではなく「On」させてもらおうと。。。
まぁ挫折の可能性は高いですが。。。
ひがやすを氏がStrutsでのファイルアップロードを成功したようで
オープンソースらしいので少しお知恵を拝借しようと。。。
http://d.hatena.ne.jp/higayasuo/20090411/1239460908
と思い少しソース(+ブログ)を眺めていたら。。。
「俺は一体何をしているのだろうと。。。」
と思ってしまいました。
※ちなみに「S3MultipartRequestHandler」を利用して
やっているみたいですね。
久々にソース(+ブログ)に感動しました。
Seasarには結構前にトライしましたが
客先に乗せるのは困難だと判断して少し離れていましたが
Slim3の考えには賛同できる気がします。
ってことでソースを借りるのではなく「On」させてもらおうと。。。
まぁ挫折の可能性は高いですが。。。
2009年4月30日木曜日
2009年4月29日水曜日
GAEのバージョン管理
ローカルでの動作確認もありますが
デプロイした状況での確認も必要になります。
そこでデプロイのバージョニングを行います。
http://appengine.google.com/にアクセスすると
自分のアプリケーションの管理画面になると思います。
対象のアプリケーションを選択して
右にあるVersionsをクリックすると

という画面が出力されます。
ここのバージョンはeclipseのプロジェクトの設定で
[Google]→[App Engine]でデプロイメントのバージョンになります。
※1~の整数を指定します。
指定したバージョンでデプロイして
上記画面で右にあるURLでテストが行えます。
ちなみに初めて試した時に例外を見事発生させました。
デプロイした状況での確認も必要になります。
そこでデプロイのバージョニングを行います。
http://appengine.google.com/にアクセスすると
自分のアプリケーションの管理画面になると思います。
対象のアプリケーションを選択して
右にあるVersionsをクリックすると
という画面が出力されます。
ここのバージョンはeclipseのプロジェクトの設定で
[Google]→[App Engine]でデプロイメントのバージョンになります。
※1~の整数を指定します。
指定したバージョンでデプロイして
上記画面で右にあるURLでテストが行えます。
ちなみに初めて試した時に例外を見事発生させました。
登録:
投稿 (Atom)