2014年12月22日月曜日

今年のTitanium向けに書いた記事まとめとアプリ紹介2014版

Titanium Advent Calendar 2014の22日目。今年自分が書いた記事のTitaniumに関するまとめ。

まず今年の革命的だった発見が2つ。

 

1つ目がアイコンフォント。

各デバイスごとに大きさの違う画像を切り出していたのがなくなった。色違いや大きさが違う場合もコードで指定するだけでいい。メモリ効率もいいらしい。

 

2つ目がthisとparentの存在。

これで無駄にグローバル領域やクロージャ(無名関数)を使わない書き方が出来るようになった。入れ子にならないのでコードが読みやすい。

 

 

そして、これらを発見する前だけど今年の初めに作ったアプリを紹介。

teniteoというママ向けのSNSサービスのクライアントアプリ。

本家のアプリ(Android, iOS)とは別に実験的なアプリ。ゲストでも閲覧できるのと学園のイメージを取り入れてみた。

appicon


技術的な特徴

  • Titanium SDK 3.2.0.GA
  • iPhone, Androidで同じ見た目。ソースコードは分けてない。必要なときだけif文で分岐している。
  • Alloyは使ってない
  • ListViewをかなり頑張っている
  • サーバーはNode.jsでWordPressのデータベースに接続

こんなListView。

device-2014-12-16-165206 device-2014-12-16-165411

コメントもListViewを使ってる。PCで投稿されたコメントの中に表示できないバグがある。。。

device-2014-12-16-165441 device-2014-12-16-165918

本当はもっと美少女ゲームっぽくしたかった。自分のデザインセンスのなさを痛感(キャラクターはデザイナーさんにお願いした)。

ゲストでも閲覧だけなら使えるので試したい方はどうぞ。サーバーでこのアプリ向けには3つのプロセスしか待ち受けしてないので遅く感じるかも。

 

来年は今作っているのも含めていくつかサービスを公開予定。

Titanium的にはSocket.IOと連携したリアルタイム性を重視したアプリを作りたい。

では、みなさん良いお年を。明日のTitanium Advent Calendarはcoeさんです。

 

< Related Posts >

2014年12月18日木曜日

開発中のTitanium SDKをInstallして試す

titanium_beta_install今起きている現象が開発バージョンで直っているかインストールして試してみたときの覚書。

環境: Mac OS X 10.10.1, Xcode 6.1.1, Titanium SDK 3.4.1.GA

最新のダウンロード先の一覧は下記から。

この時点で最新の3.5.0 BETAをダウンロードしてインストール。コマンドで全部やってくれる。

$ ti sdk install http://builds.appcelerator.com/mobile/3.5.0/mobilesdk-3.5.0.Beta-osx.zip

確認してSDKの切り替え
$ ti sdk
$ ti sdk select 3.5.0.Beta

切り替えた後はビルドする前にクリーンするのを忘れずに。
$ ti clean

 

< Related Posts >

2014年12月15日月曜日

TitaniumとWordPressで(比較的)簡単SNSアプリ

titanium_wordpressTitanium Advent Calendar 2014の15日目。Titanium + WordPress + Node.jsの環境がうまくいっているので紹介。

書いてる途中で簡単か?と思い直し「比較的」付けた(笑

慣れると
WordPressで公開している → アプリ作りましょうか?
まずはアプリでサービス提供 → Web版も提供
が短期間で出来るようになる。

長くなったのでポイントだけ。

 

WordPressを使う理由

  • データベースの構造がシンプルで汎用性がある
  • 標準でユーザー管理、投稿、コメントなどの基本機能が揃ってる
  • テーマやプラグインで本体に手を加えずに開発出来るのもいい
  • アクション・フィルターの機能がNode.jsからキックするのに便利
  • 重い処理や非同期処理したい場合はWP-CRONの仕組みが便利
  • 情報が豊富
  • 初心者でもWordPressは触った人が多いので教えなくて済む。
    → アプリ側を上級者で開発して、Web側は他人に振れる。
  • PHPフレームワークと比べて遅いけど、最近は負荷分散する仕組みが豊富なので許容範囲になる。
    → アプリのリクエストはNode.jsで処理するのでWordPress(PHP)の遅さは関係なくなる

 

 

Titaniumを使う理由

  • 一つのコードでAndroid, iOS両対応出来るのが強い
  • ネイティブとまでいかなくてもサクサク動く
  • 最悪ネイティブモジュールを作れば何とかなる
  • Node.jsと組み合わせときの開発効率がうまい(全部JavaScript)

 

Node.jsを使う理由

  • アプリからは同時に大量のリクエストが来るのでPHPでは捌き切れない
    → データベースはMySQLではなくスレッドプールが使えるMariaDBにする
  • TitaniumもJavaScript。CommonJSの考え方を理解してTitanium側のコード品質も上がっていく。
    → Titaniumで作った汎用関数がそのままNode.jsで使える

 

ちなみにWordPressは外部にAPIを提供しているので、アプリから直接WordPressにリクエストを送るだけでも大抵のことが出来る。

Node.jsのモジュールの中にはWordPressのXML-RPC APIに接続するのもある。

私は最初アプリ向けのAPIはWordPressのプラグインで実装したけど、遅すぎて耐えられなかった。そのときの記事がこれ。

 

Node.jsから直接SQL実行するのが一番というのが私の結論。

Node.jsでアプリのAPIを実装するときのポイント。WordPressとどう連携するか。

  • ユーザーのログイン、新規登録だけNode.jsからPHPコマンドを実行(wp_insert_userとか)
  • データの取得、登録はNode.jsからSQL文を発行する。
    → そうしないと速度的に耐えられない。
    → カテゴリーの登録だけちょっと苦戦するぐらいで後は難しくない。
  • WordPressのアクションを実行する汎用的なPHPを書いてNode.jsからアクション名を引数にキックすると便利。
  • メール送信とかはWordPress側(プラグイン)で実装しておくと、ウェブ版を実装したときに楽。
    → Node.jsからはアクション名でキック
  • ファイルアップロードもNode.js側でファイルの配置とDBにInsertして、あとの画像加工はアクション名からWordPress側(プラグイン)で実行する
  • PHPシリアライズされたデータでもphpjs使えばエンコード、デコード出来る
  • WordPress側でオブジェクトキャッシュを有効にしていると、直接SQLでアップデートしたデータがウェブ版に反映されない。
    → キャッシュをクリアするアクションをAPIから発行する必要あり

 

WordPress公式アプリはソースコードが公開されているので参考になるかもしれない。

GPLなのでコードを流用する場合はソースコードを公開しないといけない。便利な機能はTitaniumのモジュール用に切り出して公開するとみんなが幸せになれるかも。

 

PhoneGap + WordPressで考えている人も後でPhoneGapの限界ともっさり感で泣くより、CommonJS覚えてTitaniumで開発するのをオススメしたい。

 

明日のTitanium Advent Calendarはk0sukeyさんです。

 

< Related Posts >

2014年12月10日水曜日

【Titanium】Alloy vs Classic

titanium_alloy_vs_classic2年前にAlloyを試してみたけど、未だにClassicな書き方をしている私。そろそろAlloyも成熟してきたころだと思い調査したときの覚書。

環境:Titanium SDK 3.3.0.GA

私がクラシックな書き方をしている理由(言い訳)を考えてみた。

  • Alloyも結局クラシックなコードに変換している。だったら最初からクラシックな書き方でいいと思う
  • WEB開発で複数言語(PHP, HTML, CSS, JavaScript)から開放されたと思ったのに、やっぱりXMLとかCSSっぽいのを書かないといけないの?
  • CommonJSの考え方でサーバー側(NodeJS)とクライアント側(Titanium)をJavaScriptで書いているのが気持ちいい
  • Alloyにバグがあったら見つけるのも直すのも大変そう(まだAlloyを信用できない)
  • クラシックだとひとつの画面はひとつのJavaScriptファイルで完結するので管理しやすい
  • 今作っているアプリはサーバーと連携するクライアントアプリ。ビジネスロジックは全部サーバー側にあるので、MVCでいうところのViewをTitaniumで開発している感覚。さらにMVCで開発するほど大規模なアプリは今のところない。
  • 自分の中で確立した書き方から脱却するのが億劫

 

世間ではAlloyで開発するべきだという意見が一般的。

ベストアンサーを日本語訳してみた

  • Alloyは一般的な方法と統一したコードを提供する。これは他のAlloyプロジェクトの開発者や新規開発者が理解するのに容易。
  • Alloyは便利で役に立つたくさんのレイアウトショートカットを提供しているので、より早く開発することが出来る。我々のセールスチームは70%のコードを削減できた。これはたぶんベストな例だけど、Alloyアプリでたくさんのコード削減できる。
  • Alloyアプリは各プラットフォームに最適なコードと追加ツールを提供するのでクラシックなアプリよりも動作が速い。
  • Alloyは横長のアプリを作ったり、テーマを作ったり、再利用するコンポーネントを作るのに便利。これらはクラシックコードだと別々なカスタムソリューションが必要となる。
  • AlloyはMVCの原則に従って均一なコードを書くのを手助けしてくれる。これは前述したように開発をスピードアップする。しかもメンテナンスしやすいコードを書くのも手助けしてくれるため、トータルコストを削減する。そしてそういうコードはバグも少ない。
  • Alloyは一貫したディレクトリ構造を提供する。この構造はプロジェクト間でも変わらないし、入れ替わる開発者にとっても一貫したもの。
  • ほとんどのサンプルアプリやドキュメントはAlloyから先に作られる。ほとんどの新しいコミュニティで開発されるコンポーネントはAlloyのウィジェット。クラシック指向のコードではない。
  • 全ての拡張モジュールはクラシックなプロジェクトでも使える

 

全くスキがない。

今度新しいアプリを作るときはAlloyを使おうと思ってきた。

 

< Related Posts >

2014年12月8日月曜日

【Titanium】thisとparentとchildrenを使った書き方

Titanium Advent Calendar 2014の8日目。最近多用しているthis, parent, childrenを使った書き方の紹介です。

環境: Titanium SDK 3.3.0.GA

Alloyは使ってません。

これらを使うとループの中でクロージャ(無名関数)を作成する必要がなくなったり、「引数を使いまわしたいから無名関数でネストする」必要がなくなります。

メモリ効率的にもいいような気がします(実際は分かりません)。

例えばこんなコードがあったとします。

/ui/common/testView.js

// Expose API
exports.createView = createView;

function createView(title) {
    var frameView, buttonView, i;

    frameView = Ti.UI.createView({
        layout: 'vertical',
        height: Ti.UI.SIZE
    });

    for (i = 0; i < 10; i++) {
        buttonView = Ti.UI.createButton({
            title: title + i
        });

        (function() {
            var index = i;
            buttonView.addEventListener('click', function() {
                alert(index);
            });
        })();

        frameView.add(buttonView);
    }

    return frameView;
}

これを呼び出すapp.js

var win = Ti.UI.createWindow({
    backgroundColor:'#ffffff'
});

win.add(require('/ui/common/testView').createView('ボタン'));
win.open();

これを実行すると10個のボタンを配置します。それぞれクリックすると何番目のボタンがクリックされたかalertします。

iOS Simulator Screen shot Dec 7, 2014, 1.52.43 PM

何も問題なく動きますが、クロージャの中で宣言されたindexはメモリから開放されないようです。

なのでクロージャを使わない書き方にしてみます。childrenを参照すればbuttonViewも使わなくて済みます。Clickしたあとの処理も外に出します。

/ui/common/testView.js

// Expose API
exports.createView = createView;

function createView(title) {
    var frameView, i;

    frameView = Ti.UI.createView({
        layout: 'vertical',
        height: Ti.UI.SIZE
    });

    for (i = 0; i < 10; i++) {
        frameView.add(Ti.UI.createButton({
            title: title + i
        }));

        frameView.children[i].dataIndex = i;
        frameView.children[i].addEventListener('click', handleClick);
    }

    return frameView;
}

function handleClick(e) {
    alert(e.source.dataIndex);
}

 

これでネストするクロージャがなくなってスッキリしました。

次はモジュールとして使いやすくするために引数を追加して処理を分けてみます。

app.jsはこんな感じで呼び出します。

var win = Ti.UI.createWindow({
    layout: 'vertical',
    backgroundColor:'#ffffff'
});

win.add(require('/ui/common/testView').createView({
    title: '上のボタングループ',
    frameColor: '#faa'
}));

win.add(require('/ui/common/testView').createView({
    title: '下のボタングループ',
    frameColor: '#aaf'
}));

win.open();

 

testView側ではparentを参照して、呼び出されたときの値を参照するようにします。

あと、for文の中で追加するviewが複雑になったときのためにe.sourceを参照せずthisを使うようにします。

/ui/common/testView.js

// Expose API
exports.createView = createView;

function createView(args) {
    var frameView, i;

    frameView = Ti.UI.createView({
        layout: 'vertical',
        backgroundColor: args.frameColor,
        height: Ti.UI.SIZE
    });

    // Save to custom property
    frameView.viewData = args || {};

    for (i = 0; i < 10; i++) {
        frameView.add(Ti.UI.createButton({
            title: args.title + i
        }));

        // Save to custom property
        frameView.children[i].dataIndex = i;
        frameView.children[i].addEventListener('click', handleClick);
    }

    return frameView;
}

function handleClick() {
    var data = this.parent.viewData;
    alert(data.title + ':' + data.frameColor + ':' + this.dataIndex);
}

実行結果

iOS Simulator Screen shot Dec 7, 2014, 2.35.44 PM

こんな感じで無名関数をネストせずに書いていくことが出来ます。Androidでも問題なく動きます。

addEventListenerは自動でthisをセットしてくれますが、JavaScriptのcallメソッドを使うと自分でthisをセットして関数を呼び出すことが出来ます。

 

thisやparentはviewが複雑化したときに威力を発揮します。

実体験ではカレンダーの月表示を実装したときは、thisとparentとカスタムプロパティを使ってコードがスッキリしました。

一応今までのアプリは上手く行ってますが、懸念点としては

  • parentプロパティは公式ドキュメントに載ってないので、そのうち使えなくなるかもしれない
  • view自体に値を保存するやり方はいいのかな?
  • 実際にメモリ効率よくなるの?
  • Alloy使えばヨロシクやってくれるのかな?

 

あくまでも私はこう書いているよということで。「自分はこう書いてるよ」というコメント大歓迎です。

 

< Related Posts >

2014年11月26日水曜日

【Titanium】AndroidでMapモジュールが落ちる現象

titanium_android_mapiOS, Androidアプリ開発で、TitaniumのMapモジュールで嵌ったときの覚書。

環境: Titanium SDK 3.3.0 GA, Galaxy S2(Android 4.0.3)

下記エラーが表示されてマップ表示からしばらくしてアプリ自体が落ちる。

[DEBUG] REQUEST: Connection opened to:https://clients4.google.com/glm/mmap/api
[DEBUG] REQUEST: Open Connection
[DEBUG] REQUEST: DRD(65): 62|147
[DEBUG] REQUEST: Close
[DEBUG] REQUEST: Error processing: com.google.maps.api.android.lib6.b.d@416a5da8 not retrying
[DEBUG] REQUEST: Retrying: com.google.maps.api.android.lib6.c.au@418ecd60
[ERROR] REQUEST: ERROR
[ERROR] REQUEST: java.lang.ArrayIndexOutOfBoundsException: length=1; index=1
[ERROR] REQUEST:        at org.appcelerator.titanium.util.TiResponseCache.get(TiResponseCache.java:276)
[ERROR] REQUEST:
[ERROR] REQUEST:        at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:211)
[ERROR] REQUEST:        at libcore.net.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:80)
[ERROR] REQUEST:        at libcore.net.http.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:188)
[ERROR] REQUEST:        at libcore.net.http.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:280)
[ERROR] REQUEST:        at com.google.maps.api.android.lib6.b.l.run(Unknown Source)
[ERROR] REQUEST:        at java.lang.Thread.run(Thread.java:856)

 

こちらのチケットで報告されている。

私の場合、このエラーは下記のことを試しているうちに表示されなくなった。どれが効いたのかは分からない。

  • API Keyの再発行
  • アプリ削除
  • 端末再起動
  • Resources/android/images/にres-hdpi, res-mdpiを作った
  • annotationを使わなくした

 

ただannotationを表示するとマップを描画する時点でアプリ自体落ちる。

で、いろいろ試して分かった事。注意:Androidのみ

  • annotationのimageプロパティに指定する画像の大きさは共通でなければならない。iOSは別々の大きさでも問題ない。
  • 大きさが同じでもPNG画像で表示される(透明部分の領域)部分に大きな落差があった場合も表示されなかった。
  • 大きさ(サイズ)は最大32x32 64x64@2xみたい
  • 横長の画像(45x30, 90x60@2x)だと表示されなかった
  • 正方形でも15x15, 30x30@2xだと表示されなかった

 

あまりこの手の情報は見当たらないので別の条件があるのかもしれない。

 

< Related Posts >

2014年11月14日金曜日

TitaniumでWebSocketを使いたくてtiwsをビルドする

titanium_module_buildWebSocketを使ったアプリのイメージが出来たので、Titaniumでtiwsというネイティブモジュールを自前ビルドしてみたときの覚書。

環境:Mac OS X 10.9.4, Titanium SDK 3.3.0.GA

MarketPlaceでも配布されているのでそこからダウンロードすればビルドは必要ない。

今回はtiws自体が2年間更新がなく、何かあれば自分で対応するためにビルドしてみた。

まずはGitHubからCloneする。

$ cd ~/Documents/Titanium_Studio_Workspace/
$ git clone
https://github.com/iamyellow/tiws

Fork先でいくつか修正されているようなのでこちらを使うことにした。
$ git clone https://github.com/HilkoLantinga/tiws

 


iOS用をビルド

環境変数を変更
$ cd tiws/ios/
$ vi titanium.xcconfig

TITANIUM_SDK_VERSION = 3.3.0.GA

TITANIUM_SDK = /Users/daiki/Library/Application Support/Titanium/mobilesdk/osx/$(TITANIUM_SDK_VERSION)

ビルド実行。
$ ./build.py

カレントディレクトリに「net.iamyellow.tiws-iphone-0.3.zip」が出来る。

 


Android用をビルド

前の記事を参考にまずは環境を作る。

環境変数を編集。
$ cd ../android
$ vi build.properties

titanium.platform=/Users/daiki/Library/Application Support/Titanium/mobilesdk/osx/3.3.0.GA/android
android.platform=/Applications/android-sdk-macosx/platforms/android-14
google.apis=/Applications/android-sdk-macosx/add-ons/addon-google_apis-google-14

ビルド実行
$ ant

エラー

BUILD FAILED
/Users/daiki/Library/Application Support/Titanium/mobilesdk/osx/3.3.0.GA/module/android/build.xml:163: /Users/daiki/Documents/Titanium_Studio_Workspace/tiws/android/lib does not exist.

libsならあるのでシンボリックリンクを作ってみる。
$ pwd
$ ln -s /Users/daiki/Documents/Titanium_Studio_Workspace/tiws/android/libs/ lib
$ ls -l
 

ビルド再実行
$ ant

成功するとdistディレクトリに「net.iamyellow.tiws-android-0.1.zip」が出来る。

生成したzipファイルをこのモジュールを使うプロジェクトの直下に置いておけばビルドしたときに自動で解凍される。

 

< Related Posts >

2014年11月8日土曜日

アプリ向けのサーバーAPIは「REST」 or 「WebSocket」?

restapi_websocketアプリとサーバー間のやりとりをREST APIかまたはWebSocketで実装するか迷ったときの覚書。

よくまとまっているサイト

今のところ「共存出来るんだから、それぞれの特性を理解して使い分けろ」に落ち着いている感じ。

WebSocketでやりとりするメッセージにルール付けして、RESTを表現しようとする動きもある。

 

NginxはWebSocketをリバースプロキシ出来るし、ロードバランサとしても動作する。

 

ExpressJSとSocket.IOでセッションを共有出来る。

 

Socket.IOを利用すると名前空間(namespace)で機能分けして、部屋(room)でメッセージを送受信する範囲を制御できる。

接続すると自動的に1つの(自分のIDが付いた)roomに参加するので、特定の人だけにメッセージを送信する場合はその人だけが参加しているroomに送信する。

Socket.IO v1.0からバイナリー送受信もサポートされて、ファイルの送受信も簡単に実装できる。

PCを遠隔操作できるデモをPCやタブレットで複数開きながら操作すると感動する。

 

クライアントアプリをTitaniumで実装する場合は、iOS/Androidに対応したモジュールがある。

Ti.Network.Socket.TCPを使うと上記のネイティブモジュールを使わなくても出来るみたい。

ti-websocket-clientを使っている人が質問してtiwsを使えという回答がちらほら。

 

技術的には環境が整いつつある感じがするので、WebSocketで「最新情報を取得」とかやる必要がないアプリを作ってみようと思う。

 

オンラインゲームとかどんなプロトコルでやりとりしているのかな?

Google Cloud Platformにある記事とか参考になりそうだった。

 

何となく見えてきた。

 

< Related Posts >

2014年11月6日木曜日

node-mysqlからnode-mariasqlに乗り換える

nodejs_mariadb_mysqlNodeJSの開発にも慣れたことだし、そろそろMariaDBのスレッドプールに頼らずにnode-mysqlのpool機能を使って、DB接続を使いまわすようにしようと調べたときの覚書。

環境: MariaDB Server 10.0.14, node-mysql 2.5.2, node-mariasql 0.1.21

 

調査しているとnode-mysqlより速いnode-mariasqlを発見。

node-mysqlの作者が速度改善に取り組んだ記事が面白い。

 

速度比較ベンチマーク

 

ただDB接続をpoolしたい場合は別モジュールと組み合わせて開発する必要がある。

 

今のところnode-mariasqlに乗り換えて、MariaDBのスレッドプールに頼ることにした。

node-mysqlのpool機能を使って接続を使いまわすようにした方が結果的に速くなるかもしれない。

 

試してみて気付いたこと。

  • Node.js v0.11では動かない。v0.10では動く。
  • selectした行ごとにasync.mapで非同期処理している中でqueryを投げると「Already Closed」エラーになる。
    → res.on('end')の中でclient.close()を呼び出すと回避できた。

確かに速い気がするが、いつでもnode-mysqlに置き換えられるように作っておいた方が良さそう。

 

< 2015/04/09 Modified >
Apache Benchで試してみたところ速度的に大差ない。

node-mariasqlで実行したとき数値のカラム(countした値など)も文字列で返ってくるので、やっぱりnode-mysqlで開発することにした。

 

< Related Posts >

2014年10月31日金曜日

複数サーバーでセッション管理するためにRedisからMariaDB Cluster(MySQL)に変更

redis_to_mariadb前の記事で複数サーバー構成にした。nginxの設定では「ip_hash」を使いIPアドレスをキーにした負荷分散にしたけど、モバイルアプリからのアクセスは頻繁にセッションが切れる現象に遭遇。

環境: CentOS 6.6 x64, Node.js v0.11.14, MariaDB 10.0.14

今までアプリのセッションは各サーバーごとにRedisで管理していた。セッション管理するサーバーを用意して各アプリサーバーが参照するようにすれば出来る。

Redisでセッションを一元管理する場合のポイント

  • bind(/etc/redis.conf)は制限せずにファイヤーウォールで制限する
  • Redis Clusterもあるけど、まだ開発段階みたい

 

。。。と調べたけど、このサービスはMariaDB Galera Clusterでクラスター構築したので、セッション管理もこっちを使うことにした。

connect-mysqlを使えば解決。poolも使えるので便利。

connect-mysql-sessionの方が人気みたいだけど、内部でseqelizeというO/Rマッパーを使っている。node-mysqlを使ってほしいので除外。

MariaDB ClusterがサポートしているのはInnoDBかXtraDBストレージエンジンだけでMemoryは使えないので注意。

 

< Related Posts >

2014年10月25日土曜日

【Titanium】SQLiteのテーブル一覧を出力

titanium_sqliteTitanium.Databaseを使ってテーブルを作成したあとに、確認のためにデータベースのテーブル一覧を出力したときの覚書。

環境: Titanium SDK 3.3.0.GA

公式ドキュメントを参考に。

SQLで取得するためには「sqlite_master」からSELECTすればいいらしい。

データベース名を引数にして、構造をオブジェクトで返す関数を作った。

/**
* Get Database Structure
*
* @api public
* @param {String} dbName Database Name
* @return {Object}
*/
function getStructure(dbName) {
    var db, rsTables, rsColumns;
    var sql, tables, tableName;

    // Generate SQL
    sql = [];
    sql.push("SELECT name");
    sql.push("FROM sqlite_master");
    sql.push("WHERE type = 'table';");

    // Excute
    db = Ti.Database.open(dbName);
    rsTables = db.execute(sql.join(' '));

    // Loop in Table ResultSet
    tables = {};
    while (rsTables.isValidRow()) {
        tableName = rsTables.fieldByName('name');
        tables[tableName] = {};

        // Get Columns ResultSet
        rsColumns = db.execute("PRAGMA TABLE_INFO('" + tableName + "');");
        while (rsColumns.isValidRow()) {
            tables[tableName][rsColumns.field(1)] = {
                cid: rsColumns.field(0),
                type: rsColumns.field(2),
                notNull: rsColumns.field(3),
                defalutValue: rsColumns.field(4),
                primaryKey: rsColumns.field(5)
            };
            rsColumns.next();
        }
        // Close
        rsColumns.close();

        rsTables.next();
    }
    // Close
    rsTables.close();
    db.close();

    // Output for debug
    console.log('[Database Structure]');
    console.log(tables);

    return tables;
}

 

< Related Posts >

2014年10月14日火曜日

Google Compute Engineにインスタンス作成してSSHでログイン

gce_ssh開発用でGoogle Compute Engine上にCentOSインスタンスを作成してTera Termでログインするまでの覚書。

環境: f1-micro, CentOS 6.5 x86_64

 

目次

  1. Google Developers Consoleでプロジェクト作成
  2. Google Compute Engine上でインスタンス作成
  3. Tera Term(SSH)からrootでログイン
  4. 気付いたこと

 


1.Google Developers Consoleでプロジェクト作成

Google Developers Consoleにアクセスして「Create Project」する。

 


2.Google Compute Engine上でインスタンス作成

作成したプロジェクトを選択して、
Compute → Compute Engine
を選択すると初回はクレジットカードの登録が必要。住所と名前は日本語でも大丈夫だった。

有効になると
Compute → Compute Engine → VM instances
から「New instances」を選んでインスタンスを作成する。

MACHINE TYPEは料金に関係してくる。詳しくは公式サイトで。

「EXTERNAL IP」はEphemeral(一時IPアドレス)を選択しておけば追加料金はかからない。

作成後は自動で起動される。

一覧から選択して「SSH」を選択するとブラウザ版SSHが起動する。これでも十分操作可能。

 


3.Tera Term(SSH)からrootでログイン

TeraTerm Menuから起動するため。

詳しくは公式ドキュメントを参考にしながら。

まずは公開鍵と秘密鍵を作成する。この辺は前の記事を参考に。

作成した公開鍵を管理画面の「SSH Keys」から登録する。このとき最後にユーザー名を指定する。他のユーザー名を指定すると自動でユーザーが作成される。

ssh-rsa AAAAB3Nzahoge== root

image

今度は先ほどのブラウザSSHを起動してrootからのログインを許可する。
$ sudo vi /etc/ssh/sshd_config

PermitRootLogin yes

これでTera Termからログイン可能。

ちなみに他のユーザーでログインして
$ sudo su -
してもいい。

 


4.気付いたこと

停止(#shutdown -h now)したインスタンスを起動する手段がない。管理画面でRebootボタン押しても失敗する。

なぜ?

ディスクを切り離し → インスタンス削除 → 同じディスクから新たにインスタンス作成する必要がある。SSH Keyも再度追加しないといけないので面倒くさい。

External IPアドレスは変わらなかった。

大した時間はかからないけど、この辺はAWSの方が簡単便利。

スクリプトで自動化出来るみたいだけど。

 

< Related Posts >

2014年10月2日木曜日

PHPStormを64bitで動作+メモリ容量を増やして高速化

phpstorm_boostupコード補完候補が表示されるときに一瞬止まるのがイラッときたので調査。

環境: Windows 8.1 x64, JDK 8u20, PHPStorm 8.0.2

やってはみたものの日本語入力すると落ちるのでオススメしない。

→ こちらの方が既に報告してくれている様子。Javaのバグらしい。バージョン9で直るらしいのでしばらく先になりそう。

 

< 2015/03/24 Modified >
PHPStorm 8.0.3 + JDK 8u40で無事動作するのを確認。

 

参考サイト

 

目次

  • 使用中のメモリ容量を表示
  • 64bit版JDKをインストール
  • 64bit版PHPStormを起動
  • 最大使用メモリ容量を増やす

 


1.使用中のメモリ容量を表示

File → Settings → Appearance → Show memory indicator

 


2.64bit版JDKをインストール

Help → Aboutで確認するとJRE 1.7.0 x86を使っているので最新の64bit版JDK 1.8をインストールすれば速くなるかなと思ってやってみた。

JDKを公式サイトからダウンロード。JREでなくJDKと参考サイトに書いてあった。

「jdk-8u20-windows-x64.exe」を実行してインストール。

念のためマシン再起動。

 


3.64bit版PHPStormを起動

PHPStormのインストールディレクトリに移動して64bit版を起動。

C:\Program Files (x86)\JetBrains\PhpStorm 8.0.2\bin\PhpStorm64.exe

Help → Aboutでさっきインストールした1.8を使っているか確認。

 


4.最大使用メモリ容量を増やす

PhpStorm64.exe.vmoptionsを編集する。

エディタ(メモ帳など)を管理者として実行して下記のように変更。

変更前

-Xms128m
-Xmx750m
-XX:MaxPermSize=350m
-XX:ReservedCodeCacheSize=225m
-ea
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-Djsse.enableSNIExtension=false
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50

 

変更後

-Xms512m
-Xmx1024m
-XX:MaxPermSize=500m
-XX:ReservedCodeCacheSize=225m
-ea
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-Djsse.enableSNIExtension=false
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50

 

PhpStorm64.exeを再起動。メモリインディケーターの最大値が増えていることを確認。

何となく速くなった気がする。

 

。。。でも残念なことに日本語を入力後に必ず落ちるので、前の32bit版を使ってる。IMEはGoogle日本語入力。

 

< Related Posts >

2014年9月30日火曜日

WordPressで$_SESSIONが使えない

wordpress_errorページ遷移のログを簡易的に$_SESSIONに保存して使いまわそうとしたら動作しなかったので調査。

環境: CentOS 6.5, nginx 1.6.2, php-fpm 5.4.33

initにフックしてsession_startしても、各ページでsession_idするたびに新たに新しいIDになって引き継げない。

function hoge_init() {
    if (!session_id()) {
        session_start();
    }
}
add_action('init', 'hoge_init');

調べてみるとPHPの設定でsession.cookie_pathの設定が「/tmp」になってた。
# less /etc/php.ini

session.cookie_path = /tmp

cookie_pathはクッキーを保存するパス(URI)。一致したアクセスのみクッキーを使えるようになる。

cookie_pathを直したら動いた。

他のサーバーをみると「/」だったので、session.save_pathと勘違いして書き換えてたみたい。設定した覚えがないのでどっかのバージョンは/tmpに設定されるかも。

ちなみにsession.save_pathの設定は/etc/php-fpm.d/www.confに記載してある。

 

 

ちょっと深追いしてWordPressと_SESSIONの関係を調査。

WordPressはグローバル変数として_SESSIONを扱っていない。

var_dump($GLOBALS);
するとWordPressが生成したグローバル変数が出力される。

 

ということで$_SESSIONを使うのはやめて、update_user_metaを使うことにした。

 

< Related Posts >

2014年9月26日金曜日

WordPressのWP_Cronを使って非同期処理

wp-cronWordPressに投稿するとアプリにプッシュ通知する仕組みを作っているときの覚書。

環境: CentOS 6.5, WordPress 4.0.0

Linuxのcronとかpthreadに頼らずにやってみた。

WordPressにはwp_schedule_single_eventという登録したフックを一度だけ実行する関数がある。

ソースをみると10分の間に同じリクエストがくると無視されるが、$argsが違えばスケジュールに登録される。

同じ記事に対して何度もプッシュ通知が来てもウザいのでちょうどいい。

コードサンプルは上の公式サイトを参考に。

 

 

でも念のためLinuxのCronに10分ごとに/wp-cron.phpにリクエストを送る処理書いた。。。
# vi /etc/cron.d/wordpress

MAILTO=""

# Excute WordPress WP_Cron
*/10 * * * * root curl http://hoge.com/wp-cron.php > /dev/null 2>&1

参考サイト:Better wp-cron using linux's crontab

 

< Related Posts >

2014年9月25日木曜日

【Titanium】ビルドしたときにzipalignエラー

titanium_errorネットで拾った古いソースコードを古いSDKでビルドしたときに下記エラーに遭遇した。

環境: Mac OS X 10.9.4, Titanium SDK 3.3.0.GA, Android SDK Tools 23.0.2

[ERROR] Unable to find Android SDK tools: zipalign.
[ERROR] You have an incomplete or out-of-date installation.
[ERROR] Verify your Android SDK packages or reinstall the Android SDK by running 'titanium setup android' or manually downloading from http://appcelerator.com/android-sdk.

下記サイトを参考にAndroid SDKにあるzipalignをコピーする

$ cd /Applications/android-sdk-macosx/
$ cp build-tools/19.1.0/zipalign tools/

これでビルドは通った。

元に戻すときは削除するだけ。
$ rm -rf tools/zipalign

 

< Related Posts >

2014年9月24日水曜日

MariaDB(MySQL)設定のチューニング

mariadb_settings5年前にも書いたけど、調べ直したときの覚書。

環境: MariaDB 10.0.13

 

参考サイト

 

変数名 デフォルト 説明
max_connections 151 最大同時接続数。
Node.jsから非同期で処理する場合は一気に増加するので多めに設定。
thread_pool_size CPUの数 同時実行するスレッドグループの数。
デフォルトのままでいい。
参考:Thread pool in MariaDB 5.5 - MariaDB Knowledge Base
thread_cache_size 0 キャッシュするスレッドの数。
"pool-of-threads"の場合無視される。
table_open_cache 400 全てのスレッドでオープンするテーブルの数。
多すぎても少なすぎてもダメらしい。
参考:Optimizing table_open_cache - MariaDB Knowledge Base
参考:MySQL :: MySQL 5.1 リファレンスマニュアル :: 6.4.8 MySQL でのテーブルのオープンとクローズの方法
key_buffer_size 134217728 MyISAMのインデックスをキャッシュするメモリ容量。全てのスレッドで共通。
実メモリの25%が妥当らしい。
参考:Optimizing key_buffer_size - MariaDB Knowledge Base
sort_buffer_size 2M ソートするときにスレッドごとに使うメモリ容量。大きすぎるとメモリが足りなくなるので注意。
read_rnd_buffer_size 262144 ソート後にレコードを読み込むときに使用するメモリ容量。これもスレッドごと。
join_buffer_size 128KB インデックスを使わないテーブル結合に使われるメモリ容量。
read_buffer_size 128KB スレッドごとのシーケンシャルスキャンに使うメモリ容量。
参考:第2回 一番の基本,シーケンシャルスキャン:データベースと秋の空・RDBMS掌握術!<br />~PostgreSQLから学ぶDBMSとの付き合い方~|gihyo.jp … 技術評論社
query_cache_size 0 実行結果をキャッシュするメモリ容量。
query_cache_limit 1MB このサイズ以上の実行結果はキャッシュしない。

 

今の設定値を確認したい場合はSQLを実行する。
# mysql -p -u root
> show variables like '%size';

 

設定した値が妥当かどうか判断してくれるツールがある。

ダウンロードして実行
# cd /opt/software/
# wget http://mysqltuner.pl/ -O mysqltuner.pl
# perl mysqltuner.pl

 

 

< Related Posts >

2014年9月23日火曜日

ロードバランサとしてHAProxyかNginxか

haproxy_or_nginxロードバランサにHAProxyとNginxで迷ったときの覚書。

ベンチマークはHAProxyの方が速いみたい。

HAProxyは1.5からHTTPSもサポート。YUMのリポジトリにあるのは1.4が主流。

http, httpsをロードバランスするだけならNginxの方がいいかな?

Nginxでロードバランサするときは下記記事が参考になりそう。

 

< 2014/09/23 Modified >
Nginxの死活監視に不安があったけど、ちゃんと指定ポートに応答がなかったら別サーバーにリクエストを投げてくれた。

 

< Related Posts >

2014年9月22日月曜日

MariaDB Galera Clusterをインストールしてデータベースをクラスター化

新サービスのサーバー構成を考えているときの覚書。

環境: CentOS 6.5 x86_64, MariaDB Galera Server 10.0.13

前の記事も参考に。

サービス全体のサーバー構成。

server_structure

ロードバランサー02やWeb02がいればさらに安心できる。

ちなみにMariaDB Galera Clusterの最小ノード数は「3」。今回はデータベースをクラスター化するまで。

 

参考サイト

 

目次

  1. YUM経由でインストール
  2. 起動と設定
  3. 2台目以降のノード設定
  4. クラスターに参加

 


1.YUM経由でインストール

公式サイトを参考にYUMのリポジトリを追加する。

[web01]# vi /etc/yum.repos.d/MariaDB.repo

[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.0/centos6-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1

 

インストール実行
[web01]# yum search MariaDB
[web01]# yum install MariaDB-Galera-server

epelリポジトリを使ったので、前の記事を参考に登録した方がいいかも。

クラスター構成なので常に稼働する前提。メンテナンスで停止するときは手動で起動するため自動起動はしないようにしておく。
[web01]# chkconfig mysql off

 

 


2.起動と設定

公式ドキュメントに従う。

設定ファイルを編集
[web01]# vi /etc/my.cnf.d/server.cnf

[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

thread_handling = pool-of-threads
max_connections = 500

# Tuning
sort_buffer_size = 1MB
read_rnd_buffer_size = 1MB
join_buffer_size = 1MB
query_cache_size=16M

#
# * Galera-related settings
#
[galera]
# Mandatory settings
wsrep_provider=/usr/lib64/galera/libgalera_smm.so
wsrep_cluster_name=hoge-cluster
wsrep_cluster_address=gcomm://
wsrep_node_name=WEB01
wsrep_node_address=192.168.0.2
wsrep_slave_threads=8
binlog_format=row
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2

「wsrep_provider」「wsrep_cluster_address」はなくても良さそうだけど、コメントアウトしたら動かなかった。

最初のノードとして起動する。起動スクリプトを読むと「bootstrap」を指定すると「--wsrep-new-cluster」として実行してくれるみたい。
[web01]# /etc/rc.d/init.d/mysql bootstrap

セキュリティの初期設定
[web01]# mysql_secure_installation

起動状態を確認
[web01]# mysql -u root -p

> SHOW GLOBAL VARIABLES LIKE 'wsrep_%';

MySQLコンソール終了
> quit

 

各ノードとの通信に使うポートを開ける。
参考:Galera Cluster Address - MariaDB Knowledge Base
参考:習うより慣れろ! iptablesテンプレート集(1):ステートフルパケットフィルタを使ったサービスの公開 (2/6) - @IT
[web01]# vi /etc/sysconfig/iptables

-A INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -s 192.168.0.0/24 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 4567 -s 192.168.0.0/24 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 4444 -s 192.168.0.0/24 -j ACCEPT

ちなみに4444はrsyncが使う。

反映。確認。
[web01]# /etc/rc.d/init.d/iptables reload
[web01]# /etc/rc.d/init.d/iptables status

 


3.2番目以降のノード設定

インストールまでは一緒。

自動起動しないようにしておく。
[app01]# chkconfig mysql off

設定ファイルを編集
[app01]# vi /etc/my.cnf.d/server.cnf

[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
thread_handling = pool-of-threads
max_connections = 500

# Tuning
sort_buffer_size = 1MB
read_rnd_buffer_size = 1MB
join_buffer_size = 1MB
query_cache_size=16M

#
# * Galera-related settings
#
[galera]
# Mandatory settings
wsrep_provider=/usr/lib64/galera/libgalera_smm.so
wsrep_cluster_name=hoge-cluster
wsrep_cluster_address=gcomm://
wsrep_node_name=APP01
wsrep_node_address=192.168.0.3
wsrep_slave_threads=8
binlog_format=row
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2

赤文字以外は一緒。

 

ーーー ここから。後で気付いたクラスターに参加するので不要な設定

クラスターに参加せず起動
[app01]# /etc/rc.d/init.d/mysql start

セキュリティの初期設定
[app01]# mysql_secure_installation

Web01上のphpMyAdminからログインできるユーザーを作成する。
参考:MySQL :: MySQL 4.1 リファレンスマニュアル :: 4.4.5 MySQL への新規ユーザの追加
[app01]# mysql -p -u root mysql

> GRANT ALL PRIVILEGES ON *.* TO admin@localhost IDENTIFIED BY 'admin#1sec' WITH GRANT OPTION;
> GRANT ALL PRIVILEGES ON *.* TO admin@'192.168.0.%' IDENTIFIED BY 'some_pass' WITH GRANT OPTION;

> select host, user from user;

> quit

。。。とやったけど、同期後(クラスターに参加後)はWEB01で上書きされるのでWEB01で設定しておけば大丈夫だった。

ーーー ここまで。後で気付いたクラスターに参加するので不要な設定

 

次はファイヤーウォール(iptables)を設定。WEB01と同じ。ローカルIP以外はアクセス不可にする。
[app01]# vi /etc/sysconfig/iptables

-A INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -s 192.168.0.0/24 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 4567 -s 192.168.0.0/24 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 4444 -s 192.168.0.0/24 -j ACCEPT

iptablesを反映して確認。
[app01]# /etc/rc.d/init.d/iptables reload
[app01]# /etc/rc.d/init.d/iptables status

 

Web01上のphpMyAdminからApp01のデータベースを確認できるようにしておく。
[web01]# cd /home/httpd/httpdocs/phpMyAdmin/
[web01]# cp config.sample.inc.php config.inc.php
[web01]# vi config.inc.php

/*
* First server(WEB01)
*/
$i++;
/* Authentication type */
$cfg['Servers'][$i]['auth_type'] = 'cookie';
/* Server parameters */
$cfg['Servers'][$i]['host'] = 'localhost';
$cfg['Servers'][$i]['connect_type'] = 'tcp';
$cfg['Servers'][$i]['compress'] = false;
$cfg['Servers'][$i]['AllowNoPassword'] = false;

/*
* Second server(APP01)
*/
$i++;
/* Authentication type */
$cfg['Servers'][$i]['auth_type'] = 'cookie';
/* Server parameters */
$cfg['Servers'][$i]['host'] = '192.168.0.3';
$cfg['Servers'][$i]['connect_type'] = 'tcp';
$cfg['Servers'][$i]['compress'] = false;
$cfg['Servers'][$i]['AllowNoPassword'] = false;

下記にアクセスするとCookie用のランダム文字列を生成してくれる。

phpMyAdminから各サーバーに接続できることを確認。

 


4.クラスターに参加

APP01上で作業。まずは一旦停止。
[app01]# /etc/rc.d/init.d/mysql stop

クラスターのノードとして起動。gcommに指定するノードはクラスターに参加しているノードであればどれでもいいらしい。自動的に他のノードにも伝搬される。
[app01]# /etc/rc.d/init.d/mysql start --wsrep_cluster_address=gcomm://192.168.0.2

エラーになった場合はログを確認
[app01]# less /var/lib/mysql/app01.hoge.com.err

成功した場合は状態を確認
[app01]# mysql -p -u root

> SHOW STATUS LIKE 'wsrep_%';

| wsrep_local_state_comment    | Synced
| wsrep_incoming_addresses     | 192.168.0.2:3306,192.168.0.3:3306

phpMyAdminから、WEB01上でテーブルを作ってからAPP01上で削除したりして同期されるか確認。

この設定だと起動するときに「wsrep_cluster_address」オプションを付けないとクラスターに参加しないので注意。

一度クラスターを構成してしまえば、全てがプライマリなので最初にbootstrapで起動したノード(WEB01)も「wsrep_cluster_address」を付けて再起動する必要がある。

全てがプライマリという感覚がマスター・スレイブに慣れていると最初戸惑う。「どれがマスター?」と考えてしまう。

 

< 2014/09/25 Modified >
注意事項。詳しくは公式ドキュメントを参考に。MariaDB Galera Cluster - Known Limitations - MariaDB Knowledge Base

  • InnoDBしか同期しない。MyISAMはInsertしたあと他のNodeに反映されない。
  • 全てのテーブルはPrimary Keyも持つ必要がある。

 

< Related Posts >

2014年9月21日日曜日

Nginxを設定してWordPressにリバースプロキシする

フロントエンドにNginxのサーバーを立てて、バックエンドのWordPressにリバースプロキシする設定しているときの覚書。

環境: CentOS 6.5, Nginx 1.2.6, WordPress 4.0

システム構成は下図。ポイントは外部と通信するときはHTTPSで暗号化して、内部ではHTTPでやりとりする。

server_structure_https

 

参考サイト

 

以下「https://hoge.com/demo/」アクセスしたときにweb01のWordPressを表示する設定。

SSLのバージョンに注意。

lv01のnginx設定

server {
    listen 80;
    server_name hoge.com www.hoge.com;
    return 301 https://hoge.com$request_uri;
}

server {
    listen       443 ssl;
    server_name  hoge.com;

    root /home/httpd/httpdocs/;

    #
    # SSL
    #
    ssl on;
    ssl_certificate      /etc/nginx/ssl.d/2014_hoge_cert.pem;
    ssl_certificate_key  /etc/nginx/ssl.d/2014_hoge_nopass_key.pem;
    ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers  ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;
    ssl_prefer_server_ciphers   on;


    #
    # Header
    #
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Host $http_host;

    location /demo {
        proxy_pass http://web01.hoge.com;
    }
}

「web01.hoge.com」はhostsファイルで名前解決する。

 

web01のnginx設定

server {
    listen       80;
    server_name  .hoge.com;

    root    /home/httpd/hoge/web;
    index   index.php index.html;
    charset utf-8;


    #
    # Include other config files
    #
    include /etc/nginx/conf.d/common/location_default.conf;
    include /etc/nginx/conf.d/hoge/*.conf;
}

 

hoge/01_demo.conf

#
# Demo
#
location /demo {
    alias  /home/httpd/hoge/web-demo;

    access_log /var/log/nginx/hoge_demo.access.log;
    error_log /var/log/nginx/hoge_demo.error.log;

    # PHP-FPM
    location ~* \.php$ {
        fastcgi_split_path_info ^/demo(.+\.php)(.*)$;
        include /etc/nginx/conf.d/common/fastcgi_php.conf;
    }

    # Rewrite for WordPress
    if (!-e $request_filename) {
        rewrite ^/demo/(.*)$ /demo/index.php?q=$1 last;
    }
}

 

common/fastcgi_php.conf

#
# fastcgi for PHP-FPM
#
fastcgi_pass   127.0.0.1:9000;
fastcgi_index  index.php;
fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
fastcgi_intercept_errors on;
include        fastcgi_params;
fastcgi_buffer_size 1024k;
fastcgi_buffers 4 1024k;
client_max_body_size 5M;

 

common/location_default.conf

#
# Log Off
#
location ~ /robots.txt  { access_log off; log_not_found off; }
location ~ /favicon.ico { access_log off; log_not_found off; }

location ~ /\. {
    deny  all;
    access_log off;
    log_not_found off;
}

 

最後にweb01のWordPressのwp-config.phpに追記する。

/**
* If WordPress is behind reverse proxy which proxies https to http.
*/
if ( !empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ) {
        $_SERVER['HTTPS'] = 'on';
}

これをしないとweb01から見たらhttpアクセスなのでhttpsにリダイレクトし続ける。

LV01からWeb01のPHP-FPMへ直接処理投げてもいいかもしれない。

 

< Related Posts >

2014年9月19日金曜日

Nginxの設定(Config)変数の値を確認する方法

nginx_debugNginxでリバースプロキシして、バックエンドにあるWordPressを表示するように設定をゴニョゴニョしているときの覚書。

環境: Nginx 1.6.2

php-fpmとの連携を記述するときに「$document_root」の値を確認したい。

add_headerする方法が一番簡単。
# vi /etc/nginx/conf.d/01.wordpress.conf

#
# Demo
#
location /demo {
    root  /home/httpd/hoge/web-demo;
    try_files $uri $uri/ =404;

add_header debug_nginx1 "$document_root";
add_header debug_nginx2 "$fastcgi_script_name";
}

これでブラウザでアクセス。Google Chromeの開発ツールでは「Response Headers」に表示される。

その設定が反映されたか確認するのに便利。

 

< Related Posts >

2014年9月17日水曜日

【Titanium, Android】Notificationからアプリにデータを渡す

notification_tap_android新着プッシュ通知(Notification)をアプリ側に表示して、それを押したら新着記事を表示する方法を調査したときの覚書。

Androidで嵌った。

環境: Titanium SDK 3.3.0.GA

プッシュ通知を受け取るまでは前の記事を参考に。

 

Notificationをタップして発行するIntentにpushExtraでセットした値が取れない。試したこと。

  • Windowの「focus」イベントで渡されたActivityを参照
    Ti.Android.currentActivity.intent.getStringExtra(Ti.Android.EXTRA_TEXT)
  • Windowの「focus」イベントで渡されたActivityを参照。引数のイベントオブジェクトから参照。
    e.source.activity.intent.getStringExtra(Ti.Android.EXTRA_TEXT)
  • 起動後にrequireされた別ファイルでゴニョゴニョしているときに参照。
    Ti.Android.currentActivity.intent.getStringExtra(Ti.Android.EXTRA_TEXT)

どれもダメ。

 

どうやらIntentが渡されるActivityを勘違いしているみたい。

そもそも今起動中のActivityは何?

このサイトを参考に今起動中のActivityを表示。
$ cd /Applications/android-sdk-macosx/platform-tools/
$ ./adb shell dumpsys activity | grep -i run

  Running activities (most recent first):
      Run #3: ActivityRecord{4216fae0 com.hoge.appdemo/org.appcelerator.titanium.TiActivity}
      Run #2: ActivityRecord{426a7da0 com.hoge.appdemo/.HogeAppActivity}
      Run #1: ActivityRecord{41c99bf0 com.sec.android.app.twlauncher/.Launcher}
      Run #0: ActivityRecord{41bb6d98 com.sec.android.app.controlpanel/.activity.JobManagerActivity}
ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)

なぜか2つある。

このアプリはWindow1つだけなので、たぶんアプリとWindowかな?

と思いつつ、(Windowではなく)直接app.jsに書いてみると。。。

// Start the APP
require('/lib/core').init();

// Notification Test
console.log(Ti.Android.currentActivity.getStringExtra(Ti.Android.EXTRA_TEXT));

取れた!

 

どうやらAndroidのActivityに関して理解する必要がありそう。

 

ソースを見たわけではないけれど、Titaniumは起動後にアクティビティを作って、その後win.openすると「org.appcelerator.titanium.TiActivity」を生成するみたい。

まだ曖昧な部分があるけれど、動いたサンプルコードを一部抜粋。

app.js

// win.openする前でもTi.Android.currentActivityを取得可能

// Start the APP
// init関数の中ではNotificationからの値を取得できなかった。
require('/lib/core').init();

// Notification
if (Ti.Platform.osname == 'android') {
    var act = Ti.Android.currentActivity;
    var intent = act.intent;
    console.log(intent.getStringExtra(Ti.Android.EXTRA_TEXT));
    // ここでフラグ設定しておいて、windowのopenイベントで処理する
}

 

notification.js

/**
* This called from require('com.activate.gcm').registerC2dm
*/
function handlePushReceiveAndroid(e) {
    var intent, pending, notification;

    // Show Notification
    intent = Ti.Android.createIntent({
        className: Ti.App.getID() + '.HogeAppActivity',
        flags: Ti.Android.FLAG_ACTIVITY_NEW_TASK | Ti.Android.FLAG_ACTIVITY_CLEAR_TOP
    });
    intent.putExtra(Ti.Android.EXTRA_TEXT, 'test desu');

    // Intent for Click Notification
    pending = Ti.Android.createPendingIntent({
        intent: intent,
        flags: Ti.Android.FLAG_UPDATE_CURRENT
    });

    // Create Notification
    notification = Ti.Android.createNotification({
        contentIntent: pending,
        contentTitle: e.data.message,
        //flags: Ti.Android.FLAG_ONGOING_EVENT | Ti.Android.FLAG_NO_CLEAR
    });

    // Show
    Ti.Android.NotificationManager.notify(1, notification);
}

 

Notificationをタップしたときは常にアプリを起動し直す。本当は起動しているときはintentが更新されたイベントだけキャッチして処理したかった。今度再調査。

サーバーからのデータはputExtraするときにJSON形式で格納しておけばいいと思う。

最終的にはapp.js内のinitの引数にTi.Android.currentActivityを渡して処理することにした。

 

< Related Posts >

2014年9月16日火曜日

TitaniumアプリでPush Notification【Android版】

push-notification_android前回はiOSについて調査。今回はAndroid端末にプッシュ通知する方法を調査したときの覚書。

開発環境: Mac OS X 10.9.4, Titanium SDK 3.3.0.GA, Android 4.0.3
サーバー:CentOS 6.5

 

参考サイト

 

目次

  1. Google Cloud Messaging(GCM)を有効に
  2. GCMとの通信用モジュールを探す
  3. Titaniumモジュールをビルドする環境を整える
  4. Titaniumモジュールのビルド実行
  5. アプリからモジュールを利用
  6. サーバーからプッシュ通知テスト
  7. デバッグと検証してみた

 


1.Google Cloud Messaging(GCM)を有効に

iOSはApple Push Notification service(APNs)経由でプッシュ通知を送る。Androidの場合はGoogle Cloud Messaging(GCM)経由。

GCMのAPI Keyを取得

  1. Google API Consoleにアクセス
  2. プロジェクトを作成。
  3. プロジェクトの「Overview」を開くと左上に「Project Number」が表示されているのでこれをメモ。あとで「sender_id」としてアプリ側に記述する。
  4. APIs & auth → APIsから「Google Cloud Messaging for Android」を有効に
  5. APIs & auth → Credentialsから「Server Key」を作成。

 


2.GCMとの通信用モジュールを探す

iOSみたいにネイティブにサポートしてないので、公開されているモジュールを利用する。

「dist/com.activate.gcm-android-0.6.zip」をそのまま利用しても問題なかった。一応自分でカスタマイズできるようにビルドしてみた。

 

< 2014/12/15 Modified >
こちらの記事で紹介されている方がいろいろ修正されているみたい。

 


3.Titaniumモジュールをビルドする環境を整える

長くなったので別記事にした。

 


4.Titaniumモジュールのビルド実行

GitHubからcloneする
$ git clone https://github.com/liccowee/Google-Cloud-Messaging--Titanium-
$ cd Google-Cloud-Messaging--Titanium-/

環境変数を出力したいので、空のsampleモジュールを作る。
$ titanium create -p android -t module -d ./ -n sample --id com.sample

環境変数が記述されたbuild.propertiesをコピーする。
$ cp sample/android/build.properties ./

ちなみにbuild.propertiesはこんな感じだった。

titanium.platform=/Users/daiki/Library/Application Support/Titanium/mobilesdk/osx/3.3.0.GA/android
android.platform=/Applications/android-sdk-macosx/platforms/android-14
google.apis=/Applications/android-sdk-macosx/add-ons/addon-google_apis-google-14

 

manifestを編集
$ vi ./manifest

version: 0.6
minsdk: 3.3.0.GA

 

< 2015/04/24 Modified >
SDK 3.5.1.GAにしたらarchitecturesを加えないとビルドに失敗して「There is discrepancy between the architectures specified in manifest and compiled binary.」といわれた。

$ vi ./manifest

name: gcmjs
moduleid: net.iamyellow.gcmjs
guid:
platform: android
minsdk: 3.5.1.GA
architectures: armeabi armeabi-v7a x86

 

 

timodule.xmlを編集
$ vi ./timodule.xml

<uses-sdk android:minSdkVersion="10"/>

 

< 2015/04/24 Modified >
SDKをアップデートした場合はbuild.propertiesの値をチェック
$ vi ./build.properties

titanium.platform=/Users/daiki/Library/Application Support/Titanium/mobilesdk/osx/3.5.1.GA/android
android.platform=/Applications/android-sdk-macosx/platforms/android-21
google.apis=/Applications/android-sdk-macosx/add-ons/addon-google_apis-google-21

 

distディレクトリは空にしておく。
$ rm dist/*
$ ant clean

ビルド実行
$ ant

libディレクトリがないって怒られた。
$ mkdir lib

再ビルド
$ ant

distディレクトリに出力される。

 


5.アプリからモジュールを利用

Titaniumプロジェクトの直下に「com.activate.gcm-android-0.6.zip」を設置するだけで、次回ビルド時にインストールしてくれる。

自分で解凍しても同じ。
Titanium_Studio_Workspace/test_app/modules/android/com.activate.gcm

tiapp.xmlを編集。sender_idはGoogle API Consoleの「Project Number」

<modules>
    <module platform="android" version="0.6">com.activate.gcm</module>
</modules>
<property name="com.activate.gcm.sender_id" type="string">123427670900</property>
<property name="com.activate.gcm.component" type="string">com.hoge.app/org.appcelerator.titanium.TiActivity</property>

 

アプリ起動後に下記コードを実行して動作するか確認。

var gcm = require('com.activate.gcm');

gcm.registerC2dm({
    success: function(e) {
        console.log(e);
        alert('Registration ID:' + gcm.getRegistrationId());
    },
    error: function(e) {
        console.log(e);
    },
    callback: function(e) {
        alert('JS message event: ' + JSON.stringify(e.data));
        console.log(e);
    }
});

GCMにデバイスの登録が成功すると「success」プロパティに設定した関数が実行されて、registrationIdが返ってくる。

 


6.サーバーからプッシュ通知テスト

さっき取得したregistrationIdをターゲットにプッシュ通知をサーバーから送信してみる。

こちらを参考にしたPHPを設置。「$registration_id」にはデバイスのregistrationId。「$header」のkeyにはAPI Keyを記述する。

パラメーターの詳細は公式サイトを参考に。

<?php
$url = 'https://android.googleapis.com/gcm/send';

$registration_id = 'APAxxx';

$message = 'Hello, GCM!!';

$header = array(
  'Content-Type: application/json',
  'Authorization: key=xxx',
);
$post_list = array(
  'registration_ids' => array($registration_id),
  'collapse_key'     => 'demo',
  'data'             => array( "message" => $message )
);

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_list));

$ret = curl_exec($ch);
 
var_dump($ret);
?>

これを実行するとアプリ側で「callback」に設定した関数が実行されるはず。

 


7.デバッグと検証してみた

ddmsを立ち上げてログ出力を監視してみる。「C2dmModule」「GCM」というTagで絞り込むと見やすい。

image

どんな仕組みで動いているかまだよく分かっていない。

 

< 2015/04/24 Modified >
アプリのti.xmlにandroid:debuggable="true"を追記するとddmsでそのアプリの出力だけに絞り込むことが出来るので便利。

またtimodule.xmlに
<property name="ti.android.debug" type="bool">true</property>
を追記しないとモジュール内の
org.appcelerator.kroll.common.TiConfig.LOGD
がtrueにならないので、デバッグログを出力しない。

 

 

その他検証して分かったことなど

  • アプリをタスクマネージャーからKILLした時はプッシュ通知を受け取れない。GCMBaseIntentServiceがプッシュ通知に反応しない。
    これはLINEも同じ。これを解決するにはMessengerのように常時稼働するサービスを作る必要がある。
    メモリが圧迫されてアクティビティが破棄されるのはAndroid端末の「開発者向けオプション」→「アクティビティを破棄」をチェックすると再現できる。
  • 画面をロックしている状態だとcom.activate.gcm.componentを正しく設定してないと動かない(たぶん)。下記コマンドで現在稼働中のActivityを表示できる。
    $ ./adb shell dumpsys activity | grep -i run
  • 再起動直後はうまく動いてくれない。ddmsで見るとサービスは動いてプッシュ通知を検知している。要調査。

 

< 2014/12/19 Modified >
アプリがバックグラウンドのときにうまく動かなかったのはgcm.jsとgcm_activity.jsをResourcesの直下に設置してなかったから。

Forkされている方が断然使いやすかった。

 

< Related Posts >

2014年9月12日金曜日

Titaniumモジュールをビルドする環境を整える

titanium-module_buildこのTitaniumモジュールをビルドする環境を作ったときの覚書。

環境: Mac OS X 10.9.4, Titanium SDK 3.3.0.GA

 

参考

 

目次

  1. Android NDKをインストール
  2. Android SDK 2.3.3 (API 10)をインストール
  3. ビルドツールApache Antをインストール

 


1.Android NDKをインストール

TitaniumはNDK Revision 9をサポートしているらしいのでr9dをダウンロードする。
$ cd /Applications/
$ curl -O http://dl.google.com/android/ndk/android-ndk-r9d-darwin-x86_64.tar.bz2
$ tar jxvf android-ndk-r9d-darwin-x86_64.tar.bz2
$ rm android-ndk-r9d-darwin-x86_64.tar.bz2

環境変数を定義
$ vi ~/.profile

# Android NDK
export ANDROID_NDK=/Applications/android-ndk-r9d

確認
$ source ~/.profile
$ echo $ANDROID_NDK;

 


2.Android SDK 2.3.3 (API 10)をインストール

必須らしいので、android-sdk-macos/tools/androidを実行してインストール。

環境変数を定義してあるか確認。
vi ~/.profile

# Android SDK
export ANDROID_SDK=/Applications/android-sdk-macosx

確認
$ source ~/.profile
$ echo $ANDROID_SDK;

環境が整ったか確認
$ titanium setup

Where do you want to go? k

 


3.ビルドツールApache Antをインストール

公式サイトからダウンロード。
$ curl -O http://ftp.riken.jp/net/apache//ant/binaries/apache-ant-1.9.4-bin.tar.gz
$ tar -zxvf apache-ant-1.9.4-bin.tar.gz
$ rm apache-ant-1.9.4-bin.tar.gz

パスを通す
$ vi ~/.profile

# Apache Ant
export PATH=$PATH:/Applications/apache-ant-1.9.4/bin

確認
$ source ~/.profile
$ echo $PATH;

ちなみにgperfはMacの場合Xcodeを付随してインストールされる。

 

< Related Posts >

Related Posts Plugin for WordPress, Blogger...

Blog Archives