@blog.justoneplanet.info

日々勉強

AWS SDK for PythonでRoute53を操作する

以下のスクリプトをサーバー起動時に実行する事でホスト名を固定する事ができる。

#!/usr/bin/python
import sys
import boto
from boto.route53.record import ResourceRecordSets
import boto.ec2
import json
import pprint

key = 'your key'
secret = 'your secret'
domain_name = 'zone name'

document = boto.utils.get_instance_identity()["document"]
region = document["region"]
instance_id = document["instanceId"]

ec2_conn = boto.ec2.connect_to_region(
  region,
  aws_access_key_id=key,
  aws_secret_access_key=secret
)
reservations = ec2_conn.get_all_instances(instance_ids=[instance_id])
instance = reservations[0].instances[0]


route53_conn = boto.connect_route53(key, secret)
zone = route53_conn.get_zone(domain_name)
record = zone.get_cname(instance_id + "." + domain_name)
if record is not None:
  zone.delete_cname(instance_id + "." + domain_name)
zone.add_cname(instance_id + "." + domain_name, instance.public_dns_name)

以下の処理で自身のinstance IDを取得できる。

document = boto.utils.get_instance_identity()["document"]
region = document["region"]
instance_id = document["instanceId"]

以下のようにして削除する前に存在するか確認する必要がある。

record = zone.get_cname(instance_id + "." + domain_name)
if record is not None:
  zone.delete_cname(instance_id + "." + domain_name)

以下のようにしてCNAMEを追加する。

zone.add_cname(instance_id + "." + domain_name, instance.public_dns_name)

参考

mozcで「かお」の変換時に顔文字が表示されないようにする

現状、かおの変換候補表示にタイムラグがある。

rewriter/emoticon_rewriter.cc

bool RewriteCandidate(Segments *segments)

以下の部分を削除する。

      // "かお"
    } else if (key == "\xE3\x81\x8B\xE3\x81\x8A") {
      // When key is "かお", expand all candidates in conservative way.
      const EmbeddedDictionary::Token *token
          = Singleton<EmoticonDictionary>::get()->GetDictionary()->AllToken();
      CHECK(token);
      // first 6 candidates are inserted at 4 th position.
      // Other candidates are pushed to the buttom.
      value = token->value;
      value_size = token->value_size;
      initial_insert_pos = 4;
      initial_insert_size = 6;

rewriter/emoticon_rewriter_test.ccに2箇所ほど「かお」の変換をテストしている箇所があるので削除する。

TizenでUUIDを取得する

void
UUIDManager::Get(String &uuid) {
	result r = E_SUCCESS;
	String keyUUID("key_uuid");
	AppRegistry* appRegistry = Application::GetInstance()->GetAppRegistry();
	r = appRegistry->Get(keyUUID, uuid);
	if (r == E_KEY_NOT_FOUND) {
		String u;
		Tizen::Base::UuId* pUUID = Tizen::Base::UuId::GenerateN();
		u.Append(pUUID->ToString().GetPointer());
		appRegistry->Add(keyUUID, u);
		delete pUUID, pUUID = NULL;
	}
	r = appRegistry->Get(keyUUID, uuid);
}

Tizenでデータを永続化する

SQLiteを使うまでのデータでない場合に、SharedPreferencesやNSUserDefaultのようなものが欲しくなる。Tizenでは以下のようにする。

#include <FApp.h>
using namespace Tizen::App;
result r = E_SUCCESS;
String hogeKey("tmp_hoge_key");
String hogeValue("tmp_hoge_value");
String value("");
AppRegistry* appRegistry = Application::GetInstance()->GetAppRegistry();
r = appRegistry->Get(hogeKey, value);
if (r == E_KEY_NOT_FOUND) {
	AppLog("value:%ls", value.GetPointer());
	appRegistry->Add(hogeKey, hogeValue);
}
r = appRegistry->Get(hogeKey, value);
AppLog("value:%ls", value.GetPointer());

以下のようなログが表示される。

virtual result HogeHoge::OnInitializing()(45) > value:
virtual result HogeHoge::OnInitializing()(50) > value:tmp_hoge_value
virtual result HogeHoge::OnInitializing()(50) > value:tmp_hoge_value

TizenアプリでSQLiteを使う

SDKに同梱されているコードがいまいちわかりにくかったので、オフィシャルサイトにあるdocumentationからサンプルコードを拝借する。

■DBManager.cpp

とりあえず上から読んでいってSQLもしくはそれに準ずるものを探す。

result DBManager::CreateTable()

CREATE TABLE文がそのまま見つかったので読んでみる。

String sql;
result r = E_SUCCESS;
sql.Append(L"CREATE TABLE IF NOT EXISTS  member(seq INTEGER PRIMARY KEY,id varchar(20),name varchar(20),age INTEGER)");
r = Execute(sql);
AppLog("create : %s",GetErrorMessage(r));

return r;

SQL文を作ってそのままprivateメソッドに投げてるようだ。

result DBManager::Execute(String sql)

投げられたSQLをそのまま処理しているようだ。

__pDatabase->BeginTransaction();
r = __pDatabase->ExecuteSql(sql, true);
__pDatabase->CommitTransaction();

__pDatabaseを操作している。

result DBManager::Open(String dbName)

__pDatabaseをインスタンス化している。use `dbname`;的なところでDBをopenしているところのようだ。

AppLog("Open");
__dbName = dbName;
result r = E_SUCCESS;
__pDatabase = new Database;
__pDatabase->Construct(__dbName,true);
return r;

bool DBManager::IsMember(String id)

以下のとおり取り出したデータはAndroidのCursorのようになっているようだ。

bool isMember = false;
String sql;
sql.Format(100,L"SELECT id FROM member where id = '%ls'",id.GetPointer());

 // select a query using the Database::QueryN() wrapper AP
DbEnumerator* pEnum = __pDatabase->QueryN(sql);
if (pEnum)
{
	while (pEnum->MoveNext() == E_SUCCESS)
	{
		isMember = true;
	}
	delete pEnum;
}

return isMember;

result DBManager::GetAllMember(ArrayList& list)

もう少しちゃんと取り出しているところを見る。

AppLog("GetAllMember");
result r = E_SUCCESS;

String sql;
sql.Format(50,L"select seq, id, name,age from member");

MEMBERINFO* pInfo = null;
 // select a query using the Database::QueryN() wrapper AP
DbEnumerator* pEnum = __pDatabase->QueryN(sql);
if (pEnum)
{
	while (pEnum->MoveNext() == E_SUCCESS)
	{
		pInfo = new MEMBERINFO();
		r = pEnum->GetIntAt(0, pInfo->seq);
		r = pEnum->GetStringAt(1,pInfo->id);
		r = pEnum->GetStringAt(2,pInfo->name);
		r = pEnum->GetIntAt(3,pInfo->age);


		list.Add(*pInfo);
	}
	delete pEnum;
}

return r;

サンプルコードが簡略化されていて理解しやすい。

■DBManager.h

このクラスはSingletonパターンになっていて以下のメソッドでインスタンスを取得する。

static DBManager* GetDB()
{
	if(__pInstance == null)
	{
		__pInstance = new DBManager();
	}

	return __pInstance;
}

使い終わった時のメソッドも用意されている。

static void ReleaseDB()
{
	if(__pInstance)
	{
		delete __pInstance;
		__pInstance = null;
	}
}

コンストラクタ

DBManager::DBManager()
:__pDatabase(null)
{
	// TODO Auto-generated constructor stub
	Init();
}
void DBManager::Init()
Open(App::GetInstance()->GetAppRootPath() + L"data/mdb.db");
CreateTable();

次の2つのメソッドを呼び出している。

result DBManager::Open(String dbName)

引き数で与えられたデータベースファイルを開く。

AppLog("Open");
__dbName = dbName;
result r = E_SUCCESS;
__pDatabase = new Database;
__pDatabase->Construct(__dbName,true);

return r;
result DBManager::CreateTable()

CREATE TABLE文を実行する。

String sql;
result r = E_SUCCESS;
sql.Append(L"CREATE TABLE IF NOT EXISTS  member(seq INTEGER PRIMARY KEY,id varchar(20),name varchar(20),age INTEGER)");
r = Execute(sql);
AppLog("create : %s",GetErrorMessage(r));

return r;

デストラクタ

DBManager::~DBManager()
{
	Close();
	// TODO Auto-generated destructor stub
}
result DBManager::Close()
AppLog("Close");
result r = E_SUCCESS;
if(__pDatabase)
{
	delete __pDatabase;
	__pDatabase = null;
}

return r;

■Form1.cpp

DBManagerの使い方は以下のようになっている。

GetDB()->GetAllMember(list);
result
Form1::OnTerminating(void)
{
	result r = E_SUCCESS;

	if(__pInfoPopup)
	{
		delete __pInfoPopup;
		__pInfoPopup = null;
	}
	ReleaseDB();

	// TODO: Add your termination code here

	return r;
}

TizenアプリでJSONをパースする

サンプルアプリのJsonParserAppを見る。

■JsonForm.cpp

void JsonForm::ParseAndDisplay(void)

// なるべく簡略化したいのでエラー処理はコメントアウトしておく。

// fileを読み込み専用で開く。
File file;
String filePath = App::GetInstance()->GetAppRootPath() + L"data/jsonSample.json";
result r = file.Construct(filePath, L"r");
#TryReturnVoid(r == E_SUCCESS, "file construction failure with [%s]", GetErrorMessage(r));

FileAttributes att;
r = File::GetAttributes(filePath, att);
#TryReturnVoid(r == E_SUCCESS, "file GetAttributes failure with [%s]", GetErrorMessage(r));
long long size = att.GetFileSize();
#TryReturnVoid(size > 0, "file does not contain valid data, size of file [%ld]", size);
ByteBuffer buf;
r = buf.Construct(size + 1);// fileサイズ+1で初期化
#TryReturnVoid(r == E_SUCCESS, "bytebuffer construct failure with [%s]", GetErrorMessage(r));

r = file.Read(buf);// fileを読み込む
#TryReturnVoid(r == E_SUCCESS, "file read failure with [%s]", GetErrorMessage(r));

// 読み込んだデータをJSONにパース
IJsonValue* pJson = JsonParser::ParseN(buf);
TryCatch(pJson, , "ParseN failed with [%s]", GetErrorMessage(GetLastResult()));

AppLog("Before traverse %d", _pList->GetItemCount());
_pJsonKeyList->RemoveAll(true);
_pValueList->RemoveAll(true);
TraverseFunction(pJson);
AppLog("After traverse");

fileにclose的なものがないけどいいのかな。

void JsonForm::TraverseFunction(IJsonValue* pValue)

パースしたIJsonValueのオブジェクトを実際にどう扱うか見てみる。このメソッドは、pValue->GetType()で分岐してある。typeによっては自身を呼び出し再帰処理している。

FWebJsonIJsonValue.h

jsonの値の型は以下のように定義されている。

enum JsonType
{
	JSON_TYPE_STRING,   /**< The JSON string type*/
	JSON_TYPE_NUMBER,   /**< The JSON number type*/
	JSON_TYPE_OBJECT,   /**< The JSON object type*/
	JSON_TYPE_ARRAY,    /**< The JSON array type*/
	JSON_TYPE_BOOL,     /**< The JSON bool type*/
	JSON_TYPE_NULL      /**< The JSON null type*/
};
nullの時
TryReturnVoid(_pList, "_pList is null");

TryReturnVoid(pValue, "input jsonvalue pointer is null");
オブジェクトの時

Mapにしてkeyとvalueを取り出し、valueを引き数にして再帰呼び出ししている。

case JSON_TYPE_OBJECT:
{
	JsonObject* pObject = static_cast< JsonObject* >(pValue);
	IMapEnumeratorT< const String*, IJsonValue* >* pMapEnum = pObject->GetMapEnumeratorN();
	while (pMapEnum->MoveNext() == E_SUCCESS)
	{
		const String* key = null;
		IJsonValue* value = null;
		pMapEnum->GetKey(key);
		String* pListKey = new (std::nothrow) String(*key);
		_pJsonKeyList->Add(*pListKey);

		String* pStr = null;
		_isArray = 0;
		pMapEnum->GetValue(value);
		if (value->GetType() == JSON_TYPE_OBJECT)
		{
			pStr = new (std::nothrow) String("Value is an Object");
			_pValueList->Add(*pStr);
		}
		else if (value->GetType() == JSON_TYPE_ARRAY)
		{
			pStr = new (std::nothrow) String("Value is an Array");
			_pValueList->Add(*pStr);
		}
		TraverseFunction(value);
	}
	delete pMapEnum;
}
break;
文字列の場合

_pValueListに追加している。

case JSON_TYPE_STRING:
{
	JsonString* pVal = static_cast< JsonString* >(pValue);
	if (_isArray == 0)
	{
		String* pStr = new (std::nothrow) String(*pVal);
		_pValueList->Add(*pStr);
	}
}
break;
配列の場合

keyとvalueを取り出し、valueを引き数にして再帰呼び出ししている。

case JSON_TYPE_ARRAY:
{
	JsonArray* pJsonArray = static_cast< JsonArray* >(pValue);
	pJsonArray->GetCount();
	IEnumeratorT< IJsonValue* >* pEnum = pJsonArray->GetEnumeratorN();
	while (pEnum->MoveNext() == E_SUCCESS)
	{
		IJsonValue* pValue = null;
		pEnum->GetCurrent(pValue);
		if ((pValue->GetType() == JSON_TYPE_STRING) || (pValue->GetType() == JSON_TYPE_NUMBER) || (pValue->GetType() == JSON_TYPE_BOOL) || (pValue->GetType() == JSON_TYPE_NULL))
			_isArray = 1;
		TraverseFunction(pValue);
	}
	delete pEnum;
}
break;
数値の場合

JsonNumberをtoStringして文字列にし、_pValueListにaddしている。

case JSON_TYPE_NUMBER:
{
	JsonNumber* pVal = static_cast< JsonNumber* >(pValue);
	String* pStr = new (std::nothrow) String((pVal->ToString()));
	if (_isArray == 0)
		_pValueList->Add(*pStr);
}
break;
真偽値の場合

条件分岐して、文字列を_pValueListにaddするようにしている。

case JSON_TYPE_BOOL:
{
	String* pStr = null;
	JsonBool* pVal = static_cast< JsonBool* >(pValue);
	if (*pVal == true)
		pStr = new (std::nothrow) String("true");
	else
		pStr = new (std::nothrow) String("false");
	if (_isArray == 0)
		_pValueList->Add(*pStr);
}
break;
nullの場合

nullという文字列を_pValueListにaddするようにしている。

case JSON_TYPE_NULL:
{
	String* pStr = null;
	pStr = new (std::nothrow) String("null");
	if (_isArray == 0)
		_pValueList->Add(*pStr);
}
break;

読むのが面倒だったがC++の基本が分かる良いコードだった。

TizenアプリでHTTP通信する

サンプルアプリのHttpClientを見る。

■MainForm.cpp

MainFormはTizen::Net::Http::IHttpTransactionEventListenerを継承している。

result MainForm::RequestHttpGet(void)

エラー処理を省くと以下の通りとなる。

HttpTransaction* pHttpTransaction = null;
HttpRequest* pHttpRequest = null;

pHttpTransaction = __pHttpSession->OpenTransactionN();
r = pHttpTransaction->AddHttpTransactionListener(*this);

pHttpRequest = const_cast< HttpRequest* >(pHttpTransaction->GetRequest());
r = pHttpRequest->SetUri(HTTP_CLIENT_REQUEST_URI);
r = pHttpRequest->SetMethod(NET_HTTP_METHOD_GET);

r = pHttpTransaction->Submit();

AddHttpTransactionListenerの引き数はIHttpTransactionEventListenerを継承したクラスでサンプルアプリでは自身である。

void MainForm::OnTransactionReadyToRead(HttpSession& httpSession, HttpTransaction& httpTransaction, int availableBodyLen)

通信結果を受け取る。

HttpResponse* pHttpResponse = httpTransaction.GetResponse();
if (pHttpResponse->GetHttpStatusCode() == HTTP_STATUS_OK)
{
	HttpHeader* pHttpHeader = pHttpResponse->GetHeader();
	if (pHttpHeader != null)
	{
		String* tempHeaderString = pHttpHeader->GetRawHeaderN();
		ByteBuffer* pBuffer = pHttpResponse->ReadBodyN();

		String text(L"Read Body Length: ");
		text.Append(availableBodyLen);

		__pEditArea->SetText(text);
		Draw();

		delete tempHeaderString;
		delete pBuffer;
	}
}

IHttpTransactionEventListenerを継承した場合は以下のメソッドの実装が必要になるっぽい。

  • void MainForm::OnTransactionReadyToRead(HttpSession& httpSession, HttpTransaction& httpTransaction, int availableBodyLen)
  • void MainForm::OnTransactionAborted(HttpSession& httpSession, HttpTransaction& httpTransaction, result r)
  • void MainForm::OnTransactionReadyToWrite(HttpSession& httpSession, HttpTransaction& httpTransaction, int recommendedChunkSize)
  • void MainForm::OnTransactionHeaderCompleted(HttpSession& httpSession, HttpTransaction& httpTransaction, int headerLen, bool authRequired)
  • void MainForm::OnTransactionCompleted(HttpSession& httpSession, HttpTransaction& httpTransaction)
  • void MainForm::OnTransactionCertVerificationRequiredN(HttpSession& httpSession, HttpTransaction& httpTransaction, Tizen::Base::String* pCert)

iOSでOpenSSLを使って大きな数値計算をする

桁あふれしそうな数の計算をする。

■OpenSSLをiOS用にビルド

git clone https://github.com/x2on/OpenSSL-for-iPhone.git
cd OpenSSL-for-iPhone/
./build-libssl.sh

ビルドされたライブラリとヘッダーファイルをプロジェクトに追加する。

mkdir ../MyProject/Library/OpenSSL
cp -r include/ ../MyProject/Library/OpenSSL/include
cp -r lib/ ../MyProject/Library/OpenSSL/lib

Build Settings

Header Search Path

以下を追加する。

$(SRCROOT)/MyProject/Library/OpenSSL/include
Library Search Path

以下を追加する。

"$(SRCROOT)/MyProject/Library/OpenSSL/lib"

Build Phases

Link Binary With Libraries

libcrypto.aとlibssl.aを追加する。

■実装

#import <openssl/bn.h>

unsigned int rand = arc4random() % 100000000;
BIGNUM *random = BN_new();BN_set_word(random, rand);
BIGNUM *base = BN_new();BN_set_word(base, BASE);
BIGNUM *prime = BN_new();BN_set_word(prime, PRIME);
BIGNUM *remainder = BN_new();
BN_CTX *ctx;
ctx = BN_CTX_new();
BN_mod_exp(remainder, base, random, prime, ctx);
BN_CTX_free(ctx);

iOSでPythonが動けばいいのに。

参考

mozc for Androidにトグルが一定時間で確定される機能を追加する

mozc本体のコメントに将来的にそういった機能をつけるような事が書かれていて、その時コンフリクトする事が予想されるので個人的にはやりたくなかったのだがしょうがない。

■クライアント側

以下のように定義し、ViewManager.javaでタイマーでコマンドが実行されるようにする。

ViewManager.java

  private final int keycodeKaomojiToggleCommit;
    keycodeKaomojiToggleCommit = res.getInteger(R.integer.key_kaomoji_toggle_commit);

とりあえず定義だけ上述のようにする。タイマー部分はよきに計らってやる。

KeycodeConverter.java

  public static final ProtoCommands.KeyEvent KAOMOJI_TOGGLE_COMMIT =
      ProtoCommands.KeyEvent.newBuilder().setSpecialKey(SpecialKey.KAOMOJI_TOGGLE_COMMIT).build();

keycode.xml

  <integer name="key_kaomoji_toggle_commit">-10025</integer>

ProtoCommands.java

サーバー側をビルドすると生成される。

■サーバー側

composer/composer.cc

実装しようとしている機能はカーソルを右に動かす動作と非常に似ている。そしてカーソルを動かす処理はこのファイルの以下のメソッドである。

void Composer::MoveCursorRight() {
  if (position_ < composition_->GetLength()) {
    ++position_;
  }
  UpdateInputMode();

  typing_corrector_.Invalidate();
}

そこでpositionを移動させない関数を追加する。引き数で条件分岐してもいいのだが右に移動させる機能とトグルをコミットさせる機能は本質的に別物であるので別にする。コンフリクトすると面倒なだけでもある。

void Composer::KaomojiToggleCommit() {
  UpdateInputMode();
  typing_corrector_.Invalidate();
}

composer/composer.h

  void KaomojiToggleCommit();

次に上述を呼び出す処理を実装する。

session/session.h

  bool KaomojiToggleCommit(mozc::commands::Command *command);

右に移動させるメソッドがpublicになっていたのでpublicに書いてしまったがprivateでも問題無さそうである。

session/session.cc

bool Session::KaomojiToggleCommit(commands::Command *command) {
  if (context_->GetRequest().crossing_edge_behavior() == commands::Request::COMMIT_WITHOUT_CONSUMING && context_->composer().GetLength() == context_->composer().GetCursor()) {
    Commit(command);

    // Do not consume.
    command->mutable_output()->set_consumed(false);
    return true;
  }

  command->mutable_output()->set_consumed(true);
  if (CommitIfPassword(command)) {
    return true;
  }
  context_->mutable_composer()->KaomojiToggleCommit();
  if (Suggest(command->input())) {
    Output(command);
    return true;
  }
  OutputComposition(command);
  return true;
}

コピペで申し訳ない限りである。このメソッドは同ファイルの以下の部分から呼び出される。

    case keymap::CompositionState::MOVE_CURSOR_RIGHT:
      return MoveCursorRight(command);

    case keymap::CompositionState::KAOMOJI_TOGGLE_COMMIT:
      return KaomojiToggleCommit(command);

    case keymap::CompositionState::MOVE_CURSOR_TO_BEGINNING:
      return MoveCursorToBeginning(command);

定義したメソッドがクライアントからのコマンドで呼び出されるようにする。

session/commands.proto

下のように記述するとビルドした時にKAOMOJI_TOGGLE_COMMITが追加されたProtoCommands.javaが生成される。

COMMA = 70;  // Numpad [,]
KAOMOJI_TOGGLE_COMMIT = 71;
NUM_SPECIALKEYS = 72;

data/keymap/mobile.tsv

Composition	Right	MoveCursorRight
Composition	KaomojiToggleCommit	KaomojiToggleCommit
Composition	Space	Convert

vimで編集する時にtabキーでスペースが入るようにしておくと正しくパースできないので注意が必要。

session/internal/keymap.cc

  RegisterCompositionCommand("KaomojiToggleCommit",
                             CompositionState::KAOMOJI_TOGGLE_COMMIT);

session/internal/keymap_interface.h

  MOVE_CURSOR_RIGHT,
  KAOMOJI_TOGGLE_COMMIT,
  MOVE_CURSOR_TO_BEGINNING,

session/key_parser.cc

    keycode_map_["right"] = KeyEvent::RIGHT;
    keycode_map_["kaomojitogglecommit"] = KeyEvent::KAOMOJI_TOGGLE_COMMIT;
    keycode_map_["enter"] = KeyEvent::ENTER;

session/random_keyevents_generator.cc

  commands::KeyEvent::RIGHT,
  commands::KeyEvent::KAOMOJI_TOGGLE_COMMIT,
  commands::KeyEvent::UP,

■ダウンロード

みんなの顔文字キーボード

Tizenでクリップボードを使う

現状ではJavaScriptから操作する事ができない。

String *pText = static_cast<String *>(pMessage->GetValue(String(L"text")));
ClipboardItem item;
item.Construct(CLIPBOARD_DATA_TYPE_TEXT, pText);
Clipboard *pClipboard = Clipboard::GetInstance();
pClipboard->CopyItem(item);

今月は記事を一つも書いてないので間に合わせ。