bigdata4u / spymemcached

Automatically exported from code.google.com/p/spymemcached
0 stars 0 forks source link

Booleans contained in Clojure maps deserialized as new instances of java.lang.Boolean - breaks "if" #320

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
What version of the product are you using? On what operating system?

Using in Clojure, on a Mac, via the https://github.com/clojurewerkz/spyglass 
project. Their currently released versions use version 2.8.10.

Tell me more...

Apologies that this is a Clojure example, but I'm guessing it must also affect 
other Java nested structures. My Java isn't hot enough to work out the culprit 
though.

If you persist a Clojure map that contains a boolean into memcached, when you 
read it in, the boolean is deserialized as a new instance of java.lang.Boolean 
- so a false value does not have object identity to the false literal, which 
breaks if expression etc. This doesn't happen when you persist just a boolean 
by itself - so it seems to be something to do with the 
serialization/deserialization of a datastructure that *contains* a boolean.

Here's a REPL session to illustrate what I mean:

user=> (.set c "abc" 3000 {:superuser false})
#<OperationFuture net.spy.memcached.internal.OperationFuture@2b66969c>

user=> (def user (.get c "abc"))
#'user/user

user=> user
{:superuser false}

user=> (when (:superuser user) (println "Do very secure thing!"))
Do very secure thing!
nil

user=> (class (:superuser user))
java.lang.Boolean

user=> (identical? (:superuser user) false)
false

user=> (.set c "abc" 3000 false)
#<OperationFuture net.spy.memcached.internal.OperationFuture@3bba6d59>

user=> (.get c "abc" )
false

user=> (identical? (.get c "abc" ) false)
true

Original issue reported on code.google.com by russ...@russelldunphy.com on 14 Apr 2015 at 5:00

GoogleCodeExporter commented 8 years ago
Ok this seems quite fundamental so I'm assuming it's my lack of Java knowledge 
and is known, if strange, behaviour, rather than a bug. But as far as I can 
see, SpyMemcached uses an ObjectInputStream/OutputStream to 
serialize/deserialize any java objects it is sent (I'm looking in 
BaseSerializingTranscoder.java, lines 95-144).

So I tried serializing a java.util.HashMap containing a boolean value using an 
ObjectInputStream, then deserialized it using an ObjectOutputStream, and got 
the same problem - a new instance of java.lang.Boolean is created. Here's my 
REPL session (again, apologies for the non-Java):

user=> (def bos (ByteArrayOutputStream.))
#'user/bos

user=> (def os (ObjectOutputStream. bos))
#'user/os

user=> (def hm (HashMap.))
#'user/hm

user=> (.put hm "abc" false)
nil

user=> (.get hm "abc")
false

user=> (identical? false (.get hm "abc"))
true

user=> (.writeObject os hm)
nil

user=> (def ba (.toByteArray bos))
#'user/ba

user=> ba
#<byte[] [B@737e2e57>

user=> (def bis (ByteArrayInputStream. ba))
#'user/bis

user=> (def is (ObjectInputStream. bis))
#'user/is

user=> (def newhm (.readObject is))
#'user/newhm

user=> (.get newhm "abc")
false

user=> (identical? false (.get newhm "abc"))
false

Original comment by russ...@russelldunphy.com on 14 Apr 2015 at 5:59