OpenStreetMapってのは 誰でも自由に使用する事ができ、地図情報自体も編集できる地図です。 先日作成する勉強会に出てきたのですが実際地図も作ったりしてきました。 ちょっと地図使うサービスを作り始めていたので せっかくなのでとOSMで地図表示してみる事にしました。leafletとの出逢い国内のブログを何個か見て「これは苦労するな」と感じました。 使用するJSにvar記述がなくて、変数等が どのように管理されているかわからなかったからです。 jQueryライクな記述ができるようなプラグインはないかな?と 感じていて「下手すると作るか?」と思った時に「leaflef」に出逢いました。マップ作成方法まずHTMLに表示部分を準備します(サイズ等はお任せします)その後JSで
- <div id="canvas"></div>
setView()は中心の点です。次の引数がズーム率。 この場合日本がほぼ入る位の表示ですね。 2行目は著作権表示です。 これだけで表示されます!
- var map = L.map('canvas').setView([38.0,140.0],6)
- L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
- attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
- }).addTo(map);
バルーン(maker)を表示私のサービスはTwitterで情報を拾う予定だったので 拾ってきた情報を表示させてみよう!って事になりました。 正直地図の表示だけだと、leafletは必要なかったんですけどね。これだけです><。 座標を渡してあげて、マップを指定。 それにcontent(表示するHTML)を設定してあげたら、
- var marker = L.marker([lat,lon]).addTo(map);
- marker.bindPopup(content);
という感じで表示されます。 本当に簡単です。
大河内教授の憂鬱
システムに絶対はない。だからシステム屋は悩み続けなければならん。君の苦悩を私は支持するよ。(すみませんGistにソース移行中です)
2014年4月28日月曜日
OpenStreetMapでバルーン表示してみた
2014年4月15日火曜日
Go言語開発をVimで
Shizuoka.goという勉強会を立ち上げて、 静岡にGo言語旋風を巻き起こそうと考えていたのですが、 立ち上げた割に開発自体はあんまりしてないなぁって事で最近目下勉強中です。 当初Twitterクライアントを作っていたのですが、 必殺のOAuth周りを触って覚えようって事で自作OAuthライブラリも作りました。 ※この辺りは後日 ちなみに勉強会をやるに当たって、 みなさん多いだろうって事でIntelliJIDEAを導入して開発していたのですが、 『Vimでやりたい!』という嬉しい声が上がったので 私の設定をブログで起こす事になりました。 ちなみにIntelliJを採用したのは、viのプラグインがある為で、 『LiteIDE』にviキーバインドがあればそっちに行くと思います。 QtにFakeVimというのがあって、それをコンパイルから行えば可能かは現在調査中です。 ※この辺りもうまく行けば後日基本設定GoのSDKについているものを読み込ませます。 環境変数GOROOTはSDKを配置したパスです。 .vimrcファイルに
- set runtimepath+=$GOROOT/misc/vim
set runtimepath+=$GOROOT/misc/vimと追加します。 これによって『.go』ファイルがシンタックス表示されます。 コマンドモードで
- :Import net/http
:Import net/httpとすればnet/httpのimport文を自動補完してくれます。
- :Fmt
:Fmtとやると『go fmt xxx.go』をやってくれます。 これがSDK内にあるのは嬉しい! ただ私は
- auto BufWritePre *.go Fmt
auto BufWritePre *.go Fmtを追加して、ファイル保存時にfmtを勝手に行うようにしています。 これ実は各IDEに欲しくて、IntelliJのプラグインでやってくれる設定を PullRequestしようかと考えているところです。入力補完まず環境変数GOPATHを設定します。 これはGo言語のライブラリ等を取ってきた時に配置される場所です。 それでhttp://github.com/nsf/gocodeのコードを取ってきます。
- go get github.com/nsf/gocode
go get github.com/nsf/gocode取得できたら、
- exe "set runtimepath+=".globpath($GOPATH, "src/github.com/nsf/gocode/vim")
exe "set runtimepath+=".globpath($GOPATH, "src/github.com/nsf/gocode/vim")を追加します。 これで、『.』の後にをタイプすれば、入力候補が出てきます。 色が気持ち悪いので
- hi Pmenu ctermbg=7
- hi PmenuSel ctermbg=4
- hi PMenuSbar ctermbg=8
hi Pmenu ctermbg=7 hi PmenuSel ctermbg=4 hi PMenuSbar ctermbg=8を設定しています。 私は『neocomplcache』を入れて『.』で自動的に入力候補を出してくれます。 NeoBundleを入れてやってます。他の設定私はプレゼンで使う事も多いので端末の文字が大きめなので Goだとタブがきついので
- set tabstop=4
- set shiftwidth=4
- set softtabstop=4
set tabstop=4 set shiftwidth=4 set softtabstop=4を入れて少し幅を小さくしています。 プラグインのツリー表示を前に使ってたのですが、 最近パソコンがいかれたので、 折角なのでExモードでタブを使う用にしています。 『vim』ってだけ打つとExモードで立ち上がる設定
- function! s:GetBufByte()
- let byte = line2byte(line('$') + 1)
- if byte == -1
- return 0
- else
- return byte - 1
- endif
- endfunction
- autocmd VimEnter * nested if @% == '' && s:GetBufByte() == 0 | Ex | endif
function! s:GetBufByte() let byte = line2byte(line('$') + 1) if byte == -1 return 0 else return byte - 1 endif endfunction autocmd VimEnter * nested if @% == '' && s:GetBufByte() == 0 | Ex | endifまぁこんな感じです。 本当にGo使う為だけに設定した.vimrcはこちら。package main import "fmt" func main() { fmt.Println("%d",5) }
2012年4月28日土曜日
Windows8 でのデータ保存
さてGoogle+に対してのOAuthアクセスは作成しましたが、 アクセストークン等のアプリケーションデータを保存しよーと思ったら、 WindowsPhone的なIsolatedStorageFileStreamがない事に気付きました。 そう言われるとデータベースとか、スマートフォン系の特殊なアクセスばっかりで 「設定ファイル」何かを作ってデータを永続化してないなぁと思ったので 設定ファイルを作ってみる事にした。シリアライズ可能なオブジェクトシリアライズ可能なクラスを作成します。
- private static Settings _settings;
- public static Settings Settings {
- get { return _settings; }
- }
private static Settings _settings; public static Settings Settings { get { return _settings; } }私はAppにstaticなメンバを作成しておきました。 この中にアクセストークンのデータを残すようにしておきます。 ※アクセストークン(string)、リフレッシュトークン(string)、有効期限(現在時刻から足し込んだDateTime)データの保存データの保存は以下の通りで行いました。
- async static private Task<StorageFile> GetSettingFile()
- {
- var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
- StorageFile file = await folder.CreateFileAsync("settings.xml",CreationCollisionOption.OpenIfExists);
- return file;
- }
- async static public void SaveSetting()
- {
- var file = await GetSettingFile();
- var serial = new XmlSerializer(typeof(Settings));
- using ( var tWriter = new StringWriter() ) {
- serial.Serialize(tWriter, _settings);
- using (var stream = await file.OpenAsync(FileAccessMode.ReadWriteUnsafe))
- {
- var outputStream = stream.GetOutputStreamAt(0);
- using (var writer = new DataWriter(outputStream))
- {
- writer.WriteString(tWriter.ToString());
- await writer.StoreAsync();
- }
- }
- }
- }
async static private Task<StorageFile> GetSettingFile() { var folder = Windows.ApplicationModel.Package.Current.InstalledLocation; StorageFile file = await folder.CreateFileAsync("settings.xml",CreationCollisionOption.OpenIfExists); return file; } async static public void SaveSetting() { var file = await GetSettingFile(); var serial = new XmlSerializer(typeof(Settings)); using ( var tWriter = new StringWriter() ) { serial.Serialize(tWriter, _settings); using (var stream = await file.OpenAsync(FileAccessMode.ReadWriteUnsafe)) { var outputStream = stream.GetOutputStreamAt(0); using (var writer = new DataWriter(outputStream)) { writer.WriteString(tWriter.ToString()); await writer.StoreAsync(); } } } }「Windows.ApplicationModel.Package.Current.InstalledLocation」で インストールしたディレクトリを取得する事ができます。 他にドキュメント内とかでも保存できるのですが、 他の場所に保存する場合は、パーミッション(Package.appxmanifest)が必要になるようです。 ※また機会あったら書きます。 CreateFileAsync()を使用していますが、 同期的に取得するのでawaitで呼び出しています。 第2引数の「CreationCollisionOption.OpenIfExists」は存在しない場合作成してくれます。 ※これを付けないとsettings(2).xmlとかをモリモリ作ります。 あとはSettingsオブジェクトをシリアライズして保存しています。データの取得
- async private void LoadSetting()
- {
- var file = await GetSettingFile();
- using (var stream = await file.OpenAsync(FileAccessMode.Read))
- {
- var inputStream = stream.GetInputStreamAt(0);
- using (var reader = new DataReader(inputStream))
- {
- uint size = (uint)stream.Size;
- await reader.LoadAsync(size);っd
- string data = reader.ReadString(size);
- if (!data.Equals(""))
- {
- using (var textReader = new StringReader(data))
- {
- XmlSerializer serial = new XmlSerializer(typeof(Settings));
- _settings = (Settings)serial.Deserialize(textReader);
- }
- }
- else
- {
- _settings = new Settings();
- }
- }
- }
- }
async private void LoadSetting() { var file = await GetSettingFile(); using (var stream = await file.OpenAsync(FileAccessMode.Read)) { var inputStream = stream.GetInputStreamAt(0); using (var reader = new DataReader(inputStream)) { uint size = (uint)stream.Size; await reader.LoadAsync(size);っd string data = reader.ReadString(size); if (!data.Equals("")) { using (var textReader = new StringReader(data)) { XmlSerializer serial = new XmlSerializer(typeof(Settings)); _settings = (Settings)serial.Deserialize(textReader); } } else { _settings = new Settings(); } } } }後は同様に読み込むだけですね。保存したデータ
- <?xml version="1.0" encoding="utf-16"?>
- <Settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
- <AccessToken>
- <accessToken>ya29.AHES6ZQ0-H7cotFnLdVAcZ5CVz280mBarXy4tUn7-_Bs-AE</accessToken>
- <refreshToken>1/x8SHm9FaHD299fdZSNNyme2m3X2Up_Ki90pcUc2Y2Iw</refreshToken>
- <expiresIn>2012-04-28T21:48:28.55985+09:00</expiresIn>
- </AccessToken>
- </Settings>
<?xml version="1.0" encoding="utf-16"?> <Settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <AccessToken> <accessToken>ya29.AHES6ZQ0-H7cotFnLdVAcZ5CVz280mBarXy4tUn7-_Bs-AE</accessToken> <refreshToken>1/x8SHm9FaHD299fdZSNNyme2m3X2Up_Ki90pcUc2Y2Iw</refreshToken> <expiresIn>2012-04-28T21:48:28.55985+09:00</expiresIn> </AccessToken> </Settings>こんな感じで残ってました。 ※AccessTokenクラスで保存しています。 開発中は、 「C:\Users\(ユーザ名)\Documents\Visual Studio 11\Projects\GooglePlus\GooglePlus\bin\Debug\AppX」 って位置にありました。 リリースすると 「C:\Users\(ユーザ名)\AppxLayouts\(アプリのID)」 のようです。感想保存方法がかなりWindowsPhoneと違いました。 現状開発中で気になっているのは Suspendingのイベントに対して、保存処理を行ったんですけど、 開発中だからか、動作せずにStaticで他の画面から呼び出している状態になってしまいました。
何か間違ってるのかな? await async知らなかったので呼び出した時のエラー原因がわからなかったです。
2012年4月26日木曜日
Windows8でのWebViewのスクレイピング
Windows8用アプリの開発がVisualStudio11(ベータ版)により可能になりました。 iPhoneが世間を席巻している状況下でも 現在のWindowsユーザがMacへの移行をはじめるのかは怪しいところで、 ※開発者の移動はかなりありましたが。 Windows8への移行により、かなりのユーザさんが、 困惑するんじゃないかなぁ~と心配しているところではあります。 ※特にまだXPを使っている人 私はWindowsPhone(というよりMetro)のかわいさに負けて、 少し開発しましたが、Microsoftの底力というか、 言語設計からなる雰囲気に少しMicrosoftに惚れ直したところでして、 メインマシンをWindows8端末にして、Metro開発でもやってみよーかなーと思っていまして。 以前、WindowsPhoneで少し開発したGoogle+アプリを Windows8に移植しようと思い立ったわけです。 で最初のOAuth部分でWebViewに関して仕様が違ったみたいなので 以前書いた「認可コードのスクレイピング(WindowsPhone)」との違いを書きたいと思います。2点の違い試したのは「LoadCompletedイベント」の時なので 他のイベントの時にどうかはわかりません。 以前のコードの仕様としては 「認可後のページを開いたら、HTMLを取得して認可コードを取得」 っていう感じなのですが、 ・WebViewのUriが更新されない ・WebViewのSaveToString()がサポートされてない の2点が違います。アクセスしているUriの取得これはすごく単純でイベント自体のUriを取得すれば良いので
- private void loginView_LoadCompleted(object sender, NavigationEventArgs e)
- {
- string uri = e.Uri.AbsoluteUri;
- }
private void loginView_LoadCompleted(object sender, NavigationEventArgs e) { string uri = e.Uri.AbsoluteUri; }とする事で回避できます。 回避というか、これが当たり前かな?HTMLの取得方法これはトリッキーというか正式にやっていいものなのか。。。 別の方法がある気がするのですが、私的にはHTMLがどうしても欲しかったので JavaScriptを呼び出すInvokeScript()を使ってHTMLを抜き出してみました。 eval()関数でouterHTMLを取得してみました。
- string html = loginView.InvokeScript("eval", new string[] { "document.documentElement.outerHTML;" });
string html = loginView.InvokeScript("eval", new string[] { "document.documentElement.outerHTML;" });これでHTMLが抜き出せます。最終的には単純に2つのプロパティ(メソッド)での取得データを切り替えただけでOKでした。
- private void loginView_LoadCompleted(object sender, NavigationEventArgs e)
- {
- string uri = e.Uri.AbsoluteUri;
- if (uri.IndexOf("/o/oauth2/approval") != -1)
- {
- string html = loginView.InvokeScript("eval", new string[] { "document.documentElement.outerHTML;" });
- int tagStart = html.IndexOf("<textarea");
- int tagStartEnd = html.IndexOf(">", tagStart);
- int tagEnd = html.IndexOf("</textarea>");
- string pin = html.Substring(tagStartEnd + 1, tagEnd - tagStartEnd - 1);
- pinText.Text = pin;
- }
- }
private void loginView_LoadCompleted(object sender, NavigationEventArgs e) { string uri = e.Uri.AbsoluteUri; if (uri.IndexOf("/o/oauth2/approval") != -1) { string html = loginView.InvokeScript("eval", new string[] { "document.documentElement.outerHTML;" }); int tagStart = html.IndexOf("<textarea"); int tagStartEnd = html.IndexOf(">", tagStart); int tagEnd = html.IndexOf("</textarea>"); string pin = html.Substring(tagStartEnd + 1, tagEnd - tagStartEnd - 1); pinText.Text = pin; } }という感じのコードになり って感じで認可コードも自動設定可能になりました。 あくまで見た感じだと WindowsPhoneの時とUIの部分で色々大変な事がありそうですけどね。
2012年4月8日日曜日
もうこわくない。SELinux
第7回静岡ITPro勉強会インフラ部にてSELinux界のスーパースターSELinuxで著名な石川さん(@ishikawa84g)をお呼びして 「SELinuxハンズオン」を行いました。部長も興奮しています。 開催日の3/31はかなりの暴風雨に見まわれ、 予定していた部長のお迎えがなくなり、私が迎えに行くことに。 SELinuxは運用まで行くとまぁ色々あるんだろうなぁと思い 泣きながらDisabledにしていました。※石川さんごめんなさい。 ハンズオンがあったお陰で、すっかりSELinuxが怖くなくなりました。 というよりSELinuxを悪者だと誤解していたようです。 資料と重複しますが、おさらいも兼ねて、ブログに残しておきます。 Fedora16を準備して行ったのですが 手元のCentOSでは結構コマンドが違いましたのでご注意を。キャーイシカワサーン #shizuinfra
— kazuto_andoさん (@ando_ando_ando) 3月 31, 2012SELinuxの歴史Linux Kernel2.6.x から標準オプションになっていて 実際このころから「ヒャホー2.6インスコしたぜぇー」ってやって Apache起動したら「ん?なんぞ?動かん」って感じになった記憶がある。 海外の映画とかドラマで地味に出てくる「NSA」が開発して、 最近ではSEAndroidというものも現れてきている。これもNSAらしい。 政府、金融機関等の調達基準をLinuxで満たす為に生まれたらしい。リファレンスモニタシステムコールが発生して、システムリソースに到達するまでに セキュリティポリシーを読み込んでそれを判定をしている仕組み。 わかりやすいので資料から拝借。 こんな感じ。 私が想像していたよりKernelに近い部分にいるんだなぁ~と感じた。DACとMACDACはUNIX系OSで用いられる制御方式。 読み書き実行をオーナー、グループ、その他で制御する方式ですね。 これはご存知のように結局rootだと何でもできるわけです。 それに対してMACはセキュリティポリシーの適用してブロックする方式で、 rootだろうがポリシーに合っているかだけで判断する。 SELinuxはDACでアクセス権限を評価したのち、MACのアクセス権限を評価しています。 SELinuxを適用している場合、 rootを乗っ取られたとしてもポリシー以外の事はできなくなる。 これが一番大きいですね。 実際CVEにも載っているようなコードを 実行さえできればかなりあっさりしたコードで乗っ取りが可能なわけなので システムを乗っ取りに対して最小限の被害に抑える事ができるようになる。SELinuxの状態SELinuxの状態は
- Enforcing
- Permissive
- Disabled
- $ getenforce
$ getenforceで現在のSELinuxの状態を知る事ができます。 「/etc/selinux/config」ファイルにある 「SELINUX=」の箇所を編集して、起動時の状態を設定できます。 より詳細な情報が知りたければ「sestatus」
- $ sestatus
- SELinux status: enabled
- SELinuxfs mount: /sys/fs/selinux
- Current mode: permissive
- Mode from config file: enforcing
- Policy version: 26
- Policy from config file: targeted
$ sestatus SELinux status: enabled SELinuxfs mount: /sys/fs/selinux Current mode: permissive Mode from config file: enforcing Policy version: 26 Policy from config file: targetedでファイルの状態や現在の状態を知る事ができます。 状態を変更する場合は
- $ setenforce 0 → Permissiveに設定
- $ setenforce 1 → Enforcingに設定
$ setenforce 0 → Permissiveに設定 $ setenforce 1 → Enforcingに設定で切り替えが可能になります。 Disabledにするにはファイルの編集でしかできません。
コマンドにZオプション!
Zをつければ怖くない。 #shizuinfra
— kazuto_andoさん (@ando_ando_ando) 3月 31, 2012
これは全く知らなかった!
様々なコマンドにZオプションをつけるとそれぞれのリソースが
どのようなタイプかがわかります。
- $ ls -Z
- -rw-r--r--. root root system_u:object_r:selinux_config_t:s0 config
- ・・・
- $ ps -Z
- system_u:system_r:httpd_t:s0 17718 ? 00:00:00 httpd
- ・・・
$ ls -Z -rw-r--r--. root root system_u:object_r:selinux_config_t:s0 config ・・・ $ ps -Z system_u:system_r:httpd_t:s0 17718 ? 00:00:00 httpd ・・・をー。 これでリソースがどのようなタイプになっているかがわかる。 ここについてくるタイプによってどのようにコントロールされるかが 決まってくるわけですね。
SELinuxが悪さをした(守ってくれた)場合
これが一番勉強になったというか、なるほどとそういう事かと思った。
「アプリが動かん!SELinuxをDisabled。動いたー」
という悪魔の囁きを無視し、行儀よくする方法を教わった。
ハンズオンではApacheを利用してファイルへのアクセスを試しました。
アプリが動かない!となって「SELinuxかな?」と感じたら
まずsetenforceコマンドで「Permissive」状態に切り替えます。でアプリを動かしてみる。
で、アプリが動いたとなると
「SELinuxが止めてくれている。石川さんありがとう(´ω`)」
と東京の方向を向いてお辞儀をする。
でログを見ます。
ログは「/var/log/audit/audit.log」に出力されます。
※auditが停止中は「/var/log/message」に出力されるそうです。
これがEnforcingの状態では実際にアクセスをストップさせ、
Permissiveモードの時はログだけを吐き出してくれる。
アクセスが停止される原因は「denied」と記述してあるので
ログをgrepすると
- $ /var/log/audit/audit.log | grep denied
- type=AVC msg=audit(1333866553.014:218): avc:
- denied { open } for pid=17721 comm="httpd" name="1.html" dev="dm-1"
- ino=2621442 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=file
$ /var/log/audit/audit.log | grep denied type=AVC msg=audit(1333866553.014:218): avc: denied { open } for pid=17721 comm="httpd" name="1.html" dev="dm-1" ino=2621442 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=fileという感じのログが出てくる。 これの意味については資料にありますが 簡単に言うと「1.htmlは~httpd_t:s0からopenされたけど、~default_t:s0タイプだぜ」 と言ってくれてます。 Webで見ようとしたファイルがApacheからOpenできないと叫んでいます。 この場合は
- $ restorecon -FRv パス
$ restorecon -FRv パスとしてファイルコンテキストを設定しなおす事で Apacheからこのファイルを見れるようになるってわけです。
感想
SELinuxが原因かな?と感じたら
- Permissiveで原因の切り分け
- ログの確認
- ポリシーに合わせる、再利用する、作成する(※)
- 動作させる
2012年3月17日土曜日
DropboxにRESTfulアクセス
少しDropboxにデータを送り込みたいとなって エンジンをGoogleAppEngineで送り込む事になったので少し調べました。 SDKはiOS,Android(Java),Ruby,Pythonがあります。 今回はJavaを選択したので見ましたがHttp「s」URLConnectionを使用しており、 これがAppEngineでサポートしてないので実行環境でエラーが起こります。 ってことでRESTfulアクセスで行なってみました。準備まずここからアプリを登録してクライアントキーを作成します。 作成するとこんな感じ。 AccessTypeは後から変えられません。 「App folder」は専用のディレクトリのみアクセス可能なもの。 「Full Dropbox」はすべてのファイルアクセスです。 専用のディレクトリのみでOKだったんですけど、Fullにしました。リクエストトークンの取得https://api.dropbox.com/1/oauth/request_tokenにアクセスを行います。で戻り値(line)は「oauth_token_secret=biv7oeyp3nvkb1g&oauth_token=ql0l5qpk8h10rjz」みたいな感じです。 buildOAuthHeader()はSDKのコードにもありますが、This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
URL url = new URL("https://api.dropbox.com/1/oauth/request_token"); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setDoInput(true); connection.addRequestProperty("Authorization", buildOAuthHeader(false)); connection.setRequestMethod("GET"); InputStream inStream = connection.getInputStream(); BufferedReader input = new BufferedReader(new InputStreamReader(inStream)); String line = input.readLine();
- private static String buildOAuthHeader(boolean accessToken) {
- Map<String,String> map = new HashMap<String,String>();
- map.put("oauth_version","1.0");
- map.put("oauth_signature_method","PLAINTEXT");
- map.put("oauth_consumer_key",APP_KEY);
- String sig = null;
- if ( accessToken ) {
- map.put("oauth_token",TOKEN_KEY);
- sig = encode(APP_SECRET) + "&" + encode(TOKEN_SECRET);
- } else {
- sig = encode(APP_SECRET) + "&";
- }
- map.put("oauth_signature",sig);
- StringBuilder buf = new StringBuilder();
- buf.append("OAuth ");
- Iterator<Entry<String, String>> itr = map.entrySet().iterator();
- while ( itr.hasNext() ) {
- Entry<String, String> entry = itr.next();
- buf.append(entry.getKey() + "=" + entry.getValue() + ",");
- }
- String data = buf.toString();
- return data.substring(0,data.length()-1);
- }
private static String buildOAuthHeader(boolean accessToken) { Map<String,String> map = new HashMap<String,String>(); map.put("oauth_version","1.0"); map.put("oauth_signature_method","PLAINTEXT"); map.put("oauth_consumer_key",APP_KEY); String sig = null; if ( accessToken ) { map.put("oauth_token",TOKEN_KEY); sig = encode(APP_SECRET) + "&" + encode(TOKEN_SECRET); } else { sig = encode(APP_SECRET) + "&"; } map.put("oauth_signature",sig); StringBuilder buf = new StringBuilder(); buf.append("OAuth "); Iterator<Entry<String, String>> itr = map.entrySet().iterator(); while ( itr.hasNext() ) { Entry<String, String> entry = itr.next(); buf.append(entry.getKey() + "=" + entry.getValue() + ","); } String data = buf.toString(); return data.substring(0,data.length()-1); }といった感じでヘッダに設定してあげてます。認可用のURL作成lineにある「oauth_token」の値を使います。
- System.out.println("https://www.dropbox.com/1/oauth/authorize?oauth_token=" + argsMap.get("oauth_token"));
- System.in.read();
System.out.println("https://www.dropbox.com/1/oauth/authorize?oauth_token=" + argsMap.get("oauth_token")); System.in.read();read()はブラウザからの入力待ちです。 ブラウザを立ち上げるとDropboxに認証してその後許可のアクションがあります。 このアクセスの際に「oauth_callback」を指定しておくとコールバックしてくれます。 操作後の挙動としては ・許可(あり):コールバックURLに戻る ・許可(なし):許可画面に遷移 ・拒否:Dropboxのhomeに遷移 って感じです。 ここで良くOAuthでの認可コードを取るようなコードを書いてましたが、 コールバックを指定したとしてもそれはDropboxにはありません。 ※ただしコールバックにはuidとoauth_tokenを付けてくれるのでユーザ特定は可能です。 ようは「認可」を行った時点でアクセストークンは取れる状態になるってわけです。アクセストークンの取得認可されていれば以下ができます。
- TOKEN_KEY = argsMap.get("oauth_token");
- TOKEN_SECRET = argsMap.get("oauth_token_secret");
- url = new URL("https://api.dropbox.com/1/oauth/access_token");
- connection = (HttpURLConnection)url.openConnection();
- connection.setDoInput(true);
- connection.addRequestProperty("Authorization", buildOAuthHeader(true));
- connection.setRequestMethod("GET");
- inStream = connection.getInputStream();
- input = new BufferedReader(new InputStreamReader(inStream));
- line = input.readLine();
TOKEN_KEY = argsMap.get("oauth_token"); TOKEN_SECRET = argsMap.get("oauth_token_secret"); url = new URL("https://api.dropbox.com/1/oauth/access_token"); connection = (HttpURLConnection)url.openConnection(); connection.setDoInput(true); connection.addRequestProperty("Authorization", buildOAuthHeader(true)); connection.setRequestMethod("GET"); inStream = connection.getInputStream(); input = new BufferedReader(new InputStreamReader(inStream)); line = input.readLine();リクエストトークンで取ってきていた値を設定してbuildOAuthHeader()の値をtrueの方で動かします。 アクセストークンもリクエスト時と同じように 「oauth_token_secret=biv7oeyp3nvkb1g&oauth_token=ql0l5qpk8h10rjz」 という値で入ってきます。 これでアクセス可能になります。ファイルのアップロード使用するのはファイルのアップロードだけだったので PUTでbodyに設定してあげてアップします。
- public static void put(String name,byte[] data) throws Exception {
- URL url = new URL("https://api-content.dropbox.com/1/files_put/dropbox/" + name);
- HttpURLConnection connection = (HttpURLConnection)url.openConnection();
- connection.setDoInput(true);
- connection.setDoOutput(true);
- connection.addRequestProperty("Authorization", buildOAuthHeader(true));
- connection.setRequestMethod("PUT");
- OutputStream outputStream = connection.getOutputStream();
- outputStream.write(data);
- outputStream.close();
- InputStream inStream = connection.getInputStream();
- BufferedReader input = new BufferedReader(new InputStreamReader(inStream));
- String line = "";
- while ((line = input.readLine()) != null) {
- System.out.println(line);
- }
- }
public static void put(String name,byte[] data) throws Exception { URL url = new URL("https://api-content.dropbox.com/1/files_put/dropbox/" + name); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setDoInput(true); connection.setDoOutput(true); connection.addRequestProperty("Authorization", buildOAuthHeader(true)); connection.setRequestMethod("PUT"); OutputStream outputStream = connection.getOutputStream(); outputStream.write(data); outputStream.close(); InputStream inStream = connection.getInputStream(); BufferedReader input = new BufferedReader(new InputStreamReader(inStream)); String line = ""; while ((line = input.readLine()) != null) { System.out.println(line); } }ここでURLにある「dropbox」です。 「Full Dropbox」を選択した場合はこれでOKなのですが、 「App folder」の場合は「sandbox」を指定してあげます。 前者はフルなのでユーザのディレクトリ通りに設定していったりするのですが、 sandboxの場合、「apps/[アプリID]」が使用されて、そこのアクセスのみが可能です。 フルアクセスのViewerアプリを作らない限り、後者を選択した方が良いでしょうね。 ※ブログ書き始めてアプリを変更しました。感想DropboxのAPIって使うとは思ってなかったのでアクセスするとは思いませんでした。 まぁ本家が作っているクライアントが同期とかもかなり優秀なので あまりアクセスする人はいないのかもしれませんね。 ただファイルアクセスはかなり簡単にできたので、 クラウドから一時的に持ってくるツールは作りやすいかな?と感じました。 何点か気になるのは ・POSTアクセスのAPIがGETで取得できる ・PLAINTEXTを使用している ・一度認可されていると許可とかが出ない って感じですかね? ちなみに自分以外に公開するには 「My Apps」のところで「Additional users」をONにする必要があります。 製品バージョンと特に制限はないみたいに見えます。 ※開発中は5人だけっぽい感じには書いてありますけど。
2012年3月10日土曜日
Xtionで画像を作ってみる
XtionでDepthGeneratorで深度を取得できますが、 カメラ的に使うにはImageGeneratorを使用します。 今回はJavaで取得してみます。設定値実はここではまりました。。。
- <Node type="Image" stopOnError="false" >
- <Configuration>
- <MapOutputMode xRes="640" yRes="480" FPS="30"/>
- </Configuration>
- </Node>
<Node type="Image" stopOnError="false" > <Configuration> <MapOutputMode xRes="640" yRes="480" FPS="30"/> </Configuration> </Node>とConfigに記述しておきます。 画面を指定してない場合、320x240で取得できます。 なんでハマったかというと 描画する予定の画面(640x480)に対して、 320x240のデータを埋め込んでしまった為、うまく描画できないという 現象に引っかかったからです。 なので作成する画像が320x240の場合は指定しなくてもOKです。画像データの作成
- imageGen = ImageGenerator.create(context);
imageGen = ImageGenerator.create(context);でインスタンスを取得。 描画処理等の永久ループの箇所に
- ImageMetaData imageMD = imageGen.getMetaData();
- ImageMap imageMap = imageMD.getData();
- ByteBuffer image = imageMap.createByteBuffer();
- while ( image.remaining() > 0 ) {
- int pos = image.position();
- byte pixel = image.get();
- imgbytes[pos] = pixel;
- }
ImageMetaData imageMD = imageGen.getMetaData(); ImageMap imageMap = imageMD.getData(); ByteBuffer image = imageMap.createByteBuffer(); while ( image.remaining() > 0 ) { int pos = image.position(); byte pixel = image.get(); imgbytes[pos] = pixel; }という風にbyte[]を作成します。 imageMapにある値はwidth*height*3になります。 *3の値はRGBを表しています。 表示する際には深度の時と同じように
- DataBufferByte dataBuffer = new DataBufferByte(imgbytes, width*height*3);
- WritableRaster raster = Raster.createInterleavedRaster(dataBuffer, width, height, width * 3, 3, new int[]{0, 1, 2}, null);
- ColorModel colorModel = new ComponentColorModel(
- ColorSpace.getInstance(ColorSpace.CS_sRGB),
- new int[]{8, 8, 8},
- false,
- false,
- ComponentColorModel.OPAQUE,
- DataBuffer.TYPE_BYTE);
- bimg = new BufferedImage(colorModel, raster, false, null);
- g.drawImage(bimg, 0, 0, null);
DataBufferByte dataBuffer = new DataBufferByte(imgbytes, width*height*3); WritableRaster raster = Raster.createInterleavedRaster(dataBuffer, width, height, width * 3, 3, new int[]{0, 1, 2}, null); ColorModel colorModel = new ComponentColorModel( ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[]{8, 8, 8}, false, false, ComponentColorModel.OPAQUE, DataBuffer.TYPE_BYTE); bimg = new BufferedImage(colorModel, raster, false, null); g.drawImage(bimg, 0, 0, null);となります。 (gは画面上にあるpaint用のGraphicsオブジェクトです) これでカメラのように画像を出力する事が可能です。 ( ー`дー´)キリッ。。。もちろん動きますよ。例えば私のアプリはWebSocketと連携させているので この画像データをそのままWebSocketで送ってみようと思っています。 しかしそれはXtionとはあまり関係ないので 例えばですが1枚無人の背景データを用意してみて、 人間として認識された部分を背景データと重ねて塗りつぶす。 というプレデターごっこ(おそらくある程度マージンがあるので微妙に人の形が浮かび上がる)等が できるかなぁ。。。と思っています。 Depthとかの話とか書いてないから何か微妙な説明になるかなぁ、、、 もう少し、Generator周りとかがかっこよくなったら、記述したいと思います。
登録:
投稿 (Atom)