壊れたメガネ

ホッチキスの達人の意識の高いブログ。

mod_javaみたいなのをつくりたい

先週いろいろとやってみたことの個人的メモ。


この記事をご覧の方へ。
私はJavaについてもCについてもへっぽこです。
それはこの記事をご覧になれば明らかだと思います。
そういった点を指摘していただけるととてもうれしいです.


apacheのモジュールはおもしろい。しかも割と簡単に作れる。
apacheモジュールで動的コンテンツを作れることに気づく。
以前から頭にあったPHPPerlのようにJavaで手軽にコンテンツが作れるようなapacheモジュールが作れそうなことに気づく。(ホットデプロイ?)
しかし、そのようなものが今更無いとは考えにくく、少し調べてみたらやはりmod_gcjというapacheモジュールがあった。
かなり萎えそうになったけど、自分の欲しい物がなければつくるし、たとえ既に存在していても作ってみたいと思ったのはつくってみる。


作りたいと思ったもの
WEBサーバー(apache)に次のようなURL(ファイル名の拡張子が.java)をリクエストされた場合に当該Javaソースコードバイトコードにコンパイルし、実行する。
実行したJavaプロセスから実行結果をもらい、レスポンスを返す。そういうapacheモジュール。
URL例
http://localhost/foo/bar.java


問題
apacheがリクエストを受けて立ち上げたプロセスとJavaのプロセスってどうやってやりとりすればいいの?
最初はまったくわからなかった。
しかし、/proc/プロセス番号/fd/{0,1,2}がそれぞれそのプロセスの標準出力、標準入力、標準エラー出力だということがわかった。
以下のようなコードを試してみた。


1
2 #include <stdio.h>
3 #include <stdlib.h>
4
5 int main(int argc, char** argv) {
6 pid_t pid = -1;
7 int read_buff[1024];
8
9 pid = getpid();
10 printf("pid:%d\n", pid);
11
12 gets(read_buff);
13 printf("data received.\n%s\nexit.", read_buff);
14
15 return (EXIT_SUCCESS);
16 }
17
18
19

例えば上で示したオブジェクトコードを実行した際、端末にはプロセスIDが


pid:6095

のように表示される。このプロセスは標準入力からの入力を待ち受けている。
これに対して別の端末から以下のようにしてみた

echo hello > /proc/6095/fd/1

すると先ほどの標準入力にはhelloの文字が現れ

pid:6095
hello

となった。

12行目のgetsで入力がとれなかった。。。
入力がとれていれば


pid:6095
data received.
hello
exit.

となりプロセスは即座に終了するはずだった。
要するに私がこの方法を用いてプロセス同士がやりとりをすることが難しいと分かった。

少し途方に暮れたけど、UNIX domain socketという素晴らしい方法があることがわかった。
これは同一マシン内にあるプロセスがソケットを通じてやりとりする方法。
いろいろ試した結果、これならうまくやりとりが出来ると現在は確信している。

しかし、ここで問題が出てきた。
JavaUNIX domain socketを用いたAPIがどうやらないらしい。
ないなら作るしかない。

以前Java仮想マシンソースコードをチラッと見たときにjniという抽象化レイヤーがあることをしった。
Cでネイティブコード書いてJavajniを用いて接続用のコードを書くんだろうなと考えていた。
もうすこし調べてみるとjnaというjniの上に位置する抽象化レイヤーがあることを知った。
こちらの方がjniよりもはるかに簡単だった。(なにが?)

ということで、UNIX domain socketを扱う動的ライブラリを書いて、そのインターフェースをJavaで定義することにした。
psocket2.h


1
2 #ifndef PSOCKET_H
3 #define PSOCKET_H
4
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <sys/un.h>
8 #include <stdlib.h>
9 #include <stdio.h>
10
11 #ifdef __cplusplus
12 extern "C" {
13 #endif
14
15 typedef struct sockaddr_un sockaddr_un;
16
17 typedef struct psocket_un_stream {
18 int domain;
19 int type;
20 int protocol;
21 int sockfd_listener;
22 sockaddr_un *addr_un_self;
23 sockaddr_un *addr_un_target;
24 } psocket_un_stream;
25
26 psocket_un_stream *psun_construct();
27
28 /**
29 * if error occured, then returns value -1, else returns 0
30 * @param psun
31 * @param sockname
32 * @return
33 */
34 int psun_bind(psocket_un_stream *psun, const char *sockname);
35
36 /**
37 * if error occured, then returns value -1, else returns 0
38 * @param psun
39 * @param sockname
40 * @return
41 */
42 int psun_listen(psocket_un_stream *psun, int backlog);
43
44 /**
45 * if error occured, then returns value -1, else returns file descriptor
46 * @param psun
47 * @return file descriptor
48 */
49 int psun_accept(psocket_un_stream *psun);
50
51 void psun_destruct(psocket_un_stream *psun);
52
53 #ifdef __cplusplus
54 }
55 #endif
56
57 #endif /* PSOCKET_H */
58
59

Psocket2.java


1
2 package psa;
3
4 import com.sun.jna.*;
5
6 public interface Psocket2 extends Library {
7
8 public class psocket_un_stream extends PointerType {
9 public psocket_un_stream () {
10
11 }
12
13 public psocket_un_stream(Pointer p) {
14 super(p);
15 }
16 }
17
18 public psocket_un_stream psun_construct();
19
20 public int psun_bind(psocket_un_stream psun, String sockname);
21
22 public int psun_listen(psocket_un_stream psun, int backlog);
23
24 public int psun_accept(psocket_un_stream psun);
25
26 public void psun_destruct(psocket_un_stream psun);
27 }
28
29

あと残るはJavaでデーモンを動かして、そのデーモンにリクエストのあったJavaクラスをロードしてもらい実行するということをやる必要があると考えている。