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

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

タグ:jdk

Java のアプリ開発をしていて、こんな実行時エラーに遭遇することがあります:
java.lang.UnsatisfiedLinkError: C:\IBM\Notes\nlsxbe.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform


最近は Windows といえば 64bit 版が普通になってきました(よね?)。自分の開発環境も 64bit 版 Windows で、Java 開発環境も(JDK も JRE も Eclipse も)64bit 版を使っています。ここまでは珍しくないと思っています。

しかし開発するアプリケーションによっては外部ライブラリ(JAR ファイル)を指定する必要があり、その JAR ファイルが 32bit のネイティブライブラリを使っていたりするなど、64bit の JDK で 32bit 前提のコードを実行しなければならなくなった場合に上記のようなエラーが発生するのでした。

典型例としては、IBM Notes の Java API を使ったアプリケーション開発が挙げられます。2017/Oct/01 現在、Windows 版の IBM Notes は 32bit 版しか存在していません。Java で IBM Notes の API を実行するには IBM Notes 同梱の Notes.jar 他をインポートして使う必要がありますが、この Notes.jar は 32bit の DLL を参照するため、64bit Java から実行すると上記のエラーが発生します。


エラーの原因が分かったところで、どのように回避すればいいでしょうか? これも解決策としてはシンプルで 32bit Java(JRE) から実行すればよいことになります。32bit JRE をダウンロードして実行してもいいですが、上記のような IBM Notes の JAR を使った場合であれば、IBM Notes が使っている Java をそのまま指定して実行すればよいことになります。

ちとややこしいのが Eclipse のような IDE を使っている場合の指定方法です。まず Java - Installed JREs の中に Standard VM として Notes Java を追加しておく必要があります。Notes Java は(ノーツをインストールしたディレクトリ)/jvm にあるので、このディレクトリを指定して追加します:
2017100101


そして Eclipse の Java プロジェクトの中で JRE System Library として、上記で設定した Notes Java を指定します。これでこのプロジェクト内で作成したアプリを実行する際には Notes Java 内の java.exe が利用されるようになるので、上記のエラーが回避できるようになります:
2017100102



 

IBM Watson の API を使っていたら、ある日を境にこんなエラーが出るようになりました:
javax.net.ssl.SSLException: java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 64

2016110901


エラーが出ているのは URL を指定して REST API を実行する瞬間の箇所です。GET でも POST でも発生しました。

原因を調べるために Watson API Explorer を使って、同じ API を実行してみたところ・・・今度はエラーなく実行できてしまいました:
2016110902


なんだこれは!? というわけで、エラーメッセージをググって調べてみたところ、こんな StackOverflow にこんなスレッドを見つけました:
Receiving “javax.net.ssl.SSLException: java.lang.ArrayIndexOutOfBoundsException” while connecting to “https:” site


ここに書かれている内容が原因であるとすると、暗号化アルゴリズムである TLS のバージョンの変更が原因らしいとのこと。またいくつかの対処方法があるが、クライアント側だけで対応するには JDK のバージョンを 1.8 にしろ、とのことらしいです。

確かに今回エラーが出るようになったシステムでは JDK 1.6 が使われているので、このエラーが発生する条件は満たしています。また Watson API Explorer 側では JDK 1.8 か、Java 以外のアプリケーションサーバーが使われていると仮定すればエラーなしに成功することになります。なので、今回のような結果になっても説明は付くことになります。
2016110903



原因が分かったところで、問題は対処方法です。今回はとあるシステム内からこの API を実行したかったという背景があります。そのシステムに組み込まれている JDK のバージョンが 1.6 である以上、ここを勝手に変更するわけにはいきません。 一方で、クライアントが JDK 1.8 未満である以上はこのエラーを回避するにはサーバー側を変更する必要があります。API 提供されているサーバー設定を変更してもらえる保証もありません。そもそもセキュリティ強化のためのサーバー設定変更が背景にあるわけですが、仮にセキュリティに一旦目をつぶるとして、JDK 1.6 のままでどうにかする方法はないでしょうか?


その方法の1つとして考えられるのが、下図のように JDK 1.8(または他のプラットフォームでもよい)など Watson API を実行できる環境でプロクシー的なものを作って、JDK 1.8 未満のシステムからはこのプロクシーを経由してリクエストを実行し、レスポンスを得る、という仕組みです:
2016110904

こうすると JDK 1.8 未満のシステムからはプロクシーまでアクセスできればよく、またプロクシー側の設定等にも自分である程度手を加えることも可能になります。そして Watson からは JDK 1.8 からのリクエストとして API を実行して結果を返す、という仕組みを作ればうまくいくのではないか、と考えました。まあ2つの仕組みを作ってメンテナンスしなければならない、という点はありますが、技術的にはクリアできる目処が立ちそうです。

近いうちに実際に作ってみた仕組みをまた紹介する予定です。


僕は普段 Java の IDE (統合開発環境)として Eclipse を使ってますが、久しぶりに IDE なしでサーブレットを作る機会がありました。結構ハマったこともあって、普段いかに Eclipse に頼っているのかを痛感しました・・・


今回やりたかったことは、ごくごくシンプルな話で、
 (1) サーブレットの Java ソースファイルを書いて、
 (2) JDK でビルドしてサーブレットの class ファイルを作る
という2つです。要はサーブレットの実行環境が特殊で war ファイルとかをデプロイできないため、 class ファイルをそのまま配置する必要がある、という(IBM Domino とかの)ケースを想定しています。

なお環境は Windows7 で、JDK 1.8 はダウンロード&インストール済みとします。



まず (1) のソースファイルは以下のような感じにしました。パッケージも指定していません。これを UTF-8 で記述して、HelloWorld.java というファイル名で保存します:
//. HelloWorld.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloWorld extends HttpServlet{
  @Override
  protected void doGet( HttpServletRequest req, HttpServletResponse res ){
    try{
      res.setContentType( "text/plain; charset=UTF-8" );
      res.getWriter().println( "ハローワールド!" );
    }catch( Exception e ){
      e.printStackTrace();
    }
  }
}

↑特別なことは何もしてない、ごくごくシンプルな「ハローワールド!」です。


さて、これを (2) JDK でビルドして class ファイルに・・・という段階で何度かつまづきました。

まずは普通にそのままコンパイルしてみると、
C:\tmp>javac HelloWorld.java

HelloWorld.java:11: エラー: この文字は、エンコーディングMS932にマップできません
      res.getWriter().println( "繝上Ο繝シ繝ッ繝シ繝ォ繝会シ?" );

「ハローワールド!」という日本語文字列のエンコードでエラーが出てしまいました。ちゃんとソースコードは UTF-8 にしたつもりだったのに・・・

実は Windows 版の JDK の javac はソースコードが Shift-JIS で書かれているものとして動作するらしいのでした(なんちゅうクソ仕様・・・)。便利とは思えない仕様ですが、このエラーを回避するにはコマンドラインパラメータでソースコードのエンコードを指定必要があるのでした。

というわけで今度は UTF-8 を指定してコンパイル。すると別のエラーが大量に・・・
C:\tmp>javac -encoding utf-8 HelloWorld.java

HelloWorld.java:3: エラー: パッケージjavax.servletは存在しません
import javax.servlet.*;
^
HelloWorld.java:4: エラー: パッケージjavax.servlet.httpは存在しません
import javax.servlet.http.*;
^
HelloWorld.java:6: エラー: シンボルを見つけられません
public class DominoInfo extends HttpServlet{
                                ^
  シンボル: クラス HttpServlet
HelloWorld.java:8: エラー: シンボルを見つけられません
  protected void doGet( HttpServletRequest req, HttpServletResponse res ){
                        ^
  シンボル:   クラス HttpServletRequest
  :
  :

これら全てサーブレットの JAR ライブラリを(指定していないので)見つけられないことが原因のエラーです。これは普段の Eclipse 環境でも指定していることでしたが、すっかり忘れていました。

Tomcat あたりをダウンロード&インストールして、サーブレット JAR ファイル(servlet-api.jar とかいう名前だと思います)を見つけて取り出します(以下の例では c:\arc\servlet-api-3.1.jar というパスで保存しているものとして記述しています)。で、改めてこのファイルを classpath に指定してコンパイル:
C:\tmp>javac -classpath c:\arc\servlet-api-3.1.jar -encoding utf-8 HelloWorld.java
C:\tmp>

無事コンパイルできました。HelloWorld.class が出来ているはずです:
2016102201


・・・こ、こんなに難しかったっけ? (^^;


この応用で、作ったサーブレットを実際に IBM Domino 上で動かすための設定についてはこちら


今まで Tomcat を使う場合は使い慣れた(理由はそれだけじゃないけど) Tomcat6 を使っていました。試しに Tomcat7 を導入する機会があったので、その導入手順を紹介します。環境はいつもの CentOS 6 です。


まずは Java/JDK 環境が必要です。今回は OpenJava JDK 1.7 を導入することにしました:
# yum install java-1.7.0-openjdk-devel

さて Tomcat7 です。実は CentOS 6 の標準リポジトリには Tomcat7 は含まれていません。標準で含まれているのは Tomcat6 ですが、yum でインストールする場合は
# yum install tomcat6

のように、わざわざ「6」を指定して導入する必要があります。この辺りの導入手順の詳細はこちらを参照してください:
CentOS に Apache Tomcat を導入する

さて Tomcat7 です。Tomcat6 は「6」を指定する必要がありました。Tomcat7 も「7」を指定する必要があるかというと、これがありません(笑)。コマンドとしては
# yum install tomcat

で導入されるのですが、普通にそのまま実行しても「見つからない」というエラーになるだけだと思います。Tomcat7 を yum でインストールするには事前に EPEL リポジトリの登録が必要です。つまり、まずはこのコマンドを実行します:
# rpm -Uvh http://ftp.riken.jp/Linux/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm

その後に yum で install です。tomcat-admin-webapps パッケージを指定して、管理コンソール画面ごとインストールします:
# yum install tomcat tomcat-admin-webapps

管理コンソール画面にアクセスするための設定を追加します。/etc/tomcat/tomcat-users.xml をテキストエディタで編集し、以下の3行を追加します:
<role rolename="admin-gui"/>
<role rolename="manager-gui"/>
<user username="admin" password="P@ssw0rd" roles="admin-gui,manager-gui"/>
   ↑この例だとユーザー名: admin、パスワード: P@ssw0rd でアクセスするユーザーを作成


インストール後は "tomcat" というサービス名で起動したり、再起動したり、自動実行指定ができます:
# /etc/init.d/tomcat start
# chkconfig tomcat on

管理コンソール画面ごとインストールした場合は、http://(IPアドレス:8080)/manager/html で管理画面にアクセスできます。Basic 認証でユーザー名とパスワード(上記設定の場合であれば admin と P@ssw0rd)を指定してログインできます:
2015122801


CentOS6 で Tomcat7 を使う場合は EPEL リポジトリの登録、が肝です。


どこからでもアクセスできるクラウド上に自分のデスクトップ環境があると便利ですよね。

その OS が Windows であれば、(外部からアクセスできるようにするだけなので)環境構築自体はさほど難しくないと思います。ただ Windows の仮想環境は(Windows サーバーのライセンス代が無条件で付いてくるため)安くありません。業務上の選択肢としてはまだしも、個人の環境としてはちと非現実的です。 一方、CentOS で実現できると、多くのクラウドベンダーで無料期間が用意されていたり、安く使えたりするので個人開発者としては嬉しいわけです。一方で、主にサーバー用途向けに用意されているインスタンス上にデスクトップクライアント環境を用意するのはちょっと面倒ではあります。そもそもサーバー用途ならコマンドラインインターフェースで充分なのですが、デスクトップクライアントとなるとウィンドウシステム必須です。日本語環境が用意されているとも限りません。なので、これらからのインストールが必要になります。


というわけで、クラウド上の CentOS サーバーを、日本語デスクトップ環境に改造するまでの手続きを紹介します。具体的には IDCF クラウドの CentOS 6(64bit) を使い、以下のパッケージを追加インストールします(サーバー用途でも使いそうなパッケージはこのリストからは抜いています。必要であれば別途導入してください):
パッケージ名用途
X Window Systemウィンドウシステム
DesktopGNOME デスクトップ環境
Japanese Support日本語環境
Tiger VNC ServerVNC サーバー
LibreOfficeオフィススイート
Eclipse + JDK統合開発環境
Firefoxウェブブラウザ
Alacarteメニューカスタマイズ用ツール


まず最初に、今回作成するデスクトップ環境へは VNC を使って外部からウィンドウシステムにアクセスします。というわけで VNC 用にポートフォワード設定が必要になります。今回は 5901 番ポートで設定するので、各種クラウドのファイアウォールやポートフォワード設定で 5901 番ポートを通すように設定してください(図は IDCF クラウドでのファイアウォール設定画面ですが、使っている環境で同様の設定をしてください):
2015020603


次に日本語デスクトップ環境を構築するためのモジュールを導入します。SSH で root ログインし、以下の手順を実行します:
(リスト内の上3つのグループモジュールをまとめて導入)
# yum groupinstall "X Window System" "Desktop" "Japanese Support"

ここまでで X Window System と GNOME デスクトップ、そして日本語環境が導入されます。されますが、このままでは SSH でのみリモートアクセスできるだけで、X Window の画面を開く術がありません。せっかく X Window まで導入したので VNC を使ってウィンドウシステムを利用できるようにしましょう。このサーバーインスタンス内には VNC サーバーを導入して、5901 番ポートでアクセスできるようセットアップします:
(VNC サーバーを導入)
# yum install tigervnc-server

導入した VNC サーバーを設定します。今回は root 権限で VNC サーバーにアクセスする例を紹介しますが、一般ユーザーでアクセスする場合も同様にしてそのユーザーを指定して行ってください:
(VNC サーバーの設定変更)
# vi /etc/sysconfig/vncservers

(以下の2行のコメントを外して編集(この例では root で接続))
VNCSERVERS="1:root"
VNCSERVERARGS[1]="-geometry 1024x768 -nolisten tcp"

(VNC サーバーの接続パスワードを設定) # vncpasswd (パスワードを2回入力して設定)

設定後、改めて VNC サーバーを起動します:
(VNC サーバーの起動)
# /etc/init.d/vncserver start

起動に成功すると ~/.vnc/xstartup というファイルが作成されるので、このファイルを編集して GNOME セッションの画面をそのまま VNC で流せるように変更します:
(VNC サーバーの設定変更)
# vi ~/.vnc/xstartup

(#!/bin/sh の直下に以下の一行を追加)
export LANG="ja_JP.UTF-8"

  :
  :

(最後の2行をコメントして、その下に1行追加)
#xterm -geometry 80x24+10+10 -ls -title "$VNCDESKTOP Desktop" &
#twm &
gnome-session &

最後に VNC サーバーの自動起動を ON にして、サーバーインスタンス自体を再起動します:
(VNC サーバーの自動起動)
# chkconfig vncserver on

(再起動)
# shutdown -r now

サーバーが再起動した頃を見計らって、今度は VNC クライアント(ここでは UltraVNC プロジェクトの UltraVNC Viewer)を使ってアクセスしてみます。UltraVNC Viewer をインストール&起動して、接続情報に CentOS マシンの IP アドレス:1 を入力します(最後はコロンと1):
2015020604


パスワードを聞かれたら、上記 vncpasswd コマンド実行時に指定したパスワードを入力します:
2015020605


これで X Window System 上の GNOME デスクトップ画面が表示されるはずです。ここからは GUI が使えるようになりました!:
2015020606


この時点で日本語表示はできますが、まだ日本語入力はできないと思います。そこで日本語入力のための設定を行います。メニューから システム - 設定 - 入力メソッド を選択します:
2015020607


「入力メソッド設定ツール」が起動します。「入力メソッドの機能を有効にする」にチェックが入っていて、かつ入力メソッドに「IBus を使用する」が選択されていることを確認した上で「入力メソッドの個人設定」をクリックします:
2015020608


「IBusの設定」ウィンドウが開きます。「インプットメソッド」タブ内に「日本語 - Anthy」が含まれていることを確認して「閉じる」をクリックします:
2015020609


1つ前の画面に戻り、「閉じる」をクリックして設定ツールを終了します:
2015020610


今設定した内容を反映させるため、再度システムを再起動します。メニューの アプリケーション - システムツール - 端末を選んでターミナル画面を開き、再起動コマンドを入力します:
2015020611

(再起動)
# shutdown -r now

再起動した頃を見計らって、再度 UltraVNC Viewer でアクセス&ログインします。そして同様に端末を出した後にキーボードの CTRL+SPACE を押すと、画面上部のキーボード部分が日本語入力マークに切り替わり、同時にローマ字による日本語入力が可能になります。アルファベット入力に戻すには再度 CTRL+SPACE を押します:
2015020612


ここまでで、ほぼ日本語デスクトップ環境が整いました。後は必要そうな GUI アプリケーションを yum でインストールします:
(GUIアプリ(ここでは FireFox と LibreOffice と JDK)を導入)
# yum install firefox libreoffice java-1.7.0-openjdk-devel

実はこの後、統合開発環境の Eclipse を導入します。Eclipse 自体も yum で(yum install eclipse で)導入できるのですが、yum だと最新版でないことに加え、メニューのカスタマイズ方法と合わせて紹介したいので、Eclipse だけは別途最新版をダウンロードして導入します。Eclipse は不要であっても、ウィンドウのメニューに表示されないコマンドを表示させたい場合は以下の手順を参考にしてください。

改めて、この時点で FireFox, LibreOffice, JDK がインストールされて、メニューからも実行できるようになっているはずです:
2015020613
2015020614
2015020615


では改めて Eclipse をインストールします。最新版を導入したいので先ほど導入した FireFox を起動し、http://www.eclipse.org/downloads/ から最新版の Eclipse をダウンロードします:
2015020616


ダウンロードが完了すると(僕の環境では)eclipse-java-luna-SR1a-linux-gtk-x86_64.tar.gz というファイルができているはずです(異なるファイル名の場合は適宜読み替えてください)。これを適当なディレクトリ(以下の例では /opt/ 以下)に展開します:
(/tmp/ 以下にダウンロードモジュールがある場合のコマンド例)
# cd /opt
# tar xzvf /tmp/eclipse-java-luna-SR1a-linux-gtk-x86_64.tar.gz

これで /opt/eclipse/ というフォルダが作られ、その下に Eclipse 環境一式が作られているはずです。Eclipse 本体は /opt/eclipse/eclipse というファイルパスで実行できるようになっています。

ただ、この時点ではファイルを展開しただけなので、ウィンドウのメニューから Eclipse を実行することができません。yum で Eclipse をインストールすると、「プログラミング」メニュー内に Eclipse が作られる(ただし最新版ではない)ので、できれば同様に「プログラミング」メニュー内に Eclipse を追加したいものです。


というわけで、最後にメニューのカスタマイズ方法を紹介します。メニューのカスタマイズには alacarte というツールを使うと便利なので、まずは alacarte を yum でインストールして実行します:
(メニューカスタマイズツールを導入して)
# yum install alacarte

# alacarte

alacarte が起動すると、現在のメニュー内容が表示されます:
2015020617


今回は /opt/eclipse/eclipse を「プログラミング」メニュー内に追加したいので、左のメニューでは「プログラミング」を選択し、「新しいアイテム」ボタンをクリックします:
2015020618


追加したいアプリケーションの情報を入力します。種類はアプリケーション、名前は Eclipse 、そしてコマンドに実行コマンドの絶対ファイルパスを指定して、最後に「OK」ボタンをクリックします:
2015020619


1つ前の画面に戻り、「プログラミング」メニュー内に "Eclipse" が追加されたことを確認して「閉じる」をクリックします。これでメニューのカスタマイズができたはずです:
2015020620


実際にウィンドウのメニューを確認すると「プログラミング」メニュー内に "Eclipse" が追加されていることを確認できます:
 2015020621


実際に "Eclipse" を選択すると、指定された実行ファイルが呼び出されて Eclipse が起動するはずです:
2015020622


ざっとこんな感じ。一般的にはサーバー用途で使うクラウドの CentOS 環境に日本語デスクトップ環境を構築してメニューのカスタマイズまで行う手順を紹介しました。GUI へは VNC を使ってアクセスするので、Android や iPad などのタブレットにキーボードさえ付けておけば、後は各種 VNC クライアントをインストールしていれば、それらのモバイル環境からこのデスクトップ環境を呼び出して使うことだってできちゃいます。最近は Linux のデスクトップアプリケーションも充実してきており(個人的にはゲームだけがまだまだ、だと思ってます)、手軽にタブレットさえ持ち運んでいればそこそこ使えると思っています。

なお Amazon EC2 の場合、MarketPlace から CentOS を選んで導入すれば同様の手順で同じような環境が構築できると思っています。ただ Amazon AMI から提供される Amazon Linux の場合、X Window を導入できるかどうかは未確認です。 また、ここで紹介した手順は CentOS に限らず RHEL(RedHat Enterprise Linux) でも使えます。私自身は Power Linux の RHEL で同様のデスクトップ環境を構築したこともあるので、ある程度の所まではできると思っています。


私個人はここで紹介したアプリに加えて、GIMP とか Sublime Text とか IBM Notes とかといった GUI 前提のクライアントアプリケーションも導入して使ったりしています。ここで紹介しなかったアプリケーションについても、ここで紹介した方法の応用で導入できるのでは、と思っています。






このページのトップヘ