2011年12月31日土曜日

Blogger にFacebookの「いいね」ボタンをつける

ここのブログの記事の下にあるようなFacebookへの「いいね!」ボタンをつけるにはFacebook Like Button For Bloggerで解説されている手順でつけることができる。

やり方の概要は上記ページに描いてありますが以下のとおりです。
テンプレートのHTMLを直接変更するので壊さないように、元のHTMLをいったんテキストエディタなどに退避して保存しておいたほうが安全だと思います。


  1. 上記のページを開きページ中ほどのHTMLコードをコピーする。

  2. Blogger管理ページの「デザイン」タブを選択

  3. 「HTMLの編集」を選択する

  4. 「ウィジェットのテンプレートを展開」にチェックを入れる

  5. HTMLコードの中に>data:post.body/<という部分があるので、その下にコピーしたHTMLコードを貼り付け

  6. HTMLコードの先頭部分のhtmlタグの中に属性 xmlns:fb="https://www.facebook.com/2008/fbml"を追加する

  7. 「テンプレートを保存」を押す



これで、各記事の下にLike!Sendの2つのボタンがつくようになる。

ボタンのラベルを日本語のいいねにするには、コピーしたコード中のen_USja_JPにかえる。

この行を…

  1. js.src = "//connect.facebook.net/en_US/all.js#xfbml=1";  


こうします。

  1. js.src = "//connect.facebook.net/ja_JP/all.js#xfbml=1";  


ひとつ困ったことは、FacebookとBloggerの相性が悪いのか本文が正しく展開されず投稿の本文にjavascriptコードが表示されてしまう。これは何とかならないだろうか…

2011年12月16日金曜日

SQL Anywhere データベースをWindowsサービスにする

SQL Anywhere をWindowsサービスとして登録するにはdbsvcコマンドを使う。

このコマンドにはいろいろオプションがあるが、自分がよく使うのは次のようなやり方である。
dbsvc -as -s auto -w {サービス名} {実行するコマンド} {コマンド引数}...


引数に設定する値の意味は次のとおり。

-as
サービスをWindowsのローカル管理者アカウントで動かす
-s auto
スタートアップの種類を自動にする
-w
登録するサービスの名前(表示名は「SQL Anywhere - 名前」になる)、実行するコマンドとその引数を記述する


たとえば以下のように設定すると、サービス "SQL Anywhere - mydb"を自動起動で作成する。データベースは64bit版のdbsrv12.exeで起動する。
dbsvc -as -s auto -w mydb "%SQLANY12%\Bin64\dbsrv12.exe" "C:\Temp\mydb.db" -x tcpip(port=5440;DoBroadcast=No) -ti 0 -n mydb


実行するコマンドやデータベースファイルのパスは必ず絶対パスで書くこと。実行コマンド以降はすべてコマンドの引数である。この部分はdbsrv12.exeを普通に実行するときと同じ。

最後に忘れてはいけないこと・・・
登録しただけでは動かないので、サービスの管理画面や net start コマンドを使って起動してください。

2011年11月3日木曜日

SQL Anywhere 行の挿入と更新を1ステートメントで

行の挿入と更新を1ステートメントでできるとトランザクションを考える必要がなく便利な場合が多い。
SQL Anywhereの場合はINSERT INTO ~ ON EXISTING UPDATE という句を使うとプライマリーキーを比較して挿入と更新を自動で切り替えて実施してくれる。


例として、前記事と同じこのようなテーブルに対して
  1. CREATE TABLE mytable (  
  2.     pkey      varchar(10) NOT NULL,  
  3.     value     varchar(20),  
  4.     created   datetime NOT NULL DEFAULT current timestamp,  
  5.     modified  datetime NOT NULL DEFAULT timestamp,  
  6.     PRIMARY KEY (pkey)  
  7. )  


1回目の実行結果
  1. INSERT INTO mytable (pkey,value)  
  2. ON EXISTING UPDATE  
  3. VALUES('1''abc');  

INSERTとして実行されます。



pkeyvaluecreatedmodified
1abc2011-11-02 23:44:39.1792011-11-02 23:44:39.179


2回目の実行結果
  1. INSERT INTO mytable (pkey,value)  
  2. ON EXISTING UPDATE  
  3. VALUES('1''xyz');  

プライマリーキーであるpkey=1に対するUPDATEとして実行されます。



pkeyvaluecreatedmodified
1xyz2011-11-02 23:44:39.1792011-11-03 00:06:21.069



他のRDBMSではMERGEやREPLACEというのが似たような動きをしてくれるようである。

2011年11月2日水曜日

SQL Anywhere 行の作成日と更新日を自動でつける

CREATE TABLE文のカラムにDEFAULT句をつけると、そのカラムの値が省略された場合のデフォルト値を自動で入れることができる。SQL Anywhereの場合はこのDEFAULT句を使って行の作成日や更新日を自動でつけることができる。


current timestamp

行が挿入された時間

timestamp

行が挿入または更新された時間



例としてこのような定義のテーブルがあった場合・・・
  1. CREATE TABLE mytable (  
  2.     pkey      varchar(10) NOT NULL,  
  3.     value     varchar(20),  
  4.     created   datetime NOT NULL DEFAULT current timestamp,  
  5.     modified  datetime NOT NULL DEFAULT timestamp,  
  6.     PRIMARY KEY (pkey)  
  7. )  

行の追加結果
  1. INSERT INTO mytable (pkey,value) VALUES('1''abc');  




pkeyvaluecreatedmodified
1abc2011-11-02 23:44:39.1792011-11-02 23:44:39.179

行の更新結果
  1. UPDATE mytable SET value='xyz' WHERE pkey='1';  




pkeyvaluecreatedmodified
1xyz2011-11-02 23:44:39.1792011-11-02 23:45:32.928

こんな感じで、modifiedのほうはUPDATE実行時間に変わりますが、createdのほうはINSERT実行時間のまま変わりません。

2011年10月25日火曜日

SQL Anywhere ランダムな結果を取得

SQL Anywhereで複数の行からランダムに1件選びたい場合はtop句とrand関数を使う。

たとえばテーブルmytableのランダムな1行を返す場合は次のようなselect文を書けばいい。
  1. SELECT TOP 1 * FROM mytable  
  2. ORDER BY RAND()  


rand関数はSQL2003 Vendor extension。他のDBでは使えるものとダメなものがある。

MySQLとSQLServerはrand関数だが、PostgreSQL はrandom関数。
Oracleはまた違うようである。(dbms_random.random関数?)
SQL Anywhere以外は未検証です。

コマンドプロンプト派のためのPowerShell - ファイル削除

PowerShellでのファイル削除はコマンドプロンプトと同じdelコマンド。
オプションが違う。

よく使う「強制削除」del /f に相当するのは -Force オプションを使う。

del {ファイル} -Force

2011年1月31日月曜日

Java JDBC-ODBC 文字列またはバッファの長さが無効です

JavaアプリからJDBC-ODBCブリッジでデータベースSQL Anywhereに接続していると、不定期に「文字列またはバッファの長さが無効です」というエラーが出る現象に遭遇した。

DriverManager.getConnection()で出てみたり・・・

java.sql.SQLException: [Microsoft][ODBC Driver Manager] 文字列またはバッファの長さが無効です。
at sun.jdbc.odbc.JdbcOdbc.createSQLException(JdbcOdbc.java:6957)
at sun.jdbc.odbc.JdbcOdbc.standardError(JdbcOdbc.java:7114)
at sun.jdbc.odbc.JdbcOdbc.SQLGetDataString(JdbcOdbc.java:3907)
at sun.jdbc.odbc.JdbcOdbcResultSet.getDataString(JdbcOdbcResultSet.java:5698)
at sun.jdbc.odbc.JdbcOdbcResultSet.getString(JdbcOdbcResultSet.java:354)
at sun.jdbc.odbc.JdbcOdbcConnection.buildTypeInfo(JdbcOdbcConnection.java:1503)
at sun.jdbc.odbc.JdbcOdbcConnection.initialize(JdbcOdbcConnection.java:381)
at sun.jdbc.odbc.JdbcOdbcDriver.connect(JdbcOdbcDriver.java:174)
at java.sql.DriverManager.getConnection(DriverManager.java:582)
at java.sql.DriverManager.getConnection(DriverManager.java:185)


ResultSet.getString()で出てみたりと発生箇所もさまざま。

java.sql.SQLException: [Microsoft][ODBC Driver Manager] 文字列またはバッファの長さが無効です。
at sun.jdbc.odbc.JdbcOdbc.createSQLException(JdbcOdbc.java:6957)
at sun.jdbc.odbc.JdbcOdbc.standardError(JdbcOdbc.java:7114)
at sun.jdbc.odbc.JdbcOdbc.SQLGetDataString(JdbcOdbc.java:3907)
at sun.jdbc.odbc.JdbcOdbcResultSet.getDataString(JdbcOdbcResultSet.java:5698)
at sun.jdbc.odbc.JdbcOdbcResultSet.getString(JdbcOdbcResultSet.java:354)
at sun.jdbc.odbc.JdbcOdbcResultSet.getString(JdbcOdbcResultSet.java:411)


調べてみるとある特定の環境だけで頻発するようだ。

英語のエラーメッセージ "Invalid string or buffer length" で検索すると、SQL ServerOracleでも発生している様子。これらは不定期でなくコードの決まった場所で必ず発生するとのことだが、共通するのはx64版のJavaVMを使っていると言うことである。

今エラーが起きているのもx64 JavaVMなので何か関係がありそうである。

JDBC-ODBCをやめてType4ドライバを使うようにしたほうがよさそうだ。

2011年1月19日水曜日

InstallAnywhere VM packの追加方法

インストーラーを作るときに普段使っているのはInstallAnywhereという製品である。

これは、Javaアプリのインストーラーを作成するためのツールであり、複数のプラットフォーム向けのインストーラーを一度に作成できるという特徴がある。以前 Install Shield Multi Platformという同じ用途のツールがあったがその後継製品となっているものである。

さて、InstallAnywhereで複数のプラットフォームのインストーラーを作るには、それぞれのプラットフォーム向けのVM Packというものを入手して設定する必要がある。このツールは取り扱う会社がころころ変わっているおかげでVM Packの入手先がわかりにくく、設定方法でも少しはまったので、ここにメモしておく。

  1. InstallAnywhere: Files & Utilitiesのページを開き、"VM Pack"と書いてあるところを選択。



  2. 下のほうに行くとVM Packの一覧があるので、必要なプラットフォームとJavaのバージョンを選んでダウンロードする。ダウンロードするファイルの拡張子はvmであるが、IEでダウンロードするとなぜかzipになってしまう。*.zip を *.vm に変更して保存する。



  3. ダウンロードしたファイルを、{installer-dir}\resource\installer_vms フォルダに入れる。


自分が使っているのはInstallAnywhere 2009であるが上記ページには特にどのバージョン向けという記述は無いので、InstallAnywhereのシリーズであれば同じVM packが使えるのだと思う。

それにしても、このツールはドキュメントが不親切であるし使いにくい・・・

2011年1月18日火曜日

Android アプリからツイートする(非正攻法)

AndroidのアプリからTwitterに投稿するには正攻法としてはTwitter APIを使うのがよいであろう。

しかし、OAuthなどが必要だったり敷居が高く感じる人も多いであろう。

端末にTwitter公式アプリが入っているという前提があるなら、Intentを使うことで投稿準備ができた状態でTwitterアプリを起動し最後に投稿ボタンを押してもらうだけでツイートできるようになる。
  1. Intent intent = new Intent(Intent.ACTION_SEND);  
  2. intent.setClassName("com.twitter.android""com.twitter.android.PostActivity");  
  3. intent.setType("text/plain");  
  4. intent.putExtra(Intent.EXTRA_TEXT, "Androidから投稿なう");    // 投稿するメッセージ  
  5. try {  
  6.     startActivity(intent);  
  7. }  
  8. catch(ActivityNotFoundException ex) {  
  9.     // Twitter公式アプリが無いときのエラー処理  
  10. }  


ただし公式アプリのクラスを直接呼び出しているので、将来アプリがバージョンアップされた場合には動かなくなる可能性があることに注意。

とりあえず Twitter公式アプリVersion1.0.5で動作を確認した。他のバージョンでの動作報告をコメントしてもらえるとありがたいです。

Android アプリからSMSを送る

Androidのアプリ中からSMSを送るにはIntentを使う。ネットで調べるとそのパラメータの設定についていろいろなやり方があるようだがOSバージョンや搭載しているSMSアプリの種類によって挙動が変わるのかGalaxyTabでうまくいくパターンがなかった。

試行錯誤の結果、以下のような方法で宛先と本文を埋めた状態で標準SMSアプリが起動することがわかった。あとはユーザーに送信ボタンを押してもらえばSMSが発信される。
  1. Uri uri = Uri.parse("smsto://");  
  2. Intent intent = new Intent(Intent.ACTION_VIEW, uri);  
  3. intent.setType("vnd.android-dir/mms-sms");  
  4. intent.putExtra("address""090xxxxxxxx");    // 電話番号を入れる  
  5. intent.putExtra("sms_body""こんにちは");    // 送信メッセージを入れる  
  6. try {  
  7.    startActivity(intent);  
  8. }  
  9. catch(ActivityNotFoundException ex) {  
  10.    // SMSアプリが無いときのエラー処理  
  11. }  

2011年1月13日木曜日

SQL Anywhere 時間差を求める

SQL Anywhereで2つの時刻の差が何秒あるかを求めるにはdatediff関数を使う。

この関数の書式は以下のようになっており、date-partには求めたい差の単位day,hour,secondなど、date-expression-1,2には差を求めたい2つの時刻を入れる。date-expression-1のほうに古いほうの時刻があると関数の結果は正となり、新しいと負になる。

  1. DATEDIFF( date-part, date-expression-1, date-expression-2 )  


たとえば、以下のようなスキーマのテーブルがあり、upd_timeに各行の更新時刻が入っているような場合、
  1. CREATE TABLE mytable (  
  2.     r_id integer NOT NULL  
  3.    ,r_data varchar(100) NOT NULL  
  4.    ,upd_time timestamp NOT NULL  
  5. );  


このテーブルから、1時間以内に更新された行を抽出するSQLは次のように書ける。
条件句にDATEDIFF関数を使って現在時刻NOW()関数とupd_time列の差を秒単位で求め、3600秒以内である行のみを抽出する。
  1. SELECT r_id,r_data,upd_time  
  2. FROM mytable   
  3. WHERE DATEDIFF(second,upd_time,NOW()) <= 3600  
  4. ORDER BY start_time  


この関数はSQL2003標準であるため、サポートしている別のDB、たとえばSQL Server 2008などでも同じように使えるようだ。

Android 文字列リソースのエラー

EclipseのAndroid Development Toolkitをアップデートしたときにはまったときのメモ

アップデート前まで正常だったプロジェクトで、strings.xmlがこのようなエラーをはくようになりビルドできなくなってしまった。


W/ResourceType(15164): Bad XML block: header size 276 or total size 18088132 is larger than data size 0
...\res\values\strings.xml:13: error: Multiple substitutions specified in non-positional format; did you mean to add the formatted="false" attribute?
...\res\values\strings.xml:13: error: Unexpected end tag string



プロジェクトが壊れてしまったのかと思いあわてたが、ここの情報によると、書式文字列のチェックが厳しくなったようで、%sなどの置換部分が2個以上ある場合はそれぞれに位置を指定する必要があるらしい。

たとえば、今までこうしていたのは、
  1. <string name="message">製品名=%s %d円です</string>  


このように書き換える必要がある。
  1. <string name="message">製品名=%1$s %2$d円です</string>  


おまけ

アップデート直後にEclipseを起動すると "sdk platform tools component is missing" というエラーが出るかもしれない。その場合は、メニューから Android SDK and AVD Managerを起動してインストールされているパッケージすべてをUpdate Allしてやると復旧するようです。

2011年1月6日木曜日

SQL Anywhere データベースをコピーする

稼働中のデータベースファイルをとめないでコピーするというのはメンテナンス作業の中でも頻度が高いであろう。こんなときSQL Anywhereではdbbackupコマンドを使う。

一般的には以下の使い方が多いだろう。接続文字列にDBへの接続方法、保存先フォルダにはデータベースファイル(とログなどの付属ファイル)を保存する先を指定する。
dbbackup -c {接続文字列} {保存先フォルダ}

たとえば、以下のようにすると ODBCデータソース "mydb" で接続できるデータベースをmybackupフォルダにコピーする。
dbbackup -c "dsn=mydb" mybackup

実行結果は以下のようになる。

途中フォルダが無いよと言ってくるので y を入力してフォルダを作成する。

C:\temp>dbbackup -c "dsn=mydb" mybackup
SQL Anywhere バックアップ・ユーティリティ バージョン 11.0.1.2044
ディレクトリ "mybackup" は存在しません。作成しますか ? (Y/N) y
(6195/6193 ページ、100% 完了)
データベースのバックアップが完了しました。

C:\temp>dir mybackup
ドライブ C のボリューム ラベルがありません。
ボリューム シリアル番号は 1454-FF9A です

C:\temp\mybackupのディレクトリ

2011/01/06 15:41 <DIR> .
2011/01/06 15:41 <DIR> ..
2010/12/29 16:49 13,320,192 mydb.db
2010/12/30 15:48 12,050,432 mydb.log
2 個のファイル 25,370,624 バイト
2 個のディレクトリ 27,779,186,688 バイトの空き領域


dbbackupを実行する前には%SQLANY11%\Bin32などSQL Anywhereのコマンドがある場所にパスが通っているのを忘れないように

xcopyでフォルダごとコピー

コマンドラインでのフォルダコピーにはWindows VISTA以降であればrobocopyを使うのが便利だが、XP以前ではxcopyしか標準では使えないので、まだまだ使う機会が多い。

やりたいこと
フォルダ srcdir の内容全部を、フォルダ destdir にコピーする。
フォルダ destdir はまだ存在しない。

コマンド
xcopy /i /s /e /h srcdir destdir


ここで指定したオプションは以下のとおり、xcopy /? で出力される説明だとわかりにくいがおおむね以下のとおりである。

  • /i : コピー先フォルダがなければ作る
  • /s,/e : セットで使うとフォルダツリー全体をコピーする
  • /h : 隠しファイル(.svnなど)もコピーする



自分が使うパターンはこれしか無いのだが、つけるオプションを忘れがちなのでここにメモ。


おまけ robocopy編
最初にも書いたが、Windows VISTA以降であればrobocopyを使うのが便利だと思う。個人的には次の2つのパターンをよく使う。どちらもsrcdirからdestdirにフォルダを丸ごとコピーする。オプションはxcopyと似ているが若干異なる。

(1)destdirに重ね書きする
srcdirにあってdestdirにもあるファイルやフォルダは上書き、srcdirに無くてdestdirにあるものは残る。
robocopy /s /e srcdir destdir


(2)srcdirとdestdirを同期
destdirの内容をsrcdirと同一にする。srcdirにあってdestdirにもあるファイルやフォルダは上書き、srcdirに無くてdestdirにあるものは消す。rmdirとxcopyを使えば同等のことはできるが、robocopyは差分でコピーするので速い。
robocopy /mir srcdir destdir