まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

2016年05月

Java で(JDBCで)MySQL データベースに日本語文字を書き込んだ時に文字化けする、という問題に遭遇した時の対処方法を紹介します。

まず、MySQL データベースを自分で用意する場合にはあまり起こらないことだと思っています。起こらないというか、起こっても自分で MySQL サーバーやクライアントの文字コード設定を変えれば対処できることだと思っています。

ところが最近は PaaS が流行っていることもあり、自分でデータベースサーバーを構築するのではなく、「用意されたデータベースのインスタンスを作って使う」という使い方が可能になり、用意された(自分で設定したわけではない)データベースを使うだけ、という手法も多く使われはじめています(IBM Bluemix とか、AWS の RDS とか、・・)。これはこれで簡単で便利なのですが、いざ問題が起こった場合にサーバーの管理者ではないため、設定を変更できる部分とできない部分があったり、そもそもどういう設定で動いているのかを確認できたりできなかったりします。そしてその結果として、今回のような文字化けの問題が発生したりします(デフォルトのデータベース文字コードが分からない、当然のように UTF-8 だと思って UTF-8 で書き込んだら文字化けした、というパターン)。

これを回避するにはデータベースの設定がどのようになっていたとしても UTF-8 前提で接続して、UTF-8 を明示して書き込む、という実装が必要になるわけです。


これが Java(JDBC) と MySQL で問題になったりします(同じ MySQL でも PHP では特に意識せずに文字化けも回避して使えていたが、同じ処理を Java で行うと文字化けする、といったことが起こります)。コードのテキストそのものが UTF-8 で記述されていても起こります。要は上記で書いたような部分が自動判別では実現できておらず、コードとして実装する必要があるからです。

Java(JDBC) で MySQL に対して UTF-8 を明示して接続するには、このように記述する必要があります:
    :
    :
  Connection conn = null;

  String dburi = "mysql://mysql.test.com/mydb?useUnicode=true&characterEncoding=utf8";

  try{
    Class.forName( "com.mysql.jdbc.Driver" );
    conn = DriverManager.getConnection( "jdbc:" + dburi, mysql_username, mysql_password );
  }catch( Exception e ){
    e.printStackTrace();
  }

    :
    :

上記青字部分が指定している箇所です。useUnicode でユニコード指定を名言し、characterEncoding でそのエンコードを utf8 に指定しています。

この方法でコネクションを取得すると、読み書き時の文字化けを回避できました。まあ全ての文字化けのケースにこの方法が有効とはいえませんが、原因不明の文字化けが発生した際の回避方法の1つとして。


Ubuntu で LXC を使ってみました。

LXC は "LinuX Containers" の略で、Linux の仮想化技術の1つです。1つのセンターホストの上で複数の Linux システム(コンテナ)を走らせる、というものです。コンテナなので、いわゆる VM(Virtual Machines) とは異なり、個別のプロセスとネットワーク空間を作り出す仮想環境です。なお Ubuntu 14.04 の 64bit 版を前提として以下を記載します(いつもの CentOS6 環境だとなぜか上手くいきませんでした):

導入は簡単。apt-get コマンド一発で LXC を導入します:
$ sudo apt-get install lxc

導入できたら中身を一度確認してみます。まず用意されているテンプレートの一覧が /usr/share/lxc/templates/ にあるのでその一覧を確認します(青字が出力結果、これだけのテンプレートが用意されています):
$ ls /usr/share/lxc/templates/
lxc-alpine     lxc-centos    lxc-fedora        lxc-oracle  lxc-ubuntu-cloud
lxc-altlinux   lxc-cirros    lxc-gentoo        lxc-plamo
lxc-archlinux  lxc-debian    lxc-openmandriva  lxc-sshd
lxc-busybox    lxc-download  lxc-opensuse      lxc-ubuntu

では今回は Plamo テンプレート(lxc-plamo)を使って、p01 という名称で Plamo Linux コンテナを作成してみます:
$ sudo lxc-create -t plamo -n p01
  :
  :
  :
morse-2.1-x86_64-P1 のインストール中
PACKAGE DESCRIPTION:

qrq-0.1.4-x86_64-P1 のインストール中
PACKAGE DESCRIPTION:

Copy /var/cache/lxc/rootfs-plamo-5.x-x86_64 to /var/lib/lxc/p01/rootfs...
Copying /var/cache/lxc/rootfs-plamo-5.x-x86_64 to /var/lib/lxc/p01/rootfs...
patching file /var/lib/lxc/p01/rootfs/etc/inittab
Setting root password to 'root'...
Please change root password!

コマンドが完了するまでしばらくかかりますが、無事に完了したようです(root ユーザーのパスワードが root で、すぐに変更しろ、というメッセージが表示されています)。 作成されたコンテナのルートディレクトリは /var/lib/lxc/p01/rootfs/ 以下に作成されています(p01 はコンテナ名):
$ ls /var/lib/lxc/p01/rootfs/
bin   cdrom  etc           home  lib64  mnt   root  sbin  tmp  var
boot  dev    etc-incoming  lib   media  proc  run   sys   usr

ではこの Plamo Linux のコンテナを実際に起動してみましょう。名前を指定して、デーモンモードで起動して、コンソールに接続します:
$ sudo lxc-start -n p01 -d
$ sudo lxc-console -n p01

Connected to tty 1
Type  to exit the console,  to enter Ctrl+a itself


Welcome to Linux 3.13.0-85-generic.

p01 login:
 

接続できました!ここで root ユーザーで(パスワード root で)ログインし、とりあえずはパスワードを変更しておきます:
p01 login: root (ユーザー名は root)
Password: (パスワードは root)
root@p01:~# passwd
Enter new UNIX password: (新パスワードを入力)
Retype new UNIX password: (同じ新パスワードを入力)
passwd: password updated successfully

で、ここからはコンテナ上に展開された Plamo Linux を1サーバーのように使うことができるようになります:
root@p01:~# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/dm-1       913G   16G  851G   2% /
none            100K     0  100K   0% /dev
/media          5.8G     0  5.8G   0% /media
/tmp            5.8G     0  5.8G   0% /tmp

元の(Ubuntu の)コンソールに戻るには Ctrl+A を押し、続けて Q を押します:
root@p01:~# exit (Plamo Linux から普通にログアウト)
logout


Welcome to Linux 3.13.0-85-generic.

p01 login: (ここで Ctrl+A,Q)
$ (Ubuntu のコマンドプロンプトへ戻った)





8.9 インチという、超小型ノート PC 「東芝ダイナブック N29」を一か月ほど使ってみました(一週間の出張含む)。勘所というか、自分の利用スタイルに合わせた環境構築もできてきたので、その報告も兼ねてまとめてみました。

なお、利用開始から一週間の時点でも同様のブログエントリを書いています。その時の様子はこちらを参照してください:
ダイナブック N29 利用感想など

また、今回のブログエントリはすべて N29 のキーボードを使って編集しています。以下にも書きますが、打ちやすさは「まあまあ」です。



【主な利用用途】
端的な言い方をすると、マシンスペック的には貧弱な部類だと思っています。もともとメインマシンとして購入したつもりはありませんでした。ウェブメールを含めたウェブブラウズと、SSH などを使ったサーバー管理。加えてプログラミング環境としてどの程度使えるかなあ、という好奇心はありましたが、あまり期待していませんでした。

【導入したアプリ】
オープンソース製品というか、いわゆるフリーソフトを使ってウェブ参照、サーバー管理およびプログラミング用に以下のアプリを追加導入しています。なお全て Windows 32bit 版です。また Microsoft Office 環境は標準の Office Mobile をそのまま使っています(というか、そもそもこのマシンではあまり使っていません):

ウェブ参照用
- Google Chrome + 各種プラグイン

サーバー管理用
- OpenVPN クライアント
- TeraTerm (SSH)
- WinSCP (SCP)
- VNCViewer (VNC)

プログラミング環境用
- サクラエディタ (テキストエディタ)
- JDK 1.8 および JRE 1.8 (Java)
- Eclipse + SVN プラグイン + Jetty サーバープラグイン (統合開発環境)
- cf command line tool

その他
- 7zip
- Paint.NET
- Jane Style(2ちゃんねるビューワ)


【感想】
上記すべてインストールして、C ドライブの空きは 40GB 超といったところです。もともと 49GB 程度の空きだったことを考えると、あまり圧迫はしていません。またデータは外付け MicroSD カード(Dドライブ)を利用するようにしています。

前回も書いたのですが、常にキーボード一体(+外付けマウス)で使っています。そのおかげでバッテリーも2つ使うことができるので、電池的にも有利です。ほぼ半日使えている、という印象です。キーボードそのものは小さくて打ちにくいこともありますが、まあ許容範囲かな、と思っています。あえて言うと右 SHIFT キーがなく左 SHIFT も小さいので、SHIFT キーを使う時は、通常の左 SHIFT よりも半キーぶん程左を打つことを意識しています。また \ キーが右下(右小指の位置)にあるので、この場合も意識して打つようにしています。今のところ、打ち間違いが多いのはこの2つのキーです。あとはまあなんとか対応できています。

また、この環境(省電力の ATOM チップ + メモリ 2GB)ではサクラエディタはともかく、Eclipse を使ったアプリケーション開発には厳しいだろうな、と覚悟していました。それもあって当初は環境構築すらあきらめていました。

が、思っていた以上に他の環境が快適だったので、ダメ元のつもりで Eclipse + Java の開発環境(JDK/JRE、ソースコード管理用の Subversion プラグイン、開発用アプリケーションサーバーの Jetty まで)を導入してみました。結果としてはちゃんと動いているし、「開発用と割り切れば使える」という印象です。これが実現できたことで、普段の開発環境のかなりの割合がこの N29 上に再現できました。


欲を言えば、仮想マシン環境もこの N29 上で再現できたりすると嬉しいのですが、それはさすがに(メモリが少なすぎて)無理だと思っています。まあ欲張らずに、そのあたりはメイン機で行うことにして、この N29 では編集およびサーバー管理を持ち運んだ先で行う、と割り切って使うことにします。

自宅のローカル環境に Cloud Foundry v2 を導入してみました(注 自宅 Bluemix Local ではありません)

Cloud Foundry はオープンソースの PaaS(Platform as a Services) の名称です。IBM Bluemix を始め、多くの商用/非商用クラウドサービスの基盤として利用されている実績があります。元々は VMWare 社によって開発を行っていましたが、現在は 2014年12月に設立されたコミュニティ Cloud Foundry Foundation によって開発・管理・リリースが行われています(IBM も参画しています):
2016050203



【前提条件】

Intel x86_64 版の Ubuntu 14.04 環境が前提として必要です。同環境上に Cloud Foundry v2 Nise Installer を使って Cloud Foundry 環境を構築します。

なお、自分は以下の様なスペックの物理マシンを1台用意して、そこに Ubuntu 14.04 を導入しました:
メモリ: 12GB
HDD: 1TB
導入OS: Ubuntu Desktop 14.04 (要ネット接続)


ちなみに自分が使ったのは ASUS P30AD の(旧モデルの)現品処分特価品でした:
https://www.asus.com/jp/Tower-PCs/P30AD/

2016050201



メモリだけ 12GB に増設して、〆て50000円といった所です。この約 50000 円の ASUS 機が自宅 Cloud Foundry 環境になっています。今後同様の環境を作る人にとって、1つの目安となれば。


【Cloud Foundry 導入手順】


導入作業自体はシンプルです。まず Cloud Foundry v2 Nise Installer を動かすには Rbenv が必要です。そのため最初に Rbenv と、その前提となるパッケージをインストールします:
$ sudo apt-get install build-essential bison libreadline6-dev curl git-core zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev autoconf libncurses5-dev

そして Cloud Foundry v2 Nise Installer を使って Cloud Foundry v2 を導入します:
$ sudo bash < <(curl -s -k -B https://raw.githubusercontent.com/yudai/cf_nise_installer/${INSTALLER_BRANCH:-master}/scripts/bootstrap.sh)

なお、この(2番目の)コマンドを実行してから完了するまでに、自分の環境で1時間ちょっとかかりました。コーヒーでも飲みながら完了を待ちましょう。

成功すると、最後に以下のようなメッセージが表示されてプロンプトに戻ります。「Ubuntu 10.04 の場合は再起動しろ」というメッセージが表示されていますが、自分は 14.04 でしたが念のため再起動しました:
  :
  :
Done!
You can launch Cloud Foundry with './scripts/start.sh'
Restart your server before starting processes if you are using Ubuntu 10.04

これで Cloud Foundry のローカル環境構築自体は完了です。Cloud Foundry は上記の導入手順を実行したユーザーのホームディレクトリの ~/cf_nise_installer/ フォルダ以下に導入されているはずです。

なお、この後で cf コマンドを使ったオペレーションを行うため、cf コマンドも同環境上にダウンロードして導入しておきます。cf コマンドのウェブページから Debian 64 bit の最新版インストーラをダウンロードします:
2016050201



自分が 2016/May/02 にこの作業を行ったタイミングでは、cf ツールの最新版のバージョンは 6.17.0 で、ダウンロードファイル名は cf-cli-installer_6.17.0_x86-64.deb でした。異なるバージョンおよびファイル名だった場合は適宜読み替えて以下を実行してください。

ダウンロードした cf コマンドラインツールを dkpg を使ってインストールします:
$ sudo dpkg -i cf-cli-installer_6.17.0_x86-64.deb

【Cloud Foundry 起動】

ではローカル環境に導入した Cloud Foundry を起動します。起動は以下のコマンドを実行します:
$ sudo ~/cf_nise_installer/scripts/start.sh

少しずつ Cloud Foundry のモジュールが起動していく様子がわかります。最後に以下のようなメッセージが表示されてコマンドプロンプトが戻ったら、全ての Cloud Foundry モジュールの起動が完了したことを意味します(自分の環境だと Cloud Foundry 起動完了まで10分くらいかかりました):
  :
  :
+ tail -n +3
+ grep -v -E '(running|accessible)$'
+ sudo /var/vcap/bosh/bin/monit summary
+ break
+ grep -v -E '(running|accessible)$'
+ tail -n +3
+ sudo /var/vcap/bosh/bin/monit summary
+ set +x
All processes have been started!
Login : 'cf login -a https://api.192.168.0.10.xip.io -u admin -p c1oudc0w --skip-ssl-validation'
Download CF CLI from https://github.com/cloudfoundry/cli

最後のメッセージを見ると分かるのですが、API サーバー名が api.192.168.0.10.xip.io となっています。この NISE Installer を使うと、このような名前(xxxxx.IPアドレス.xip.io)で動作する DNS サーバーが自動的にローカルマシンに導入されて動きます。自分の環境では Cloud Foundry 導入マシンの IP アドレスが 192.168.0.10 だったので、このようなサーバー名になっていますが、皆さんの環境ではこの部分は皆さんの Cloud Foundry 導入マシンの IP アドレスになっているはずです。以下を適宜置き換えて参照ください。


【cf ツールで動作確認】

では cf ツールを使って動作確認してみます。まずは api サーバーを指定して Cloud Foundry にログインします。なお API サーバーに指定する名前は api.(このサーバーのIPアドレス<この例では 192.168.0.10>).xip.io と指定します。またログイン時に指定するメールアドレスとパスワードですが、デフォルトではそれぞれ admin / c1oudc0w となっています:
$ cf login -a api.192.168.0.10.xip.io -u admin -p c1oudc0w --skip-ssl-validation

最初の段階ではデフォルトの組織は DevBox となっていますが、スペースが未定義です。そこでスペース dev を作成して、組織 DevBox と紐付けます(最初の1回だけ、この作業が必要です):
$ cf create-space dev
$ cf target -o DevBox -s dev

では適当なアプリケーションをローカルの Cloud Foundry 上にデプロイしてみましょう。今回は PHP アプリケーションをデプロイしてみます:
$ mkdir phpinfo
$ cd phpinfo
$ echo "<?php phpinfo(); ?>" > index.php $ cf push phptest -b https://github.com/cloudfoundry/php-buildpack

↑のコマンドライン操作では、中身が <?php phpinfo(); ?> だけの index.php ファイル1つだけからなる phpinfo フォルダを作り、このフォルダを丸ごと phptest という名前のアプリケーションとしてプッシュ(デプロイ)しています。またプッシュの際に GitHub 上の PHP ビルドパックを指定して、これを使ってプッシュしています。

このプッシュ操作が成功すると、以下のように実行中のステータスが表示されます:
2016050202


いまプッシュした phptest アプリケーションを "cf apps" コマンドで確認すると、同アプリケーションは phptest.192.168.0.10.xip.io というサーバー名で動いていることがわかります(青字部分が出力メッセージ):
$ cf apps
admin として組織 DevBox / スペース dev 内のアプリを取得しています...
OK

名前      要求された状態   インスタンス   メモリー   ディスク   URL
phptest   started          1/1            1G         1G         phptest.192.168.0.10.xip.io

早速同マシンのデスクトップ環境のウェブブラウザから http://phptest.192.168.0.10.xip.io/ にアクセスしてみました:
2016050204


動いたーっ!! これで我が自宅にも Cloud Foundry 環境を構築することができました!


【今後の課題?】
一応動いたこの環境ですが、必ずしもあまり安定していない気がしています。使っているうちに同じ URL にアクセスしても、"502 Bad Gateway" エラーになってしまうこともあります:
2016050205


この場合は以下のコマンドでアプリを再ステージングすると復活するのですが・・・原因とかまだよく分かっていません:
$ cf restage phptest


まあ Cloud Foundry 環境としてはマシンがちと貧弱すぎる感は否めないのですが、どーなんでしょ。


ともあれ、とりあえず github のビルドパックを指定して、PHP ランタイムが動く、ということは確認できました。カスタマイズとかほとんどしてないけど、インストーラーを実行するだけなので楽ちんでした。今後はサービスとかも組み合わせてアプリを作ってみよう。


前回の続きです:
CentOS に Go をインストールする


Go の実行環境が導入できたので、テンプレートエンジンを使ってみます。まずは HTML テンプレートを作成しておきます。base.html というファイル名で以下の内容を作成/保存します:
{{define "base"}}<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>{{.Title}}</title>
</head>
<body>
<div class="content">
<h1>{{.Title}}</h1>
<hr/>
<div>
{{.Body|safehtml}}
</div>
</div>
</body>
</html>{{end}}

↑ほぼ HTML ですが、赤字で書いた部分が変数になります。変数名としては Title と Body の2つがあり、その中身はこの後の Go アプリの中で定義して渡します。また全体を "base" という名前で定義しています(青字部分)。


次にこのテンプレートを使って画面を表示する Go のウェブアプリを作成します(http2.go というファイル名で、base.html と同じディレクトリに作成します):
package main

import(
  "net/http"
  "html/template"
)

func viewHandler( w http.ResponseWriter, req *http.Request ){
  funcMap := template.FuncMap{
    "safehtml": func(text string) template.HTML { return template.HTML(text) },
  }
  templates := template.Must(template.New("").Funcs(funcMap).ParseFiles("base.html"))
  dat := struct {
    Title string
    Body string
  }{
    Title: req.FormValue( "title" ),
    Body: req.FormValue( "body" ),
  }
  err := templates.ExecuteTemplate(w, "base", dat)
  if err != nil {
    http.Error( w, err.Error(), http.StatusInternalServerError )
  }
}

func main(){
  http.HandleFunc( "/", viewHandler )
  http.ListenAndServe( ":8080", nil )
}

基本形は前回の Go ウェブアプリとほぼ同じですが、まず html/template を使う宣言をして、テンプレートに上記で作成した(同じディレクトリ上にある) base.html を使う宣言をしています。

またテンプレート内の変数 Title, Body を、それぞれ title, body という URL パラメータを受け取って定義するようにしています。

この状態で http2.go を実行します:
# go run http2.go


そしてウェブブラウザでこのサーバーの 8080 番ポートのルートパスに、パラメータ title と body を指定してアクセスすると、指定した内容がウェブページの一部になって表示されるはずです:
2016042602


これで Go でもテンプレートエンジンっぽいこともできました。

このページのトップヘ