New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exception to deserialize 'com.spotify.docker.client.messages.Event' #584

Closed
adalrsjr1 opened this Issue Jan 11, 2017 · 3 comments

Comments

Projects
None yet
3 participants
@adalrsjr1

adalrsjr1 commented Jan 11, 2017

Description

I'm trying to serialize events from Docker to send for a monitoring tool. I can serialized an event to json, but when I try to deserialize arise an exception : Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: For input string: "2017-01-11T06:43:34.000+0000" (through reference chain: com.spotify.docker.client.messages.Event["time"])

How to reproduce

package br.cin.gfads.adalrsjr1.docker_monitor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.spotify.docker.client.DefaultDockerClient;
import com.spotify.docker.client.DockerClient;
import com.spotify.docker.client.EventStream;
import com.spotify.docker.client.ObjectMapperProvider;
import com.spotify.docker.client.messages.Event;

public class App 
{
	private static final Logger log = LoggerFactory.getLogger(App.class);
	
    public static void main( String[] args ) throws Exception
    {
    	DockerClient docker = DefaultDockerClient.fromEnv().build();
    	DefaultDockerClient.builder()
    					   .uri("localhost:2376")
    					   .connectionPoolSize(1)
    	                                   .build();
    	
    	EventStream eventStream = docker.events();
    	while(eventStream.hasNext()) {
    		Event ev = eventStream.next();
    		
    		ObjectMapperProvider omp = new ObjectMapperProvider();
    		ObjectMapper mapper = omp.getContext(Event.class);
    		
    		String json = mapper.writerWithView(Event.class).writeValueAsString(ev);
    		
    		Event ev2 = mapper.getFactory().createParser(json).readValueAs(Event.class);
//    		Event ev2 = mapper.readerFor(Event.class).readValue(json); // also not working
    		log.info(ev2+"");
    	}
    	
    	docker.close();
    	
    }
}

What do you expect

I expect something like this:

Event{status=null, id=null, from=null, type=NETWORK, action=disconnect, actor=Actor{id=hvb6mpc8t4iu7j85vu27sb1n6, attributes={container=a2ceade7f529694723695256c5d04b135e5573f9b05c597448e1a8d171a22231, name=ingress, type=overlay}}, time=Wed Jan 11 03:52:33 GFT 2017, timeNano=1484117553094885100}

What happened instead

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: For input string: "2017-01-11T06:43:34.000+0000" (through reference chain: com.spotify.docker.client.messages.Event["time"])

Software:

  • docker version:
    Client:
    Version: 1.13.0-rc5
    API version: 1.25
    Go version: go1.7.3
    Git commit: 43cc971
    Built: Thu Jan 5 03:07:30 2017
    OS/Arch: windows/amd64

Server:
Version: 1.13.0-rc5
API version: 1.25 (minimum version 1.12)
Go version: go1.7.3
Git commit: 43cc971
Built: Thu Jan 5 03:07:30 2017
OS/Arch: linux/amd64
Experimental: true

  • Spotify's docker-client version: 7.0.0

Full backtrace

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: For input string: "2017-01-11T06:54:27.000+0000" (through reference chain: com.spotify.docker.client.messages.Event["time"])
	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:210)
	at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:177)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1475)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:463)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:377)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1100)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:294)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:131)
	at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:3674)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1996)
	at com.fasterxml.jackson.core.JsonParser.readValueAs(JsonParser.java:1511)
	at br.cin.gfads.adalrsjr1.docker_monitor.App.main(App.java:39)
Caused by: java.lang.NumberFormatException: For input string: "2017-01-11T06:54:27.000+0000"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Long.parseLong(Long.java:589)
	at java.lang.Long.parseLong(Long.java:631)
	at com.spotify.docker.client.jackson.UnixTimestampDeserializer.deserialize(UnixTimestampDeserializer.java:47)
	at com.spotify.docker.client.jackson.UnixTimestampDeserializer.deserialize(UnixTimestampDeserializer.java:36)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:520)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:461)
	... 8 more
@johnflavin

This comment has been minimized.

Show comment
Hide comment
@johnflavin

johnflavin Jan 11, 2017

Contributor

It looks to me like the problem is that we have a custom deserializer class for the Event.time field, but we do not have a corresponding serializer class. So you can read the events from docker just fine (Event.time is created from the unix timestamp), but when you serialize out to JSON the time field is written out to a String in whatever default way java Dates are written. When you then try to deserialize the object you just serialized out, the custom deserializer says "I don't recognize this time value. Doesn't look like a unix timestamp to me." and it throws an error.

In fact, this same behavior would happen on any object -> json -> object round trip if the object has a Date property.

To fix this, we would need to create a serializer class that takes Dates and writes them as unix timestamps, reversing the process of the deserializer. Everywhere in docker-client that currently uses the custom deserializer should also use this to-be-written custom serializer.

Contributor

johnflavin commented Jan 11, 2017

It looks to me like the problem is that we have a custom deserializer class for the Event.time field, but we do not have a corresponding serializer class. So you can read the events from docker just fine (Event.time is created from the unix timestamp), but when you serialize out to JSON the time field is written out to a String in whatever default way java Dates are written. When you then try to deserialize the object you just serialized out, the custom deserializer says "I don't recognize this time value. Doesn't look like a unix timestamp to me." and it throws an error.

In fact, this same behavior would happen on any object -> json -> object round trip if the object has a Date property.

To fix this, we would need to create a serializer class that takes Dates and writes them as unix timestamps, reversing the process of the deserializer. Everywhere in docker-client that currently uses the custom deserializer should also use this to-be-written custom serializer.

@mattnworb

This comment has been minimized.

Show comment
Hide comment
@mattnworb

mattnworb Jan 11, 2017

Member

I agree that it would make sense for the Event class, if it has a custom deserializer for the date field, to also attach serializer too, just in case anyone wants to go from Object -> JSON.

On the other hand, as a workaround, anyone trying to serialize these Events back to JSON could add their own serializer or customize the ObjectMapper instance themselves - I am surprised anyone would want to use the same ObjectMapperProvider we use internally.

Member

mattnworb commented Jan 11, 2017

I agree that it would make sense for the Event class, if it has a custom deserializer for the date field, to also attach serializer too, just in case anyone wants to go from Object -> JSON.

On the other hand, as a workaround, anyone trying to serialize these Events back to JSON could add their own serializer or customize the ObjectMapper instance themselves - I am surprised anyone would want to use the same ObjectMapperProvider we use internally.

@adalrsjr1

This comment has been minimized.

Show comment
Hide comment
@adalrsjr1

adalrsjr1 Jan 12, 2017

@mattnworb I'm using the same ObjectMapperProvider because I don't want need to implement a new one by myself. As the API have an object mapper why not use it?

I solved the problem by creating a custom DateSerializer as below:

class MyDateSerializer extends StdSerializer<Date> {
    protected MyDateSerialize(Class<Date> t) {
        super(t);
    }
    @Override
    public void serialize(Date value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeNumber(value.getTime());
    }
}

Thus, I just need to register MyDateSerializer for correctly desserialize an Event by using the native ObjectMapperProvider:

...

ObjectMapperProvider omp = new ObjectMapperProvider();
SimpleModule testModule = new SimpleModule("MyModule").addSerializer(Date.class, new MyDateSerializer(Date.class));

ObjectMapper mapper = omp.getContext(Event.class);
mapper.registerModule(testModule);

String json = mapper.writerWithView(Event.class).writeValueAsString(ev);
Event ev2 = mapper.getFactory().createParser(json).readValueAs(Event.class);

...

All are working fine now!

adalrsjr1 commented Jan 12, 2017

@mattnworb I'm using the same ObjectMapperProvider because I don't want need to implement a new one by myself. As the API have an object mapper why not use it?

I solved the problem by creating a custom DateSerializer as below:

class MyDateSerializer extends StdSerializer<Date> {
    protected MyDateSerialize(Class<Date> t) {
        super(t);
    }
    @Override
    public void serialize(Date value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeNumber(value.getTime());
    }
}

Thus, I just need to register MyDateSerializer for correctly desserialize an Event by using the native ObjectMapperProvider:

...

ObjectMapperProvider omp = new ObjectMapperProvider();
SimpleModule testModule = new SimpleModule("MyModule").addSerializer(Date.class, new MyDateSerializer(Date.class));

ObjectMapper mapper = omp.getContext(Event.class);
mapper.registerModule(testModule);

String json = mapper.writerWithView(Event.class).writeValueAsString(ev);
Event ev2 = mapper.getFactory().createParser(json).readValueAs(Event.class);

...

All are working fine now!

davidxia added a commit that referenced this issue Feb 17, 2017

davidxia added a commit that referenced this issue Feb 17, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment