PROTON-2188: throw IAE for map entries with unknown key/value type rather than just NPE
diff --git a/proton-j/src/main/java/org/apache/qpid/proton/codec/MapType.java b/proton-j/src/main/java/org/apache/qpid/proton/codec/MapType.java
index 78106ce..d73351e 100644
--- a/proton-j/src/main/java/org/apache/qpid/proton/codec/MapType.java
+++ b/proton-j/src/main/java/org/apache/qpid/proton/codec/MapType.java
@@ -90,7 +90,12 @@
 
                 if (fixedKeyType == null)
                 {
-                    elementEncoding = _encoder.getType(element.getKey()).getEncoding(element.getKey());
+                    AMQPType keyType = _encoder.getType(element.getKey());
+                    if(keyType == null) {
+                        throw new IllegalArgumentException(
+                                "No encoding is known for map entry key of type: " + element.getKey().getClass().getName());
+                    }
+                    elementEncoding = keyType.getEncoding(element.getKey());
                 }
                 else
                 {
@@ -98,7 +103,14 @@
                 }
 
                 len += elementEncoding.getConstructorSize() + elementEncoding.getValueSize(element.getKey());
-                elementEncoding = _encoder.getType(element.getValue()).getEncoding(element.getValue());
+
+                AMQPType valueType = _encoder.getType(element.getValue());
+                if(valueType == null) {
+                    throw new IllegalArgumentException(
+                            "No encoding is known for map entry value of type: " + element.getValue().getClass().getName());
+                }
+
+                elementEncoding = valueType.getEncoding(element.getValue());
                 len += elementEncoding.getConstructorSize() + elementEncoding.getValueSize(element.getValue());
             }
         } finally {
diff --git a/proton-j/src/test/java/org/apache/qpid/proton/codec/ListTypeCodecTest.java b/proton-j/src/test/java/org/apache/qpid/proton/codec/ListTypeCodecTest.java
index 3e964c8..b486fd1 100644
--- a/proton-j/src/test/java/org/apache/qpid/proton/codec/ListTypeCodecTest.java
+++ b/proton-j/src/test/java/org/apache/qpid/proton/codec/ListTypeCodecTest.java
@@ -16,9 +16,12 @@
  */
 package org.apache.qpid.proton.codec;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.StringContains.containsString;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -144,4 +147,32 @@
         // Check that the ListType tries to reserve space, actual encoding size not computed here.
         Mockito.verify(spy).ensureRemaining(Mockito.anyInt());
     }
+
+    @Test
+    public void testEncodeListWithUnknownEntryType() throws Exception {
+        List<Object> list = new ArrayList<>();
+        list.add(new MyUnknownTestType());
+
+        doTestEncodeListWithUnknownEntryTypeTestImpl(list);
+    }
+
+    @Test
+    public void testEncodeSubListWithUnknownEntryType() throws Exception {
+        List<Object> subList = new ArrayList<>();
+        subList.add(new MyUnknownTestType());
+
+        List<Object> list = new ArrayList<>();
+        list.add(subList);
+
+        doTestEncodeListWithUnknownEntryTypeTestImpl(list);
+    }
+
+    private void doTestEncodeListWithUnknownEntryTypeTestImpl(List<Object> list) {
+        try {
+            encoder.writeList(list);
+            fail("Expected exception to be thrown");
+        } catch (IllegalArgumentException iae) {
+            assertThat(iae.getMessage(), containsString("No encoding defined for type: class org.apache.qpid.proton.codec.MyUnknownTestType"));
+        }
+    }
 }
diff --git a/proton-j/src/test/java/org/apache/qpid/proton/codec/MapTypeCodecTest.java b/proton-j/src/test/java/org/apache/qpid/proton/codec/MapTypeCodecTest.java
index 73b4a70..b0762fb 100644
--- a/proton-j/src/test/java/org/apache/qpid/proton/codec/MapTypeCodecTest.java
+++ b/proton-j/src/test/java/org/apache/qpid/proton/codec/MapTypeCodecTest.java
@@ -16,9 +16,12 @@
  */
 package org.apache.qpid.proton.codec;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.StringContains.containsString;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.util.HashMap;
@@ -123,4 +126,60 @@
         // Check that the MapType tries to reserve space, actual encoding size not computed here.
         Mockito.verify(spy).ensureRemaining(Mockito.anyInt());
     }
+
+    @Test
+    public void testEncodeMapWithUnknownEntryValueType() throws Exception {
+        Map<String, Object> map = new HashMap<>();
+        map.put("unknown", new MyUnknownTestType());
+
+        doTestEncodeMapWithUnknownEntryValueTypeTestImpl(map);
+    }
+
+    @Test
+    public void testEncodeSubMapWithUnknownEntryValueType() throws Exception {
+        Map<String, Object> subMap = new HashMap<>();
+        subMap.put("unknown", new MyUnknownTestType());
+
+        Map<String, Object> map = new HashMap<>();
+        map.put("submap", subMap);
+
+        doTestEncodeMapWithUnknownEntryValueTypeTestImpl(map);
+    }
+
+    private void doTestEncodeMapWithUnknownEntryValueTypeTestImpl(Map<String, Object> map) {
+        try {
+            encoder.writeMap(map);
+            fail("Expected exception to be thrown");
+        } catch (IllegalArgumentException iae) {
+            assertThat(iae.getMessage(), containsString("No encoding is known for map entry value of type: org.apache.qpid.proton.codec.MyUnknownTestType"));
+        }
+    }
+
+    @Test
+    public void testEncodeMapWithUnknownEntryKeyType() throws Exception {
+        Map<Object, String> map = new HashMap<>();
+        map.put(new MyUnknownTestType(), "unknown");
+
+        doTestEncodeMapWithUnknownEntryKeyTypeTestImpl(map);
+    }
+
+    @Test
+    public void testEncodeSubMapWithUnknownEntryKeyType() throws Exception {
+        Map<Object, String> subMap = new HashMap<>();
+        subMap.put(new MyUnknownTestType(), "unknown");
+
+        Map<String, Object> map = new HashMap<>();
+        map.put("submap", subMap);
+
+        doTestEncodeMapWithUnknownEntryKeyTypeTestImpl(map);
+    }
+
+    private void doTestEncodeMapWithUnknownEntryKeyTypeTestImpl(Map<?, ?> map) {
+        try {
+            encoder.writeMap(map);
+            fail("Expected exception to be thrown");
+        } catch (IllegalArgumentException iae) {
+            assertThat(iae.getMessage(), containsString("No encoding is known for map entry key of type: org.apache.qpid.proton.codec.MyUnknownTestType"));
+        }
+    }
 }
diff --git a/proton-j/src/test/java/org/apache/qpid/proton/codec/MyUnknownTestType.java b/proton-j/src/test/java/org/apache/qpid/proton/codec/MyUnknownTestType.java
new file mode 100644
index 0000000..a8e0b05
--- /dev/null
+++ b/proton-j/src/test/java/org/apache/qpid/proton/codec/MyUnknownTestType.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.qpid.proton.codec;
+
+class MyUnknownTestType {
+
+}
\ No newline at end of file