2020年10月31日土曜日

Javaでデュアルスタック環境のときIPv6通信させるには

最近、Java(JDK11)環境でHTTP通信しているときに気が付いたのだが、Javaアプリケーションの通信の多くはIPv4で通信している。IPv6対応のサービスに接続してもIPv4通信している。Javaのドキュメントを見ると、IPv6対応をうたっており、通信できないわけではない。
実際、IPv6のアドレスを直接打って通信させると問題なくIPv6で通信する。
IPv6が使われない理由は、Javaのデフォルト設定だとIPv4とIPv6の両方対応している場合、IPv4を優先して利用するようになっているためである。

IPv4とIPv6の両方対応しているとはどういうことなのか?
ここからはJava固有の話ではなく、インターネット通信全般の話になる。

そもそも、IPv6とIPv4は全く互換性がなく相互通信できない。つまり、IPv4のネットワークとIPv6のネットワークは完全に分離されており別物である。したがって、IPの世界で両方対応するということは不可能である。

そのため、両方対応するにはDNSのデュアルスタックという技術が一般的に利用されている。

DNSの仕様で、IPv4のアドレス解決はAレコード、IPv6のアドレス解決はAAAAレコードを登録することになっている。IPv4/IPv6デュアルスタックとは、同じホスト名に両方のレコードを登録することである。

IPv4ネットワークに接続している端末からは、DNSでAのクエリを発行し、IPv4アドレスに変換する。IPv4アドレスが判明するので、IPv4ネットワークでサーバーに接続しに行く。
IPv6の場合も同様で、IPv6ネットワークに接続している端末から、DNSでAAAAのクエリを発行し、IPv6アドレスに変換する。IPv6アドレスが判明するので、IPv6ネットワークでサーバーに接続しに行く。
つまり、IPv6とIPv4の両方に接続している端末が、最初にAレコードとAAAAレコードのどっちを投げるのかで、IPv4/IPv6のどっちのネットワークに接続するかが決まる。
サーバー側は、IPv4とIPv6で別のサーバーを用意してもいいし、1台で両方のネットワークに接続してもよい。両方のネットワークにサーバーを用意して、DNS設定さえすればいい。

やっと、本題。JavaでIPv4とIPv6どっちを優先するかの設定は、以下のJVMプロパティによって切り替えることになっている。
java.net.preferIPv6Addresses
JVMプロパティなのでこのリクエストはIPv6といったことはできず、アプリケーション単位での切り替えとなる。

JVMプロパティの切り替えは、起動オプションで「-Djava.net.preferIPv6Addresses=true」をつけるか、mainメソッドの最初で以下のコードを実行する。

System.setProperty("java.net.preferIPv6Addresses", "true");

近年はIPv6の普及も進んできているので、基本的に上記の設定を行うことをお勧めする。

2015年7月25日土曜日

正しくカスタムプロトコルのURLStreamHandlerを登録する

JavaでカスタムプロトコルのURLStreamHandlerを登録する方法として、ネット上で挙げられている方法はURL.setURLStreamHandlerFactoryである。
しかし、このメソッドは大きな欠点があり、複数回setURLStreamHandlerFactoryを呼びさせない仕様にしてある。つまり、複数のURLStreamHandlerFactoryが登録できない。複数のURLStreamHandlerを登録したいときには、一つのURLStreamHandlerFactoryで、すべてのURLStreamHandlerを登録しなければならない。

しかし、ドキュメントをよく読むと、カスタムプロトコルのURLStreamHandlerを登録する方法がもう一つ存在すると示されている。
  1. 以前にアプリケーションがURLStreamHandlerFactoryのインスタンスをストリーム・ハンドラ・ファクトリとして設定している場合は、そのインスタンスのcreateURLStreamHandlerメソッドがプロトコル文字列を引数として呼び出されて、ストリーム・プロトコル・ハンドラを作成する。
  2. まだURLStreamHandlerFactoryが設定されていない場合、あるいはファクトリのcreateURLStreamHandlerメソッドがnullを返した場合は、コンストラクタが次のシステム・プロパティの値を探す。
    java.protocol.handler.pkgs
    このシステム・プロパティの値がnullでなければ、値は、垂直スラッシュ文字「|」で区切られた、パッケージのリストとして解釈される。コンストラクタは次の名前を持つクラスをロードしようとする。
    <package>.<protocol>.Handler
    ここで、<package>にはパッケージの名前が入り、<protocol>にはプロトコルの名前が入る。このクラスが存在しない場合、あるいはクラスは存在してもそれがURLStreamHandlerのサブクラスではない場合には、リストにある次のパッケージを試すことになる。
  3. 以上の手順でもプロトコル・ハンドラが見つからなかった場合、コンストラクタはシステムのデフォルト・パッケージからロードしようとする。
    <system default package>.<protocol>.Handler
    このクラスが存在しない場合、またはクラスは存在するがURLStreamHandlerのサブクラスではない場合は、MalformedURLExceptionがスローされる。
2番目のシステムプロパティjava.protocol.handler.pkgsを使う方法である。システムプロパティにパッケージ名を登録すると、そのパッケージ配下から必要なURLStreamHandlerを検索する。パッケージ名はパイプ区切りで列挙できるので、この方法を使えば、何度でもURLStreamHandlerを登録することができる。

具体的な方法は、まず<任意のパッケージ名>.<プロトコル名>.HandlerのクラスとしてURLStreamHandlerのサブクラスを作る。
システムプロパティjava.protocol.handler.pkgsに対して、<任意のパッケージ名>を登録する。このとき、java.protocol.handler.pkgsがすでに登録されているときは、パイプ区切りで追加する必要があるので、以下のように分岐をして判別する必要がある。
String pkg = System.getProperty("java.protocol.handler.pkgs", "");
if(!"".equals(pkg)) pkg += "|";
pkg += "<任意のパッケージ名>";
System.setProperty("java.protocol.handler.pkgs", pkg);

これだけで、複数の必要なカスタムプロトコルのURLStreamHandlerを登録できる。

2014年6月10日火曜日

WildFly 8.1.0に乗せ換える

もともとGlassFish 3を利用していたが、Java EE 7サーバーに切り替える際にWildFly 8に乗せ換えた。
しかし、乗せ換えは単純にいかなかったので、今後挑戦する勇者のために、うまくいく作業記録を記すことにした。

wildflyのインストール・起動まで
あまり難しい作業でない。
ただし、RedHat系の作業手順であるので、Debian系の場合一部異なる。
chownをしているのは、wildfly.confでJBOSS_USER=wildflyと設定したため。

wget http://download.jboss.org/wildfly/8.1.0.Final/wildfly-8.1.0.Final.tar.gz
tar xzvf wildfly-8.1.0.Final.tar.gz
cd wildfly-8.1.0.Final
./bin/add-user.sh
cp ./bin/init.d/wildfly.conf /etc/default/wildfly.conf
cp ./bin/init.d/wildfly-init-redhat.sh /etc/init.d/wildfly
vi /etc/default/wildfly.conf
chown -R wildfly:wildfly .
chkconfig --add wildfly
service wildfly start

wildflyの設定コンソール

./bin/jboss-cli.sh
connect

DerbyのJTA登録
一番つまずくところは、module追加時のdependencies設定をするところである。入れ忘れると謎のエラーに悩まされる。
GlassFishと違いJNDIは必ず[java:]で始まらければならない。そのため、WARも修正が必須になる場合が多い。

module add --name=derbyclient --resources=/......./derbyclient.jar --dependencies=javax.api,javax.transaction.api,javax.servlet.api
:shutdown(restart=true)
/subsystem=datasources/jdbc-driver=derby:add(driver-name="derby",driver-module-name="derbyclient",driver-class-name="org.apache.derby.jdbc.ClientDriver40",driver-datasource-class-name="org.apache.derby.jdbc.ClientDataSource40",driver-xa-datasource-class-name="org.apache.derby.jdbc.ClientXADataSource40")
/subsystem=datasources/data-source=DBNAME:add(driver-name="derby",jndi-name="java:jboss/jdbc/DBNAME",connection-url="jdbc:derby://localhost/DBNAME;user=user;password=password")
:reload

Java Mail登録
こちらもJNDIは必ず[java:]で始まらければならない。

/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=gmail:add(host="smtp.gmail.com",port="465")
/subsystem=mail/mail-session=gmail:add(jndi-name="java:jboss/mail/Gmail",from="example@repy.info")
/subsystem=mail/mail-session=gmail/server=smtp:add(outbound-socket-binding-ref="gmail",ssl="true",username="example@repy.info",password="password")
:reload

VirtualHost設定
VirtualHostを利用するには、WARの/WEB-INF/jboss-web.xmlに対して<virtual-host>と<context-root>を設定しておく。
また、利用するVirtualHostを前もって設定しておく必要がある。設定していないとデプロイに失敗する。

/subsystem=undertow/server=default-server/host="example.repy.info":add

デプロイ

deploy /......./example.war


今回はすべてjboss-cliから行う手順にしたが、一部を除いて Management console から実行することもできる。
ただ、VirtualHostは Management console から設定できないので要注意である。

2013年4月17日水曜日

日本語フルキーボード for Tablet Mozcエンジン

2013年4月7日に日本語フルキーボード for Tablet専用Mozcエンジンをリリースしました。これにより、オフラインでも快適な日本語入力環境が利用できます。

Mozcエンジンは、辞書が巨大です。また、日本語入力という仕様上SDカードへの移動はできません。内部ストレージの容量が大きい端末にお勧めです。
また、PC向けMozcと同一辞書なので、携帯で入力する際にはあまり利用しない単語が上位に来る場合があります。話し言葉などが上位に来ない場合があります。
ユーザー辞書編集機能、予測学習機能はありません。これに関しては今後実装したいと思います。

カスタマイズ
のライブが最近多いので、ちょっと開発が停滞気味ですが、春の大型連休にはアップデートをしたいと思います。

2013年4月1日月曜日

日本語フルキーボード for Tablet with Mozc

とりあえず動作するレベルにはなっているが、まだまだ公開できるレベルにはなっていないので、スクリーンショットだけあげておく。

きょう」と入力して、「2013年4月1日」に変換できているので、Mozcの変換結果だと判別できると思う。

動作自体には全く問題ないが、問題なのは別の点にある。それはAPKサイズ。今までは、3MB弱であったものが、一気に21.3MBまでパワーアップ。
もともとMozcの辞書自体が15MB程度あり、ARM用・x86用・MIPS用のMozcライブラリを同梱したため、圧縮しても+18MBになってしまった。

21.3MBのサイズは、気軽にインストールできないので、別パッケージにしようと思う。
こう考えると、あの容量で日本語変換ができる、OpenWnnがすごいということが分かった。



2013年3月30日土曜日

Mozc for Androidを見てみた

Mozc for Androidエンジンを移植するため、ソースを見てみた。
結果、簡単に移植できないことが判明。実装には1か月はかかるかもしれない。もしくは、もっとかかるかもしれない。

理由について述べる前に、IMEを作るにあたって必要なことを書いておきたい。
IMEの仕事は大きく分けて4つほどある。
  1. キー入力受付部分
  2. 文字状態の認識部分
  3. かな漢字変換部分
  4. 表示部分
最初は、キー入力受付部分だ。
キー入力といっても、ソフトウェアキーボード・ハードキーボード・端末物理キー・OSのソフトキー・USBキーボードなど様々なデバイスがある。
それらのデバイスから送られる情報をもとに、何のキーを押されたか判定する必要があるが、デバイスごとに情報の形が異なるので、それらを共通化しなければならない。
この時点でデータを共通化することで、それ以降の処理を一本化している。

キー入力受付部分から、送られる情報は、aなどアルファベットや記号などの状態なのでこのままでは何もできない。
キー情報をもとにひらがなの状態を管理し、漢字変換後は漢字や読みの状態を管理しなければならない。それらを行っているのが、文字状態の認識部分だ。
この部分はさほど複雑ではないが、設定で変更したいという要望が多い部分なので、分岐コードがたくさん存在する。

かな漢字変換部分は、ひらがなを漢字に変換するだけだ。わかりやすいが、一番時間のかかるところだ。
日本語フルキーボード for Tabletでは、全く作っていない部分で、OpenWnnやGoogle CGI API for Japanese Inputなどから提供を受けている。

表示部分は、ひらがなの状態や漢字変換後は漢字や読みの状態を表示させているだけだ。
AndroidのIMEなら、キーボードの上に表示される。

つまり、日本語フルキーボード for Tabletに新たなエンジンを載せるためには、かな漢字変換部分だけが利用できると都合がよい。
しかし、Mozcの構成は、コミックにもなっている通り、文字状態の認識部分かな漢字変換部分が一塊になっているため、そのままでは日本語フルキーボード for Tabletに載せることができない。
一度PC向けMozcを移植しようとしてつまずいた点でもあるので、今回も難易度があるかもしれない。

2013年3月28日木曜日

日本語フルキーボード for TabletにMozcエンジンを搭載します

オープンソースになったMozcエンジンの搭載を目指します。

いままでオフラインエンジンはOpenWnnエンジンだったため、語彙力が低く、文節の区切り方もいまいちでしたが、次回のバージョンでMozcエンジン(ベータ)を搭載し、どちらでも利用できるように
できるように開発を行います。
ただし、開発工数によっては機能の一部有料化や広告表示率の調整を行わせていただくことになるかもしれません。

皆さんダウンロードしてください。