ドキュメントに、Hot Rod Clientではいくつか未サポートのメソッドがあるよと書いてあったので、ちょっと試してみることにしました。
https://docs.jboss.org/author/display/ISPN/Java+Hot+Rod+client#JavaHotRodclient-Unsupportedmethods
対象は、
boolean remove(Object key, Object value); boolean replace(Object key, Object value); boolean replace(Object key, Object oldValue, Object value);
ということらしいです。使うとUnsupportedOperationExceptionが飛んでくるんだとか。
では、ちょっと試してみましょう。まずはEmbedded Modeから。
src/main/scala/EmbeddedClient.scala
import org.infinispan.manager.DefaultCacheManager object EmbeddedClient { def main(args: Array[String]): Unit = { val manager = new DefaultCacheManager val cache = manager.getCache[String, Integer] cache.put("key1", 1) println("put-1 => " + cache.get("key1")) cache.put("key1", 2) println("put-2 => " + cache.get("key1")) cache.remove("key1") println("remove => " + cache.get("key1")) cache.put("key1", 2) println("put-2-2 => " + cache.get("key1")) cache.remove("key1", 2) println("remove value => " + cache.get("key1")) cache.put("key1", 3) println("put-3 => " + cache.get("key1")) cache.remove("key1", 1) println("remove value, ng pattern => " + cache.get("key1")) cache.replace("key1", 3, 4) println("replace old, new => " + cache.get("key1")) cache.replace("key1", 3, 5) println("replace old, new, ng pattern => " + cache.get("key1")) cache.replace("key1", 6) println("replace => " + cache.get("key1")) } }
実行。
[info] put-1 => 1 [info] put-2 => 2 [info] remove => null [info] put-2-2 => 2 [info] remove value => null [info] put-3 => 3 [info] remove value, ng pattern => 3 [info] replace old, new => 4 [info] replace old, new, ng pattern => 4 [info] replace => 6
まあ、普通に動きますね。
では、これをHot Rodを使うように修正して試してみます。
src/main/scala/HotRodClient.scala
import scala.util.Try import org.infinispan.client.hotrod.{Flag, RemoteCacheManager, VersionedValue} object HotRodClient { def main(args: Array[String]): Unit = { val manager = new RemoteCacheManager("localhost") val cache = manager.getCache[String, Integer]() cache.put("key1", 1) println("put-1 => " + cache.get("key1")) cache.put("key1", 2) println("put-2 => " + cache.get("key1")) cache.remove("key1") println("remove => " + cache.get("key1")) cache.put("key1", 2) println("put-2-2 => " + cache.get("key1")) println(Try { cache.remove("key1", 2) }) println("remove value => " + cache.get("key1")) cache.put("key1", 3) println("put-3 => " + cache.get("key1")) println(Try { cache.remove("key1", 1) }) println("remove value, ng pattern => " + cache.get("key1")) println(Try { cache.replace("key1", 3, 4) }) println("replace old, new => " + cache.get("key1")) println(Try { cache.replace("key1", 3, 5) }) println("replace old, new, ng pattern => " + cache.get("key1")) cache.replace("key1", 6) println("replace => " + cache.get("key1")) } }
未サポートというメソッドは、Tryで囲っています。では、Infinispanを起動して
$ $ISPN_HOME/bin/startServer.sh -r hotrod
実行。
[info] put-1 => 1 [info] put-2 => 2 [info] remove => null [info] put-2-2 => 2 [info] Failure(java.lang.UnsupportedOperationException) [info] remove value => 2 [info] put-3 => 3 [info] Failure(java.lang.UnsupportedOperationException) [info] remove value, ng pattern => 3 [info] Failure(java.lang.UnsupportedOperationException) [info] replace old, new => 3 [info] Failure(java.lang.UnsupportedOperationException) [info] replace old, new, ng pattern => 3 [info] replace => 6
確かに、UnsupportedOperationExceptionが飛んできてますね。
ところで
boolean replace(Object key, Object value);
は未サポートとドキュメントに書いている割には、動いているような…。ドキュメントが間違ってる??
他にもreplaceAsyncとかの一部が使えませんが、
http://docs.jboss.org/infinispan/5.2/apidocs/org/infinispan/client/hotrod/RemoteCache.html
要は「前の値を指定して削除する、または置き換える」という操作を未サポートだよ、ということみたいですね。
で、この操作は全くできないかというと、古い値を指定することはできないみたいですが、データのバージョンを指定することはできるのでそちらで代替して欲しいみたいです。
https://docs.jboss.org/author/display/ISPN/Java+Hot+Rod+client#JavaHotRodclient-VersionedAPI
では、動いていなかった部分をVersionedValueを使うように書き直してみます。ドキュメントは、
RemoteCache.VersionedValue valueBinary = remoteCache.getVersioned("car");
みたいなRemoteCacheのインナークラスみたいな書きっぷりになっていますが、実際はorg.infinispan.client.hotrodパッケージのトップレベルのインターフェースになっています。
src/main/scala/HotRodClient.scala
import scala.util.Try import org.infinispan.client.hotrod.{Flag, RemoteCacheManager, VersionedValue} object HotRodClient { def main(args: Array[String]): Unit = { val manager = new RemoteCacheManager("localhost") val cache = manager.getCache[String, Integer]() cache.put("key1", 1) println("put-1 => " + cache.get("key1")) cache.put("key1", 2) println("put-2 => " + cache.get("key1")) cache.remove("key1") println("remove => " + cache.get("key1")) cache.put("key1", 2) println("put-2-2 => " + cache.get("key1")) var version = cache.getVersioned("key1") println(s"current value => ${version.getValue}, current version ${version.getVersion}") cache.removeWithVersion("key1", version.getVersion) println("removeWithVersion value => " + cache.get("key1")) cache.put("key1", 3) println("put-3 => " + cache.get("key1")) version = cache.getVersioned("key1") println(s"current value => ${version.getValue}, current version ${version.getVersion}") cache.removeWithVersion("key1", 1) // 適当なバージョンを指定 println("removeWithVersion value, ng pattern => " + cache.get("key1")) version = cache.getVersioned("key1") println(s"current value => ${version.getValue}, current version ${version.getVersion}") cache.replaceWithVersion("key1", 4, version.getVersion) println("replaceWithVersion old, new => " + cache.get("key1")) version = cache.getVersioned("key1") println(s"current value => ${version.getValue}, current version ${version.getVersion}") cache.replaceWithVersion("key1", 5, 1) // 適当なバージョンを指定 println("replaceWithVersion old, new, ng pattern => " + cache.get("key1")) cache.replace("key1", 6) println("replace => " + cache.get("key1")) } }
RemoteCache#removeWithVersion、RemoteCache#replaceWithVersionを使用するようになりました。
1度Infinispan Serverを再起動して、実行。
[info] put-2 => 2 [info] remove => null [info] put-2-2 => 2 [info] current value => 2, current version 3 [info] removeWithVersion value => null [info] put-3 => 3 [info] current value => 3, current version 4 [info] removeWithVersion value, ng pattern => 3 [info] current value => 3, current version 4 [info] replaceWithVersion old, new => 4 [info] current value => 4, current version 5 [info] replaceWithVersion old, new, ng pattern => 4 [info] replace => 6
今度は動きました。
RemoteCache#getではなくて、RemoteCache#getVersionedを使うことで、取得したデータとバージョンを一緒に取れるようです。
で、バージョンを要求するメソッドの引数に一緒に渡し、もしバージョンが一致しなかったらその操作は失敗、というか無視される挙動をします。
cache.replaceWithVersion("key1", 5, 1) // 適当なバージョンを指定
その他、普通のMapと違う挙動をするものとしては、
V remove(Object key); V put(K key, V value);
が前の値を返さない、というものがあるそうな。
そうなん?
Embedded Modeのサンプルをちょっと変えてみて
val manager = new DefaultCacheManager val cache = manager.getCache[String, Integer] println("put-1 return => " + cache.put("key1", 1)) println("put-1 => " + cache.get("key1")) println("put-2 return => " + cache.put("key1", 2)) println("put-2 => " + cache.get("key1")) println("remove return => " + cache.remove("key1")) println("remove => " + cache.get("key1"))
実行。
[info] put-1 return => null [info] put-1 => 1 [info] put-2 return => 1 [info] put-2 => 2 [info] remove return => 2 [info] remove => null
こっちは、値が返ってきますね。
では、Hot Rodの方だと
val manager = new RemoteCacheManager("localhost") val cache = manager.getCache[String, Integer]() println("put-1 return => " + cache.put("key1", 1)) println("put-1 => " + cache.get("key1")) println("put-2 return => " + cache.put("key1", 2)) println("put-2 => " + cache.get("key1")) println("remove return => " + cache.remove("key1")) println("remove => " + cache.get("key1"))
実行。
[info] put-1 return => null [info] put-1 => 1 [info] put-2 return => null [info] put-2 => 2 [info] remove return => null [info] remove => null
確かに返ってきません…。
この挙動を変更するには、2つ方法があるようです。
https://docs.jboss.org/author/display/ISPN/Java+Hot+Rod+client#JavaHotRodclient-Returnvalues
まずは、RemoteCache#withFlagsを使用するように変更します。
// Flagは、以下のパッケージ import org.infinispan.client.hotrod.{Flag, RemoteCacheManager, VersionedValue} val manager = new RemoteCacheManager("localhost") val cache = manager.getCache[String, Integer]() println("put-1 return => " + cache.withFlags(Flag.FORCE_RETURN_VALUE).put("key1", 1)) println("put-1 => " + cache.get("key1")) println("put-2 return => " + cache.withFlags(Flag.FORCE_RETURN_VALUE).put("key1", 2)) println("put-2 => " + cache.get("key1")) println("remove return => " + cache.withFlags(Flag.FORCE_RETURN_VALUE).remove("key1")) println("remove => " + cache.get("key1"))
実行。
[info] put-1 return => 6 [info] put-1 => 1 [info] put-2 return => 1 [info] put-2 => 2 [info] remove return => 2 [info] remove => null
RemoteCache#withFlagsはRemoteCacheを返すので、それに対してputやremoveを実行します。ただし、この方法は最初の1回目のメソッド呼び出しにしか効果がありません。
よって、こういう使い方をしても、最初の1回しか効きません。
val manager = new RemoteCacheManager("localhost") val cache = manager.getCache[String, Integer]().withFlags(Flag.FORCE_RETURN_VALUE) println("put-1 return => " + cache.put("key1", 1)) // => 戻り値があるのは、ここだけ println("put-1 => " + cache.get("key1")) println("put-2 return => " + cache.put("key1", 2)) println("put-2 => " + cache.get("key1")) println("remove return => " + cache.remove("key1")) println("remove => " + cache.get("key1"))
デフォルトの挙動として、戻り値を返すようにする場合はRemoteCacheManagerに明示的に設定します。
http://docs.jboss.org/infinispan/5.2/apidocs/org/infinispan/client/hotrod/RemoteCacheManager.html
import java.util.Properties val properties = new Properties properties.put("infinispan.client.hotrod.server_list", "localhost:11222") properties.put("infinispan.client.hotrod.force_return_values", "true") val manager = new RemoteCacheManager(properties) val cache = manager.getCache[String, Integer]() println("put-1 return => " + cache.put("key1", 1)) println("put-1 => " + cache.get("key1")) println("put-2 return => " + cache.put("key1", 2)) println("put-2 => " + cache.get("key1")) println("remove return => " + cache.remove("key1")) println("remove => " + cache.get("key1"))
これで、どのput/removeも値を返すようになります。