Repository: docker-java/docker-java (java)

Issue:
Roundtrip of InspectContainerResponse fails (VolumeBinds)
Seems <code>@JsonIgnore</code> does not work as expected.

Reproduction steps (use <code>ObjectMapper</code>):
1) Import <code>InspectContainerResponse</code> from an external <code>docker inspect containerId</code> output
2) Export <code>InspectContainerResponse</code> to a string
3) Import <code>InspectContainerResponse</code> again. An exception happens 

Error happens during the serialization of InspectContainerResponse.volumes:
* Expected JSON output: <code>"Volumes":{}</code>
* Actual JSON output: <code>"Volumes":{"binds":[]}</code>

Error could be handled on deserialization side as well.

Exception:
```
Expected path for 'binds'in host but got '[]'.
 at [Source: java.io.StringReader@3c07e830; line: 1, column: 2244] (through reference chain: com.github.dockerjava.api.command.InspectContainerResponse["Volumes"])
com.fasterxml.jackson.databind.JsonMappingException
 at [Source: java.io.StringReader@3c07e830; line: 1, column: 2244] (through reference chain: com.github.dockerjava.api.command.InspectContainerResponse["Volumes"])
	at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164)
	at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:608)
	at com.github.dockerjava.api.model.VolumeBinds$Deserializer.deserialize(VolumeBinds.java:43)
	at com.github.dockerjava.api.model.VolumeBinds$Deserializer.deserialize(VolumeBinds.java:31)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:375)
	at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:107)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:308)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
	at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:147)
	at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:18)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2797)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1943)
	at 
```

Pull request:
Extra Serializer classes for VolumeBinds and VolumesRW
Resolves #211 by introducing missing serializers.
The pull request also provides a simple framework for data object roundtrip tests.

@marcuslinke @reviewbybees

Patch:
diff --git a/src/main/java/com/github/dockerjava/api/model/VolumeBinds.java b/src/main/java/com/github/dockerjava/api/model/VolumeBinds.java
index 62ebc118..e23ddc9e 100644
--- a/src/main/java/com/github/dockerjava/api/model/VolumeBinds.java
+++ b/src/main/java/com/github/dockerjava/api/model/VolumeBinds.java
@@ -1,5 +1,6 @@
 package com.github.dockerjava.api.model;
 
+import com.fasterxml.jackson.core.JsonGenerator;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -12,11 +13,15 @@ import com.fasterxml.jackson.core.ObjectCodec;
 import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JsonDeserializer;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.fasterxml.jackson.databind.node.NullNode;
 
 // This is not going to be serialized
 @JsonDeserialize(using = VolumeBinds.Deserializer.class)
+@JsonSerialize(using = VolumeBinds.Serializer.class)
 public class VolumeBinds {
     private final VolumeBind[] binds;
 
@@ -28,6 +33,18 @@ public class VolumeBinds {
         return binds;
     }
 
+    public static final class Serializer extends JsonSerializer<VolumeBinds> {
+
+        @Override
+        public void serialize(VolumeBinds value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
+            jgen.writeStartObject();
+            for (final VolumeBind bind : value.binds) {
+                jgen.writeStringField(bind.getContainerPath(), bind.getHostPath());
+            }
+            jgen.writeEndObject();
+        }    
+    }
+    
     public static final class Deserializer extends JsonDeserializer<VolumeBinds> {
         @Override
         public VolumeBinds deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
diff --git a/src/main/java/com/github/dockerjava/api/model/VolumesRW.java b/src/main/java/com/github/dockerjava/api/model/VolumesRW.java
index 223f518c..a898ed57 100644
--- a/src/main/java/com/github/dockerjava/api/model/VolumesRW.java
+++ b/src/main/java/com/github/dockerjava/api/model/VolumesRW.java
@@ -1,5 +1,6 @@
 package com.github.dockerjava.api.model;
 
+import com.fasterxml.jackson.core.JsonGenerator;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -12,10 +13,14 @@ import com.fasterxml.jackson.core.ObjectCodec;
 import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JsonDeserializer;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.fasterxml.jackson.databind.node.NullNode;
 
 // This is not going to be serialized
+@JsonSerialize(using = VolumesRW.Serializer.class)
 @JsonDeserialize(using = VolumesRW.Deserializer.class)
 public class VolumesRW {
     private final VolumeRW[] volumesRW;
@@ -28,6 +33,19 @@ public class VolumesRW {
         return volumesRW;
     }
 
+    public static final class Serializer extends JsonSerializer<VolumesRW> {
+
+        @Override
+        public void serialize(VolumesRW value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
+            jgen.writeStartObject();
+            for (final VolumeRW volumeRW : value.volumesRW) {
+                jgen.writeBooleanField(volumeRW.getVolume().getPath(), volumeRW.getAccessMode().toBoolean());
+            }
+            jgen.writeEndObject();
+        }
+        
+    }
+    
     public static final class Deserializer extends JsonDeserializer<VolumesRW> {
         @Override
         public VolumesRW deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {


Test patch:
diff --git a/src/test/java/com/github/dockerjava/api/command/CommandJSONSamples.java b/src/test/java/com/github/dockerjava/api/command/CommandJSONSamples.java
new file mode 100644
index 00000000..500919fd
--- /dev/null
+++ b/src/test/java/com/github/dockerjava/api/command/CommandJSONSamples.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 CloudBees Inc., Oleg Nenashev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.github.dockerjava.api.command;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.dockerjava.test.serdes.AbstractJSONResourceRef;
+import com.github.dockerjava.test.serdes.JSONResourceRef;
+import java.io.IOException;
+import java.io.InputStream;
+import org.apache.commons.io.IOUtils;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+/**
+ * References test resources and provides basic tests functionality.
+ * @author Oleg Nenashev
+ */
+public enum CommandJSONSamples implements JSONResourceRef {
+    
+    inspectContainerResponse_full,
+    inspectContainerResponse_empty;
+    
+    @Override
+    public String getFileName() {
+        return this + ".json";
+    } 
+
+    @Override
+    public Class<?> getResourceClass() {
+        return CommandJSONSamples.class;
+    }  
+}
diff --git a/src/test/java/com/github/dockerjava/api/command/InspectContainerResponseTest.java b/src/test/java/com/github/dockerjava/api/command/InspectContainerResponseTest.java
new file mode 100644
index 00000000..2e294b14
--- /dev/null
+++ b/src/test/java/com/github/dockerjava/api/command/InspectContainerResponseTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 CloudBees Inc., Oleg Nenashev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.github.dockerjava.api.command;
+
+import static com.github.dockerjava.test.serdes.JSONTestHelper.testRoundTrip;
+import java.io.IOException;
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/**
+ * Tests for {@link InspectContainerResponse}.
+ * @author Oleg Nenashev
+ */
+public class InspectContainerResponseTest {
+    
+    @Test
+    public void roundTrip_full() throws IOException {
+        InspectContainerResponse[] responses = testRoundTrip(
+                        CommandJSONSamples.inspectContainerResponse_full, 
+                        InspectContainerResponse[].class);   
+        assertEquals(1, responses.length);
+        final InspectContainerResponse response = responses[0];
+        
+        // Check volumes: https://github.com/docker-java/docker-java/issues/211
+        assertEquals(response.getVolumes().length, 2);
+        assertEquals(response.getVolumesRW().length, 2);
+        assertEquals(response.getVolumes()[1].getContainerPath(), "/bar/foo/myvol2");
+        assertEquals(response.getVolumes()[1].getHostPath(), "/path2");
+        assertEquals(response.getVolumesRW()[1].getVolume().getPath(), "/bar/foo/myvol2");
+        assertFalse(response.getVolumesRW()[1].getAccessMode().toBoolean());
+        assertTrue(response.getVolumesRW()[0].getAccessMode().toBoolean());
+    }
+    
+    @Test
+    public void roundTrip_empty() throws IOException {
+        testRoundTrip(CommandJSONSamples.inspectContainerResponse_empty, InspectContainerResponse[].class);
+    }
+}
diff --git a/src/test/java/com/github/dockerjava/core/command/InspectExecCmdImplTest.java b/src/test/java/com/github/dockerjava/core/command/InspectExecCmdImplTest.java
index 2deddca0..f1270f4e 100644
--- a/src/test/java/com/github/dockerjava/core/command/InspectExecCmdImplTest.java
+++ b/src/test/java/com/github/dockerjava/core/command/InspectExecCmdImplTest.java
@@ -3,8 +3,11 @@ package com.github.dockerjava.core.command;
 import com.github.dockerjava.api.DockerException;
 import com.github.dockerjava.api.command.CreateContainerResponse;
 import com.github.dockerjava.api.command.ExecCreateCmdResponse;
+import com.github.dockerjava.api.command.InspectContainerResponse;
 import com.github.dockerjava.api.command.InspectExecResponse;
 import com.github.dockerjava.client.AbstractDockerClientTest;
+import com.github.dockerjava.test.serdes.JSONTestHelper;
+import java.io.IOException;
 
 import org.testng.ITestResult;
 import org.testng.annotations.*;
@@ -41,7 +44,7 @@ public class InspectExecCmdImplTest extends AbstractDockerClientTest {
     }
 
     @Test(groups = "ignoreInCircleCi")
-    public void inspectExecTest() {
+    public void inspectExecTest() throws IOException {
         String containerName = "generated_" + new SecureRandom().nextInt();
 
         CreateContainerResponse container = dockerClient
@@ -93,5 +96,9 @@ public class InspectExecCmdImplTest extends AbstractDockerClientTest {
         InspectExecResponse third = dockerClient.inspectExecCmd(checkFileCmdCreateResponse.getId()).exec();
         assertThat(third.getExitCode(), is(0));
 
+        // Get container info and check its roundtrip to ensure the consistency
+        InspectContainerResponse containerInfo = dockerClient.inspectContainerCmd(container.getId()).exec();
+        assertEquals(containerInfo.getId(), container.getId());
+        JSONTestHelper.testRoundTrip(containerInfo);
     }
 }
diff --git a/src/test/java/com/github/dockerjava/test/serdes/AbstractJSONResourceRef.java b/src/test/java/com/github/dockerjava/test/serdes/AbstractJSONResourceRef.java
new file mode 100644
index 00000000..882b3b01
--- /dev/null
+++ b/src/test/java/com/github/dockerjava/test/serdes/AbstractJSONResourceRef.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 CloudBees Inc., Oleg Nenashev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.github.dockerjava.test.serdes;
+
+/**
+ * Default implementation of the Resource reference.
+ * @author Oleg Nenashev
+ */
+public abstract class AbstractJSONResourceRef implements JSONResourceRef {
+    /**
+     * Gets a class which stores resources.
+     * @return Reference class by default.
+     */
+    @Override
+    public Class<?> getResourceClass() {
+        return this.getClass();
+    }   
+}
diff --git a/src/test/java/com/github/dockerjava/test/serdes/JSONResourceRef.java b/src/test/java/com/github/dockerjava/test/serdes/JSONResourceRef.java
new file mode 100644
index 00000000..96e5df72
--- /dev/null
+++ b/src/test/java/com/github/dockerjava/test/serdes/JSONResourceRef.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015 Oleg Nenashev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.github.dockerjava.test.serdes;
+
+import java.io.IOException;
+
+/**
+ * References JSON resources, which 
+ * @author Oleg Nenashev
+ */
+public interface JSONResourceRef {
+
+    /**
+     * Gets the resource file name under the class.
+     * @return File name, which is stored under the resource class
+     */
+    String getFileName();
+
+    /**
+     * Gets a class which stores resources.
+     * @return Class to be used as a resource source
+     */
+    Class<?> getResourceClass();    
+}
diff --git a/src/test/java/com/github/dockerjava/test/serdes/JSONTestHelper.java b/src/test/java/com/github/dockerjava/test/serdes/JSONTestHelper.java
new file mode 100644
index 00000000..b465b2aa
--- /dev/null
+++ b/src/test/java/com/github/dockerjava/test/serdes/JSONTestHelper.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2015 Oleg Nenashev.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.github.dockerjava.test.serdes;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.dockerjava.api.command.CommandJSONSamples;
+import java.io.IOException;
+import java.io.InputStream;
+import org.apache.commons.io.IOUtils;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+/**
+ * Provides helper methods for serialization-deserialization tests
+ * @author Oleg Nenashev
+ * @since TODO
+ */
+public class JSONTestHelper {
+    
+    /**
+     * Reads JSON String from the specified resource
+     * @param resource JSON File
+     * @return JSON String 
+     * @throws IOException JSON Conversion error 
+     */
+    public static String readString(JSONResourceRef resource) throws IOException {
+        InputStream istream = CommandJSONSamples.class.getResourceAsStream(resource.getFileName());
+        if (istream == null) {
+            throw new IOException("Cannot retrieve resource " + resource.getFileName());
+        }
+        return IOUtils.toString(istream, "UTF-8");
+    }
+    
+    /**
+     * Reads item from the resource.
+     * @param <TClass> Data class to be read
+     * @param resource Resource reference
+     * @param tclass Class entry
+     * @return Item
+     * @throws IOException JSON conversion error
+     */
+    public static <TClass> TClass readObject(JSONResourceRef resource, Class<TClass> tclass) throws IOException {
+        ObjectMapper mapper = new ObjectMapper();
+        String str = readString(resource);
+        return mapper.readValue(str, tclass);
+    }
+    
+    /**
+     * Basic serialization-deserialization consistency test for the resource.
+     * @param <TClass> Data class
+     * @param resource Resource reference
+     * @param tclass Class entry
+     * @throws IOException JSON conversion error
+     * @throws AssertionError Validation error
+     * @return Deserialized object after the roundtrip
+     */
+    public static <TClass> TClass testRoundTrip(JSONResourceRef resource, Class<TClass> tclass) 
+            throws IOException, AssertionError {
+        TClass item = readObject(resource, tclass);
+        assertNotNull(item);
+        return testRoundTrip(item, tclass);
+    }
+    
+    /**
+     * Performs roundtrip test for the specified class.
+     * @param <TClass> Item class
+     * @param item Item to be checked
+     * @return Deserialized object after the roundtrip
+     * @throws IOException JSON Conversion error
+     * @throws AssertionError Validation error
+     */
+    @SuppressWarnings("unchecked")
+    public static <TClass> TClass testRoundTrip(TClass item) 
+            throws IOException, AssertionError {
+        return testRoundTrip(item, (Class<TClass>)item.getClass());
+    }
+    
+    /**
+     * Performs roundtrip test for the specified class.
+     * @param <TClass> Item class
+     * @param item Item to be checked
+     * @param asclass Class to be used during conversions
+     * @return Deserialized object after the roundtrip
+     * @throws IOException JSON Conversion error
+     * @throws AssertionError Validation error
+     */
+    public static <TClass> TClass testRoundTrip(TClass item, Class<TClass> asclass) 
+            throws IOException, AssertionError {
+        ObjectMapper mapper = new ObjectMapper();
+       
+        String inputItemString = mapper.writeValueAsString(item);
+        TClass convertedItem = mapper.readValue(inputItemString, asclass);
+        String convertedItemString = mapper.writeValueAsString(convertedItem);
+        assertEquals(convertedItemString, inputItemString, "JSONs must be equal after the second roundtrip");
+        return convertedItem;
+    }
+}
diff --git a/src/test/resources/com/github/dockerjava/api/command/inspectContainerResponse_empty.json b/src/test/resources/com/github/dockerjava/api/command/inspectContainerResponse_empty.json
new file mode 100644
index 00000000..ea31d6f3
--- /dev/null
+++ b/src/test/resources/com/github/dockerjava/api/command/inspectContainerResponse_empty.json
@@ -0,0 +1,115 @@
+[{
+    "AppArmorProfile": "",
+    "Args": [ ],
+    "Config": {
+        "AttachStderr": true,
+        "AttachStdin": true,
+        "AttachStdout": true,
+        "Cmd": [ ],
+        "CpuShares": 0,
+        "Cpuset": "",
+        "Domainname": "",
+        "Entrypoint": null,
+        "Env": [ ],
+        "ExposedPorts": { },
+        "Hostname": "469e5edd8d5b",
+        "Image": "jenkinsci/workflow-demo",
+        "Labels": {},
+        "MacAddress": "",
+        "Memory": 0,
+        "MemorySwap": 0,
+        "NetworkDisabled": false,
+        "OnBuild": null,
+        "OpenStdin": true,
+        "PortSpecs": null,
+        "StdinOnce": true,
+        "Tty": true,
+        "User": "",
+        "Volumes": null,
+        "WorkingDir": "/var/lib/jenkins/workflow-plugin-pipeline-demo"
+    },
+    "Created": "2015-04-29T11:55:42.968262967Z",
+    "Driver": "aufs",
+    "ExecDriver": "native-0.2",
+    "ExecIDs": null,
+    "HostConfig": {
+        "Binds": null,
+        "CapAdd": null,
+        "CapDrop": null,
+        "CgroupParent": "",
+        "ContainerIDFile": "",
+        "CpuShares": 0,
+        "CpusetCpus": "",
+        "Devices": [],
+        "Dns": null,
+        "DnsSearch": null,
+        "ExtraHosts": null,
+        "IpcMode": "",
+        "Links": null,
+        "LogConfig": {
+            "Config": null,
+            "Type": "json-file"
+        },
+        "LxcConf": [],
+        "Memory": 0,
+        "MemorySwap": 0,
+        "NetworkMode": "bridge",
+        "PidMode": "",
+        "PortBindings": {
+            "8080/tcp": [ ]
+        },
+        "Privileged": false,
+        "PublishAllPorts": false,
+        "ReadonlyRootfs": false,
+        "RestartPolicy": {
+            "MaximumRetryCount": 0,
+            "Name": ""
+        },
+        "SecurityOpt": null,
+        "Ulimits": null,
+        "VolumesFrom": null
+    },
+    "HostnamePath": "/mnt/sda1/var/lib/docker/containers/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1/hostname",
+    "HostsPath": "/mnt/sda1/var/lib/docker/containers/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1/hosts",
+    "Id": "469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1",
+    "Image": "4300417211ebb75b48b06ed5640d641778f312072d24b37978682345cbb362b1",
+    "LogPath": "/mnt/sda1/var/lib/docker/containers/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1-json.log",
+    "MountLabel": "",
+    "Name": "/desperate_babbage",
+    "NetworkSettings": {
+        "Bridge": "docker0",
+        "Gateway": "172.17.42.1",
+        "GlobalIPv6Address": "",
+        "GlobalIPv6PrefixLen": 0,
+        "IPAddress": "172.17.0.2",
+        "IPPrefixLen": 16,
+        "IPv6Gateway": "",
+        "LinkLocalIPv6Address": "fe80::42:acff:fe11:2",
+        "LinkLocalIPv6PrefixLen": 64,
+        "MacAddress": "02:42:ac:11:00:02",
+        "PortMapping": null,
+        "Ports": {
+            "22/tcp": null,
+            "8080/tcp": [ ]
+        }
+    },
+    "Path": "/bin/sh",
+    "ProcessLabel": "",
+    "ResolvConfPath": "/mnt/sda1/var/lib/docker/containers/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1/resolv.conf",
+    "RestartCount": 0,
+    "State": {
+        "Dead": false,
+        "Error": "",
+        "ExitCode": 0,
+        "FinishedAt": "0001-01-01T00:00:00Z",
+        "OOMKilled": false,
+        "Paused": false,
+        "Pid": 898,
+        "Restarting": false,
+        "Running": true,
+        "StartedAt": "2015-04-29T11:55:43.464717907Z"
+    },
+    "Volumes": {},
+    "VolumesRW": {}
+}
+]
diff --git a/src/test/resources/com/github/dockerjava/api/command/inspectContainerResponse_full.json b/src/test/resources/com/github/dockerjava/api/command/inspectContainerResponse_full.json
new file mode 100644
index 00000000..c4383ef1
--- /dev/null
+++ b/src/test/resources/com/github/dockerjava/api/command/inspectContainerResponse_full.json
@@ -0,0 +1,153 @@
+[{
+    "AppArmorProfile": "",
+    "Args": [
+        "-c",
+        "/var/lib/jenkins/run.sh"
+    ],
+    "Config": {
+        "AttachStderr": true,
+        "AttachStdin": true,
+        "AttachStdout": true,
+        "Cmd": [
+            "/bin/sh",
+            "-c",
+            "/var/lib/jenkins/run.sh"
+        ],
+        "CpuShares": 0,
+        "Cpuset": "",
+        "Domainname": "",
+        "Entrypoint": null,
+        "Env": [
+            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+            "MAVEN_VERSION=3.3.1",
+            "JETTY_VERSION=9.2.9.v20150224",
+            "REV=107ea141f5c7581056c6eb53d2ccd222cdf0d58c"
+        ],
+        "ExposedPorts": {
+            "22/tcp": {},
+            "8080/tcp": {},
+            "8081/tcp": {}
+        },
+        "Hostname": "469e5edd8d5b",
+        "Image": "jenkinsci/workflow-demo",
+        "Labels": {},
+        "MacAddress": "",
+        "Memory": 0,
+        "MemorySwap": 0,
+        "NetworkDisabled": false,
+        "OnBuild": null,
+        "OpenStdin": true,
+        "PortSpecs": null,
+        "StdinOnce": true,
+        "Tty": true,
+        "User": "",
+        "Volumes": null,
+        "WorkingDir": "/var/lib/jenkins/workflow-plugin-pipeline-demo"
+    },
+    "Created": "2015-04-29T11:55:42.968262967Z",
+    "Driver": "aufs",
+    "ExecDriver": "native-0.2",
+    "ExecIDs": null,
+    "HostConfig": {
+        "Binds": null,
+        "CapAdd": null,
+        "CapDrop": null,
+        "CgroupParent": "",
+        "ContainerIDFile": "",
+        "CpuShares": 0,
+        "CpusetCpus": "",
+        "Devices": [],
+        "Dns": null,
+        "DnsSearch": null,
+        "ExtraHosts": null,
+        "IpcMode": "",
+        "Links": null,
+        "LogConfig": {
+            "Config": null,
+            "Type": "json-file"
+        },
+        "LxcConf": [],
+        "Memory": 0,
+        "MemorySwap": 0,
+        "NetworkMode": "bridge",
+        "PidMode": "",
+        "PortBindings": {
+            "8080/tcp": [
+                {
+                    "HostIp": "",
+                    "HostPort": "8080"
+                }
+            ],
+            "8081/tcp": [
+                {
+                    "HostIp": "",
+                    "HostPort": "8081"
+                }
+            ]
+        },
+        "Privileged": false,
+        "PublishAllPorts": false,
+        "ReadonlyRootfs": false,
+        "RestartPolicy": {
+            "MaximumRetryCount": 0,
+            "Name": ""
+        },
+        "SecurityOpt": null,
+        "Ulimits": null,
+        "VolumesFrom": null
+    },
+    "HostnamePath": "/mnt/sda1/var/lib/docker/containers/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1/hostname",
+    "HostsPath": "/mnt/sda1/var/lib/docker/containers/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1/hosts",
+    "Id": "469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1",
+    "Image": "4300417211ebb75b48b06ed5640d641778f312072d24b37978682345cbb362b1",
+    "LogPath": "/mnt/sda1/var/lib/docker/containers/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1-json.log",
+    "MountLabel": "",
+    "Name": "/desperate_babbage",
+    "NetworkSettings": {
+        "Bridge": "docker0",
+        "Gateway": "172.17.42.1",
+        "GlobalIPv6Address": "",
+        "GlobalIPv6PrefixLen": 0,
+        "IPAddress": "172.17.0.2",
+        "IPPrefixLen": 16,
+        "IPv6Gateway": "",
+        "LinkLocalIPv6Address": "fe80::42:acff:fe11:2",
+        "LinkLocalIPv6PrefixLen": 64,
+        "MacAddress": "02:42:ac:11:00:02",
+        "PortMapping": null,
+        "Ports": {
+            "22/tcp": null,
+            "8080/tcp": [
+                {
+                    "HostIp": "0.0.0.0",
+                    "HostPort": "8080"
+                }
+            ],
+            "8081/tcp": [
+                {
+                    "HostIp": "0.0.0.0",
+                    "HostPort": "8081"
+                }
+            ]
+        }
+    },
+    "Path": "/bin/sh",
+    "ProcessLabel": "",
+    "ResolvConfPath": "/mnt/sda1/var/lib/docker/containers/469e5edd8d5b33e3c905a7ffc97360ec6ee211d6782815fbcd144568045819e1/resolv.conf",
+    "RestartCount": 0,
+    "State": {
+        "Dead": false,
+        "Error": "",
+        "ExitCode": 0,
+        "FinishedAt": "0001-01-01T00:00:00Z",
+        "OOMKilled": false,
+        "Paused": false,
+        "Pid": 898,
+        "Restarting": false,
+        "Running": true,
+        "StartedAt": "2015-04-29T11:55:43.464717907Z"
+    },
+    "Volumes": { "/foo/bar/myvol":"/path1", "/bar/foo/myvol2":"/path2" },
+    "VolumesRW": { "/foo/bar/myvol": true, "/bar/foo/myvol2": false }
+}
+]