2010年10月3日日曜日

mixi Graph APIの認証

http://developer.mixi.co.jp/connect/mixi_graph_api/api_auth

にある認可方法のJava実装の説明です。


まず、使用するには開発者登録を行い
https://sap.mixi.jp/
で新規サービス追加を行っておく必要があります。

まずURLを作って以下にリダイレクトします。

String endPoint = "https://mixi.jp/connect_authorize.pl";

String url = endPoint + "?response_type=code";

String key = "キー";
String scope = "r_profile";
String display = "pc";

url += "&client_id=" + key;
url += "&scope=" + scope;
url += "&display=" + display;

return redirect(url);

※redirect関数はslim3の関数です。
 このURLにリダイレクトすればOKです。

するとmixiの認証画面に飛びます。
許可がおりるとサービスを作成した際のURLに遷移します。
キーにはサービス作成後に生成される「Consumer Key」を指定しておきます。
r_profileの指定は使用するサービスによって変更します。

認可手順のところに記述してありますね。




//コードを取得
String token = requestScope("code");
String endPoint = "https://secure.mixi-platform.com/2/token";
String args = "";

String key = "サービスのKEYを指定";
String scret = "サービスのSECRETを指定";
String uri = "http://mixi.latest.secondarykey.appspot.com/callback";

args += "grant_type=authorization_code";
args += "&client_id=" + key;
args += "&client_secret=" + scret;
args += "&code=" + token;
args += "&redirect_uri=" + uri;

String json = post(endPoint,args);
Map jsonMap = createJsonMap(json);

String email = "メールアドレス";
String url = "http://api.mixi-platform.com/2/search/people?q=" + email;
//String url = "http://api.mixi-platform.com/2/people/@me/@self";

get(url,jsonMap.get("access_token"));

return forward("oauth.jsp");


飛んできたURLの引数で「code」がきますので
それを元にURLを作成します。
「redirect_uri」は、、、、何設定するのかな?多分合ってない気がする。。。
それをPOSTで投げます。
post()はオリジナルでHttpURLConnectionを利用して投げています。

その戻り値はjsonで戻ってきます。

{"refresh_token":"a4a634845360522a85cc030e043cb869d2536aa",
"expires_in":900,
"access_token":"8b17d199e43c879c1a015019cf55d9ecd0e2503",
"scope":"r_profile"}

こういう感じのjsonです。

この「access_token」を利用して、利用したいサービスにサクセスします。
ここではPeople lookup APIにアクセスして、
マイミクの情報を取得しています。

get()ではHttpURLConnectionに対して

connection.setRequestProperty("Authorization", "OAuth " + oauth);


と認証情報にアクセストークンを指定する必要があります。

このブログを記述している時点では
メールアドレスによる検索は認可されたユーザの
マイミクでないと検索できないようで、404を返してきていました。
テスト時に自分のメールアドレスを検索してて
「なんで404なんじゃー!」って思っていましたが、仕様のようです。

2010年4月24日土曜日

ExCellaで帳票

最近ExCellaを触ってます。

実際は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月26日金曜日

カーソルの変更

[Embed(source="Assets.swf", symbol="mx.skins.cursor.hogehoge")]

private var _hogehogeClass:Class;

こういう感じみたいですけど、
Assets.swfにあるイメージの一覧が欲しい。

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日本語サイト




第二弾をやってから第一弾をやらないと、
アノテーションのところがプロジェクト名に依存しているので
大変な事になりそう(>_<)すみません。


今回はGoogleAppEngineへのデプロイです。

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月22日月曜日

Slim3でクラウド開発を始める。その2


Slim3の開発はこんなブログより正式ドキュメントが有効です。
Slim3サイト
非公式と言われていますが充実した日本語サイトもあります。
Slim3日本語サイト





第一弾

さぁ第二弾です。
いよいよSlim3 RC1が近づいて参りました。
今回はsllim3-blankの名称を変更したい時の話をします。


まずプロジェクトで右クリックします。
その後リファクタを選んで、名前変更を選択します。
もしくはプロジェクトを選択して、F2でもOK。

出てきたボックスに対して、
自分の好きな名前(任意)を書きます。

するとプロジェクト名が変わります。
その後、build.xmlを選択して、
nameを変更します。

こんな感じですかね?

第三弾に続く。