xstream issue report

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

xstream issue report

Andrey Rahimov
Hello!

I found very weird and annoying behavior in xstream(1.4.7 and tried 1.4.5) library on Android.

Basically ClassCastException if not recreate XStream object whenever deserialize new type of annotated object. 

More details here: http://stackoverflow.com/questions/26778983/xstream-classcastexception-if-not-recreate-xstream-object-whenever-deserialize-n

Is here any possibility to avoid recreating xStream object every time?

Also even recreating xStream I'm getting this error approx. 4 crashes on 3000 users.

Can be the problem in our model? 

Regards, Andrey Rahimov
Reply | Threaded
Open this post in threaded view
|

Re: xstream issue report

Jörg Schaible-4
Hi Andrey,

Andrey Rahimov wrote:

> Hello!
>
> I found very weird and annoying behavior in xstream(1.4.7 and tried 1.4.5)
> library on Android.
>
> Basically ClassCastException if not recreate XStream object whenever
> deserialize new type of annotated object.
>
> More details here:
> http://stackoverflow.com/questions/26778983/xstream-classcastexception-if-not-recreate-xstream-object-whenever-deserialize-n
>
> Is here any possibility to avoid recreating xStream object every time?

There's normally no need to create a new instance every time. Normally, you
simply create the instance, set it up and use it then everywhere (even
concurrently).

However, setting up the XStream is *not* thread-safe, i.e. if you use auto-
detection for annotations or call processAnnotations, you will better use an
own instance for each thread. See also FAQ, Javadoc and the annotations
tutorial:

- http://xstream.codehaus.org/faq.html#Scalability_Thread_safety
-
http://xstream.codehaus.org/javadoc/com/thoughtworks/xstream/XStream.html#autodetectAnnotations(boolean)
- http://xstream.codehaus.org/annotations-tutorial.html#AutoDetect

> Also even recreating xStream I'm getting this error approx. 4 crashes on
> 3000 users.

You can get in trouble if you marshal/unmarshal system resources:

- http://xstream.codehaus.org/faq.html#Serialization_types

> Can be the problem in our model?

You call processAnnotations on-the-fly i.e. you change the setup of the
XStream instance. Every XStream instance builds internal caches during the
(un)marshalling process and - apart from the concurrency problem - you might
get surprising results, e.g. if an alias has been defined twice or a
converter instance is already used for a type, you can register something
different as long as you want, it won't have any effect.

However, since you're creating a new XStream instance yourself, you should
not be affected. But you realize that your code is not thread-safe also? You
create a new XStream instance and keep it in a member. Then you use that
member in processAnnotations again and later on in fromBody. If fromBoby is
called concurrently, you actually have no clue about which XStream instance
is used in which state at which time ... and your code silently assumes that
every type you have handled with toBody will use an XStream instance that
has marshalled the same type in fromBody already before in the same run.

BTW: Your processAnnotations method with the loop is superfluous, XStream
did the same already.

>
> Regards, Andrey Rahimov

Cheers,
Jörg


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|

Re: xstream issue report

Andrey Rahimov
Thank you for your help and deep response! 

We found the problem it was our object model every object in our model has 
@XStreamAlias("response")
for example
@XStreamAlias("response")
public class AGPSRoutePoint extends ARoutePoint
So now if I changing this to different names in unit-tests like @XstreamAllia("ping"), @XstreamAlias("geo") it working well. 

We have old backend and big base of Android/iOS users with old won't updated versions. The question is what approach you recommend us to use  to parse such xml?(Annotations will be most convenient way for us coz we have around 50 model classes with additional logic)

Cheers,
Andrey


On Tue, Nov 11, 2014 at 11:57 AM, Jörg Schaible <[hidden email]> wrote:
Hi Andrey,

Andrey Rahimov wrote:

> Hello!
>
> I found very weird and annoying behavior in xstream(1.4.7 and tried 1.4.5)
> library on Android.
>
> Basically ClassCastException if not recreate XStream object whenever
> deserialize new type of annotated object.
>
> More details here:
> http://stackoverflow.com/questions/26778983/xstream-classcastexception-if-not-recreate-xstream-object-whenever-deserialize-n
>
> Is here any possibility to avoid recreating xStream object every time?

There's normally no need to create a new instance every time. Normally, you
simply create the instance, set it up and use it then everywhere (even
concurrently).

However, setting up the XStream is *not* thread-safe, i.e. if you use auto-
detection for annotations or call processAnnotations, you will better use an
own instance for each thread. See also FAQ, Javadoc and the annotations
tutorial:

- http://xstream.codehaus.org/faq.html#Scalability_Thread_safety
-
http://xstream.codehaus.org/javadoc/com/thoughtworks/xstream/XStream.html#autodetectAnnotations(boolean)
- http://xstream.codehaus.org/annotations-tutorial.html#AutoDetect

> Also even recreating xStream I'm getting this error approx. 4 crashes on
> 3000 users.

You can get in trouble if you marshal/unmarshal system resources:

- http://xstream.codehaus.org/faq.html#Serialization_types

> Can be the problem in our model?

You call processAnnotations on-the-fly i.e. you change the setup of the
XStream instance. Every XStream instance builds internal caches during the
(un)marshalling process and - apart from the concurrency problem - you might
get surprising results, e.g. if an alias has been defined twice or a
converter instance is already used for a type, you can register something
different as long as you want, it won't have any effect.

However, since you're creating a new XStream instance yourself, you should
not be affected. But you realize that your code is not thread-safe also? You
create a new XStream instance and keep it in a member. Then you use that
member in processAnnotations again and later on in fromBody. If fromBoby is
called concurrently, you actually have no clue about which XStream instance
is used in which state at which time ... and your code silently assumes that
every type you have handled with toBody will use an XStream instance that
has marshalled the same type in fromBody already before in the same run.

BTW: Your processAnnotations method with the loop is superfluous, XStream
did the same already.

>
> Regards, Andrey Rahimov

Cheers,
Jörg


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email



Reply | Threaded
Open this post in threaded view
|

Re: Re: xstream issue report

Jörg Schaible-4
Hi Andrey,

Andrey Rahimov wrote:

> Thank you for your help and deep response!
>
> We found the problem it was our object model every object in our model has
>
> @XStreamAlias("response")
>
> for example
>
> @XStreamAlias("response")
> public class AGPSRoutePoint extends ARoutePoint
>
> So now if I changing this to different names in unit-tests like
> @XstreamAllia("ping"), @XstreamAlias("geo") it working well.
>
> We have old backend and big base of Android/iOS users with old won't
> updated versions. The question is what approach you recommend us to use
> to parse such xml?(Annotations will be most convenient way for us coz we
> have around 50 model classes with additional logic)

Your objects seemed to have a common parent (or implement a common
interface) TypedInput. In this case you might define now an alias "response"
for TypedInput and an own converter handling such a general TypedInput.

You will now have to tell this converter what type it actually should
convert. You can "talk" to it using a DataHolder (it's a very simple map).
Instead of

  fromXML = xstream.fromXML(bodyString);

You should invoke XStream with something like:

  DataHolder dataHolder = xstream.newDataHolder();
  dataHolder.put(Converter.class, this.getClass());
  InputStream in = new ByteArrayInputStream(bodyString.getBytes("utf-8"));
  fromXML = xstream.unmarshal(driver.createReader(in), null, dataHolder);

"driver" is an instance, you may keep along to the XStream:

  Driver driver = new XppDriver(); // XStream default
  XStream xstream = new XStream(driver);

The generic TypedInput converter should look like:

  class TypedInputConverter implements Converter {
    ConverterLookup lookup;
    TypedInputConverter(ConverterLookup lookup) {
      this.lookup = lookup;
    }
    boolean canConvert(Class type) {
      return type == TypedInput.class;
    }
    void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
      throw ConverterException("Marshalling unsupported");
    }
    Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext
context) {
      Class realType = context.get(Converter.class);
      Converter converter = lookup.lookupConverterFor(realType);
      return converter.unmarshal(reader, context);
    }
  }


This converter must have been registered:


  xstream.registerConverter(new
TypedInputConverter(xstream.getConverterLookup()));


This approach assumes that you no longer use the "response" alias for
something else and that you do not (un)marshal direct instances of
TypedInput. Otherwise you must give more information to your class
hierarchy.

Cheers,
Jörg


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email