Scala から Websphere MQ を使う [Scala]
Scala は最初から JVM で動かす前提で作られた言語なので Java のライブラリに関してもほぼ透過的に利用することができます。(これに対して例えば Jython だとバイト配列の扱いなどで Python-Java 間のインピーダンスを感じることがあります)
とはいえ Scala プログラムの中で Java 用のライブラリを使用しているとどうしてもその Java 臭さが鼻につくことがあることも確かです。こういうときは Scala の DSL 力を駆使してラッパーインターフェイスを作ってしまうのがよいでしょう。
ここでは Websphere MQ の Java インターフェイスを Scala 風にする例を取り上げます。
以下は Websphere MQ で「キューマネージャ "QM" 上の "postcard" キューに対してメッセージ "Hello" を PUT し、同一メッセージID指定で同じキューからメッセージを GET して標準出力に出力する」というサンプルを Java で書いたコード片です。エラー処理などを削っていますが、それでも日本語で表現するのに比べてずいぶん長いコードを書かなければいけません。
MQQueueManager qm = new MQQueueManager("QM");
MQQueue queue = qm.accessQueue("postcard", MQC.MQOO_INPUT_AS_Q_DEF | MQC.MQOO_OUTPUT);
MQMessage msg = new MQMessage();
msg.writeString("Hello");
MQPutMessageOptions pmo = new MQPutMessageOptions();
queue.put(msg, pmo);
MQMessage retrievedMessage = new MQMessage();
retrievedMessage.messageId = msg.messageId;
MQGetMessageOptions gmo = new MQGetMessageOptions();
queue.get(retrievedMessage, gmo);
int len = retrievedMessage.getMessageLength();
byte[] b = new byte[len];
retrievedMessage.readFully(b);
System.out.println(new java.lang.String(b));
qm.disconnect();
これが Scala で以下のように書けたらずいぶんすっきりしますね。
import WebsphereMQ._
mqconn("QM") {
val msg = "postcard" ! "Hello"
mqget("postcard", 'messageId->msg.messageId) {
case m => Console.println(m: String)
}
}
これを実現するために作成したラッパーオブジェクトが以下のものです。
import com.ibm.mq._
object WebsphereMQ {
private val qMgr = new ThreadLocal
private val openOptions = new ThreadLocal { override def initialValue = int2Integer(MQC.MQOO_INPUT_AS_Q_DEF | MQC.MQOO_OUTPUT) }
class ExtendedQueue(queue: MQQueue) {
def ! (msg: MQMessage) = { queue.put(msg); msg }
def to_mqQueue = queue
}
def mqconn(qmName: String)(block: => Unit) = {
qMgr.set(new com.ibm.mq.MQQueueManager(qmName))
try {
block
} finally {
qMgr.get.asInstanceOf[MQQueueManager].disconnect
}
}
def mqget(queue: MQQueue, args: (Symbol, Any)*)(block: MQMessage => Unit) = {
val argmap = Map(args: _*).withDefaultValue(null)
val messageId = argmap('messageId).asInstanceOf[Array[Byte]]
val retrievedMessage = new MQMessage()
if (messageId != null) retrievedMessage.messageId = messageId
val gmo = new MQGetMessageOptions()
queue.get(retrievedMessage, gmo)
block(retrievedMessage)
}
implicit def str2mqq(qName: String): MQQueue =
qMgr.get.asInstanceOf[MQQueueManager].accessQueue(qName, openOptions.get.asInstanceOf[Int])
implicit def str2extq(qName: String): ExtendedQueue = {
new ExtendedQueue(str2mqq(qName))
}
implicit def mqq2extq(queue: MQQueue): ExtendedQueue = new ExtendedQueue(queue)
implicit def extq2mqq(queue: ExtendedQueue): MQQueue = queue.to_mqQueue
implicit def str2mqmsg(msg: String): MQMessage = {
val mqmsg = new MQMessage
mqmsg.writeString(msg)
mqmsg
}
implicit def mqmsg2str(msg: MQMessage): String = {
val len = msg.getMessageLength
val b = Array.make(len, 0:Byte)
msg.readFully(b)
new java.lang.String(b)
}
}
文字列からキューオブジェクトへの暗黙の変換はちょっとやりすぎかもしれませんが。
新興言語は最初の頃は「○×ライブラリへのバインディングを作成した」というような活動で盛り上がるのが一般的だと思うのですが Scala はなまじ Java ライブラリがそのまま使える分だけそういった傾向がないのがさびしいところです。しかし「Scala 風に使えるようにする」というところまで考えればそうしたことをやる余地はいくらでもあると思います。
この Websphere MQ 用ラッパーも需要があればきちんとした形にまとめたいですが、問題は Websphere MQ を使った開発と Scala と両方に興味があるような人が今世界中で何人いるだろうかということです。
コメント 0