Tumblr

pixiv RSS

FlashDevelopによるAIRアプリ開発 導入メモ

AIRはFlexと併せて覚えて行きたいなぁということで、唐突にやってみた。

IDEはフリーでEclipse以外という制限の元、FlashDevelopに決定。
minibuilderも良いなあと思ったものの、mxmlの編集には対応していないので除外。
(今後、バージョンアップして対応した場合は乗り換えるかも)

FlashDevelopで開発するには以下の環境が必要。
java runtime: 1.6以上
.NET Framework runtime: .NET Framework 2.0以上
コンパイラ: Flex3 SDK(3.4推奨?)
AIRランタイム: Adobe AIR 1.5以上

環境が用意出来たら以下からFlashDevelopをゲット。お好みで日本語化ファイルも。
以下の
FlashDevelop.org - View forum - Releases
logicalyze::blog

FlashDevelopの初期設定は以下のような感じで。
1.FlashDevelopを起動する。
2.メニューから「Tools」→「Program Settings」を選ぶ。
3.左のリストの中から「FlashDevelop」を選び、右のリストの「Fallback CodePage」を「UTF8」にする。
4.左のリストの中から「AS3Context」を選び、右のリストの「Flex SDK Location」を「C:\flex」にする。
5.「Close」ボタンを押して閉じる。

(参考:FlashDevelopとFlex 3 SDKでAdobe AIR - 今日覚えたこと

とりあえず動かす


[Project]→[New Project...]から新規プロジェクトを作成する。設定は適当に。NewProject


この状態で、[F5]もしくは右上青色の三角ボタンを押すと、コンパイルされ、実行結果が表示される。
(何も弄っていないので、何にも無いただの窓が表示される)


ビューとロジックを分けて書く


mxmlファイルがビューとなり、この中にActionScriptでロジックを書くことも出来る。
が、多分そんなことをしたら後で後悔することになるので、
「mxml=ビュー」「as=ロジック」と分離して書く方法を先に学ぶ。

AIR開発用のフレームワークを使わないでそのように書くとなると、
・IMXMLObjectを使う
・ビューを継承する

の2つがあるらしい。「mxml as 分離」でググると沢山出てくる。
実際にそのようにして書いた例が以下。

[src/Sample.mxml]






[src/views/MyView.mxml]






[src/logics/MyLogic.as]

package logics {
import flash.events.MouseEvent;

import mx.controls.Alert;
import mx.events.FlexEvent

import views.MyView;

public class MyLogic extends MyView {
public function MyLogic() {
super();
addEventListener(FlexEvent.CREATION_COMPLETE, createCompleteHandler);
}
private function createCompleteHandler(event:FlexEvent):void {
btn.addEventListener(MouseEvent.CLICK, button_clickHandler);
}

private function button_clickHandler(event:MouseEvent):void {
Alert.show("ボタンがクリックされたよ");
}
}
}


MyViewにはビューだけ書き、MyLogicにてビューの対するロジックを書いている。
そしてそれをSample.mxmlから呼び出している感じ。

左から右へ受け流す


AIRLife.net: view(MXML)とlogic(ActionScript)の分離

[src/views/MyView.mxml]













[src/logics/MyLogic.as]

package logics {
import flash.events.MouseEvent;

import mx.controls.Alert;
import mx.events.FlexEvent

import views.MyView;

public class MyLogic extends MyView {
public function MyLogic() {
super();
addEventListener(FlexEvent.CREATION_COMPLETE, createCompleteHandler);
}

private function createCompleteHandler(event:FlexEvent):void {
copyButton.addEventListener(MouseEvent.CLICK, copy);
clearButton.addEventListener(MouseEvent.CLICK, clear);
}

private function copy(event:MouseEvent):void{
rightField.text = leftField.text;
}

private function clear(event:MouseEvent):void {
leftField.text = "";
rightField.text = "";
}
}
}

あけおめ

ことよろ

虎丸


色塗る元気はありませんでした。

pixiv 用 SITEINFO (AutoPagerize on Greasemonkey)

pixiv を見ていると、あるユーザの絵を順に見たいときが結構あります。
以前、そういった用途に AutoPagerize の SITEINFO が追加されたことがあるのですが、
一瞬で無くなりました。苦情があったか、書き換えられたかしたのでしょう。
でも、個人的には便利だったので、自分で SITEINFO を追加しています。

AutoPagerize の SITEINFO は wedata に追加してグローバルに適用するか、
スクリプトを直接書き換えて、ローカルに適用することが出来ます。
前者の方法だと、いろんな人に迷惑が掛かる場合があるので、後者の方法で適用します。

Firefox から [ツール]>[Greasemonkey]>[ユーザスクリプトの管理] を開いて
AutoPagerize 選択し、編集ボタンを押します。エディタを適当に選ぶと編集画面が開きます。

上部に SITEINFO の変数があるので、以下のように編集します。
(コメントは初期のものです。当然無視して構いません)
var SITEINFO = [
/* sample
{
url: 'http://(.*).google.+/(search).+',
nextLink: 'id("navbar")//td[last()]/a',
pageElement: '//div[@id="res"]/div',
exampleUrl: 'http://www.google.com/search?q=nsIObserver',
},
*/
/* template
{
url: '',
nextLink: '',
pageElement: '',
exampleUrl: '',
},
*/
{
url: 'http://www.pixiv.net/member_illust.php\\?mode=medium&illust_id=',
nextLink: '//div[@id="content2"]//a[contains(text(), "»")]',
pageElement: '//div[@id="pixiv"]',
},
]
以上で、pixiv 絵を順に見る AutoPagerize の設定は完了です。

※ 余談 ※
AutoPagerize の SITEINFO は誰でも編集が可能です。
『このページは AutoPagerize 化すると便利だろうな』と思ったら一度編集してみると良いでしょう。
迷惑に思う人がいれば削除されますし、間違いがあれば XPATH に強い人が修正してくれます。
恐れずどんどん追加編集して、AutoPagerize をより便利に使えるようにしていきましょう。

ちなみに自分は、このサイトを AutoPagerize 化したいがために忍びブログの AutoPagerize を編集したことがあります。
drry氏に編集されたりして、結構面白かったです。(どうやらdrry氏は正規表現や、XPATH などで有名な人のようです)

Tumblr とか、各種ニュースサイトで流れている株価のグラフ

i0030306-1258634735


どうしてこうなった

諸事情により wicket-rome による RSS 出力コードの抜粋


【追記】
ここを参照してコードを書いた』的な記事を過去に書いていたw


wicket-rome を使ってRSSを出力する場合、以下のような方法が見つかる。
http://jroller.com/wireframe/entry/wicket_feedpage
public abstract class FeedPage extends WebPage {
@Override
public String getMarkupType() {
return "xml";
}

@Override
protected final void onRender(MarkupStream markupStream) {
PrintWriter writer = new PrintWriter(getResponse().getOutputStream());
SyndFeedOutput output = new SyndFeedOutput();
try {
output.output(getFeed(), writer);
} catch (IOException e) {
throw new RuntimeException("Error streaming feed.", e);
} catch (FeedException e) {
throw new RuntimeException("Error streaming feed.", e);
}
}

protected abstract SyndFeed getFeed();
}

これだと理由は不明だが日本語が文字化けする問題が発生する。
微妙に文字コードだけの問題でも無いような雰囲気。多分どうにか出来る気がするが。

でも、別の方法を取れば一応文字化けしないようなので以下抜粋。
[WicketApplication.java]
	@Override
protected void init() {
mountSharedResource("/rss", new ResourceReference("myFeed"){
@Override
protected Resource newResource() {
return new MyFeedResource(Guice.createInjector(new Module()).getInstance(Service.class));
}
}.getSharedResourceKey());
}

[RSSFeedPage.java]
public class MyFeedResource extends FeedResource{
private Service service;

public MyFeedResource(){}
public MyFeedResource(Service service){
this.service = service;
}

@Override
protected SyndFeed getFeed() {
SyndFeed feed = new SyndFeedImpl();
feed.setFeedType("rss_2.0");
feed.setEncoding("UTF-8");
feed.setTitle(service.getSetting().getBlogname());
feed.setLink(service.getSetting().getBlogurl());
feed.setDescription(service.getSetting().getDescription());
feed.setPublishedDate(new Date());

List<SyndEntry> entries = new ArrayList<SyndEntry>();
try {
for(ArticleModel articleModel : service.getArticleList(service.getSetting().getListnum(), 0)){
SyndEntry entry = new SyndEntryImpl();
entry.setTitle(articleModel.getTitle());
entry.setLink(service.getSetting().getBlogurl()+"detail/"+articleModel.getId()+"/");
entry.setPublishedDate(articleModel.getDate());
SyndContent description = new SyndContentImpl();
description.setType("text/plain");
description.setValue(articleModel.getContents());
entry.setDescription(description);
entries.add(entry);
}
} catch (SQLException e) {
e.printStackTrace();
}
feed.setEntries(entries);
return feed;
}
}

1.wicket-romeのFeedResourceを継承してgetFeedを実装したものを作成(MyFeedResource)。
2.WicketApplication#init()内にで、ResourceReferenceを実装して(MyFeedResource)を返すようにして、mountする。

ローカルだとこれで一応文字化けせずに表示できている。
どっか海外のサイトで解説されていたwicket-romeのRSS出力方法。どこだったかは失念。

WicketStuffのほうでも、Resourceとして出力するような例が示されているので、
多分Resourceとして出力するのが良いのかなぁという感じ。

誰得サービス Taggimblr

Taggimblr(たぎんぶらー)とは、Tumblrのpixiv絵のポストに自動でタグを付けるサービスです。
http://taggimblr.appspot.com/

いずれは、汎用的にタグ付け出来るようなサービスにするつもりです。
(URLとxpathをペアで設定できるようにするなどして、タグ付け対象を後から増やせるようにするなど)

HTTPURLConnection のラッパー

GAE/JのURLFetchが上手く行かなかったので、HTTPURLConnectionを使うようにしたが、
そのままだと煩雑過ぎるので、適当にラップしてみた。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import java.util.Map.Entry;

import com.google.appengine.repackaged.com.google.common.base.StringUtil;

public class URLFetch {
private String url;
private String method;
private String cookie;
private String response;
private Integer responseCode;
private boolean isConnected = false;

public URLFetch(String url, String method){
this.url = url;
this.method = method;
}

public String getCookie(){
if(!isConnected){
try {
throw new IOException("まだ接続していません");
} catch (IOException e) {
e.printStackTrace();
}
}
return this.cookie;
}
public String getResponse(){
if(!isConnected){
try {
throw new IOException("まだ接続していません");
} catch (IOException e) {
e.printStackTrace();
}
}
return this.response;
}
public Integer getResponseCode(){
if(!isConnected){
try {
throw new IOException("まだ接続していません");
} catch (IOException e) {
e.printStackTrace();
}
}
return this.responseCode;
}

public void newConnection(String url, String method){
this.url = url;
this.method = method;
this.cookie = null;
this.response = null;
this.responseCode = null;
this.isConnected = false;
}

public void doConnect(Map params, String cookie) throws IOException{
URL url = new URL(this.url);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod(this.method);
if(method.toLowerCase().equals("get")){
con.setDoInput(true);
if(cookie != null){
con.setInstanceFollowRedirects(false);
con.setRequestProperty("Cookie", cookie);
}
con.connect();
}else if(method.toLowerCase().equals("post")){
if(params != null){
con.setRequestProperty("Content-type", "application/x-www-form-urlencoded");
}
if(cookie != null){
con.setInstanceFollowRedirects(false);
con.setRequestProperty("Cookie", cookie);
}
con.setDoInput(true);
con.setDoOutput(true);

con.connect();
if(params != null){
OutputStreamWriter osw = new OutputStreamWriter(con.getOutputStream());
osw.write(makeParams(params));
osw.flush();
osw.close();
}
}
this.cookie = con.getHeaderField("Set-Cookie");
this.responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
String result = "";
String line;
while ((line = in.readLine()) != null) {
result += line;
}
this.response = result;
this.isConnected = true;
con.disconnect();
}

private String makeParams(Map params){
if(params.isEmpty())
return null;
String result = "";
for(Entry e: params.entrySet()){
result += e.getKey()+"="+e.getValue()+"&";
}
result = StringUtil.stripSuffix(result, "&");
return result;
}
}

使い方は以下のような感じ。GETはURLに直接パラメータを指定する形。修正する予定。
// コンストラクタでURLとMethodを指定します。
URLFetch uf = new URLFetch("http://mix3.tumblr.com/api/read", "GET");
URLFetch uf = new URLFetch("http://www.tumblr.com/api/authenticate", "POST");

// URLFetch#doConnectで、パラメータをHashで、クッキーを文字列で指定します。
uf.doConnect(null, null);
uf.doConnect(new HashMap(){{
put("key1", value1);
put("key2", value2);
}}, "");

// URLFetch#doConnect実行後、レスポンス結果を受け取れます。
uf.getResponseCode() // レスポンスコード
uf.getResponse() // レスポンス(文字列)
uf.getCookie() // クッキー

#shiwake3 が盛り上がってると聞いて、飛んできました

#shiwake3が面白いよ!ってことで、途中から、ネットの生中継を見てた(聞いてた)のだけど、
最初面白がって笑ってたけど、途中から笑えなくなって、冗談抜きで涙出てきて、すっごいブルーになった…

研究費の割り当てに外国人枠があって、研究費の3割ほどが外国人研究者に割り当てられるのだそうだけど、
それを削りたいという話で、削る理由が以下のような感じだった。
『研究費の割り当てに外国人枠があるけど、日本の研究がトップクラスであるなら、
金出さなくても勝手に向こうから来てくれるよね? だからいらないよね?』

これ聞いて、正直耳を疑った。研究してる人は研究の奴隷だとでも思ってるのか?と。
研究が好きな人は、お金が出なくても研究するかもしれないが、お金が出なければ間違いなくモチベーションは下がるし、
出来ればお金が出るところで研究しようとする。研究者の全てが『研究したい』だけが研究する理由では無い。

それに外国人研究者を招くことは、様々な交流が生む。交流し技術的な交わりがさらなる技術の発展に繋がる可能性を生む。
外国人研究者を招き入れる理由の多くは、そこにあるのだと思うのだけれども、そういう交流のメリットも『どうでも良い』という風だった。

この時点でもう完全にブルー入っていたけれども、この後研究費を削減するという話が来て、

『すぐにお金にならないような研究に金出す必要ないよね?』

みたいな理由で予算を削ろうとしてきて、これ聞いて自分は泣いた。

研究にも分野があって、基礎研究なんかはすぐにはお金にならないものが殆ど。
でも10年後、20年後、それが土台となってイマをより豊かにする技術が生まれる切欠になるかもしれない。
そういう研究もある。意味のある研究になるかどうかすぐには分からない。
でも、分からないからこそ、そこにお金をつぎ込んで支援してあげないと、色々な可能性を潰してしまう。

日本が技術立国として発展出来たのは、科学技術に対する弛まぬ投資があったから。
それを、こんな噛み合わない、議論にもなってないやりとりで潰してしまうのはあんまりじゃないかと思う。

そんなこんなで、仕分け人の削減縮退ありきで議論する気が全く無い感じが凄く嫌だった。
最初から削ることが決まってて、削れるかどかを見分けるような雰囲気では無かった。
削れるところは削るし、大事なところは削らない。それをするのが仕分け人の仕事だろうに。
そもそもこんな専門分野の話の席で『私素人で貴方の言っていること良く分からないのですけど』
みたいなことを平気で言うような人が混じって、予算削減縮退を決めること自体歪で違和感を感じる。

日本どうなるのかなぁ… 本気で心配になる…

GAE/J TaskQueue メモ

GAEではTaskQueueなるものが使える。

処理をキューに積んで、アプリケーションとは非同期に処理させることが出来る。
キューはいくつか(?)用意できるので、仕事を分割して、キューに分散させて積んで並列に処理。
処理結果をMemCacheなりDatastoreなり保存して、最後にマージすることで、並列処理が出来るという寸法。

並列処理で力を発揮すると思われるTaskQueueは、排他がどうの、スケールがどうのと、
面倒なことを考えなければ簡単に使える。

TaskQueueもcronと同じようにURLを叩くだけ。アプリケーション中、任意のタイミングで、
URLとパラメータを指定してキューにaddするだけ。後はキューがパラメータ付きでURLにアクセスしてくれる。

// 静的インポート
import static com.google.appengine.api.labs.taskqueue.TaskOptions.Builder.url;
import com.google.appengine.api.labs.taskqueue.Queue;
import com.google.appengine.api.labs.taskqueue.QueueFactory;

// デフォルトで使えるキューを取得する この場合、xmlによる設定いらず
// Queue queue = QueueFactory.getDefaultQueue();
// キューに名前を付けてキューを取得する
Queue queue = QueueFactory.getQueue("queue");
// パラメータとして、key1=value2, key2=value2 を設定して、/queue にアクセスしてくれる。
queue.add(url("/queue").param("key1", value1).param("key2", value));

名前を付けてキューを使用する場合はxmlファイルによる設定が必要。

war/WEB-INF/queue.xml
<queue-entries>
<queue>
<name>queue</name>
<rate>12/m</rate>
</queue>
</queue-entries>
<rate>1/s</rate>とかすると毎秒タスクを処理していく。

TaskQueueは、上手く使うと処理を高速化出来るのでパフォーマンスを気にする場合は積極的に使っていくと良いかもしれない。
もちろん並列処理にありがちな排他処理などには気をつけないといけないけれど。

GAE/J urlfetch ゴミメモ

URLFetchService ufs = URLFetchServiceFactory.getURLFetchService();
HTTPResponse request = new HTTPRequest(
new URL("http://www.pixiv.net/index.php"),
// GETがよければ HTTPMethod.GET を指定
HTTPMethod.POST,
// リダイレクトするとクッキーが消える場合があるので
// クッキーを取得したい場合はリダイレクトさせないこと
FetchOptions.Builder.doNotFollowRedirects()
);

// パラメータをここでセットする
request.setPayload(("mode=login&pixiv_id=user_id&pass=******&skip=1&submit=1").getBytes("UTF-8"));
request.addHeader(new HTTPHeader("Content-Type","application/x-www-form-urlencoded"));
response = ufs.fetch(request);

// respose のヘッダからクッキーを取得
for(HTTPHeader httpHeader : response.getHeaders()){
if(httpHeader.getName().equals("Set-Cookie")){
System.out.println("[Cookie] "+httpHeader.getValue());
}
}
こんな感じでローカルでは普通に動いていたのだが、デプロイすると何故かクッキーの取得が出来ない。
エラーも警告も確認出来ていないので、実際どうなのか分からないのだけど、
永続化処理しているにも関わらず保存された様子が無いので、クッキーを取得出来てないっぽい。

仕方ないので、java.net で置き換えることに。 こっちだと大丈夫
URL login_url = new URL("http://www.pixiv.net/index.php");
HttpURLConnection con = (HttpURLConnection) login_url.openConnection();
con.setRequestProperty("Content-type", "application/x-www-form-urlencoded");
con.setRequestMethod("POST");
// パラメータ送信するので、DoOutputをtureに
con.setDoOutput(true);
// クッキー取得するのでリダイレクトはさせない
con.setInstanceFollowRedirects(false);
con.connect();

// Streamでパラメータ送信
OutputStreamWriter osw = new OutputStreamWriter(con.getOutputStream());
osw.write("mode=login&pixiv_id=user_id&pass=******&skip=1&submit=1");
osw.flush();
osw.close();

// レスポンスもStream
//BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
//String result = "";
//String line;
//while ((line = in.readLine()) != null) {
// result += line;
//}

// クッキー取得
cookie = con.getHeaderField("Set-Cookie");
con.disconnect();

多分自分がなんかミスってるんだろうなと思いつつ、とりあえず動くほうで動かしておく。
<<  2010年2月  
  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            
Profile
mix3
主にTwitterに生息

絵を描くマのエンジニア死亡。
Twitter
pixiv blogparts - Daily Ranking
読書メーター
mix3の最近読んだ本