[TOREE-508] Properly reply errors to iopub.error (#180)

Reply errors to iopub.error instead of iopub.exec_result
diff --git a/kernel/src/main/scala/org/apache/toree/kernel/protocol/v5/handler/ExecuteRequestHandler.scala b/kernel/src/main/scala/org/apache/toree/kernel/protocol/v5/handler/ExecuteRequestHandler.scala
index 96c306e..6f253e1 100644
--- a/kernel/src/main/scala/org/apache/toree/kernel/protocol/v5/handler/ExecuteRequestHandler.scala
+++ b/kernel/src/main/scala/org/apache/toree/kernel/protocol/v5/handler/ExecuteRequestHandler.scala
@@ -89,19 +89,29 @@
         case Success(tuple) =>
           val (executeReply, executeResult) = updateCount(tuple, executionCount)
 
-          //  Send an ExecuteReply to the client
-          val executeReplyMsg = skeletonBuilder
-            .withHeader(MessageType.Outgoing.ExecuteReply)
-            .withContentString(executeReply).build
-          relayMsg(executeReplyMsg, relayActor)
+          if (executeReply.status.equals("error")) {
+            // Send an ExecuteReplyError with the result of the code execution to ioPub.error
+            val replyError: ExecuteReply = ExecuteReplyError(
+              executionCount,
+              executeReply.ename,
+              executeReply.evalue,
+              executeReply.traceback)
+            relayErrorMessages(relayActor, replyError, skeletonBuilder)
+          } else {
+            //  Send an ExecuteReply to the client
+            val executeReplyMsg = skeletonBuilder
+              .withHeader(MessageType.Outgoing.ExecuteReply)
+              .withMetadata(Metadata("status" -> executeReply.status))
+              .withContentString(executeReply).build
+            relayMsg(executeReplyMsg, relayActor)
 
-          //  Send an ExecuteResult with the result of the code execution
-          if (executeResult.hasContent) {
-            val executeResultMsg = skeletonBuilder
-              .withIds(Seq(MessageType.Outgoing.ExecuteResult.toString.getBytes))
-              .withHeader(MessageType.Outgoing.ExecuteResult)
-              .withContentString(executeResult).build
-            relayMsg(executeResultMsg, relayActor)
+            if (executeResult.hasContent) {
+              val executeResultMsg = skeletonBuilder
+                .withIds(Seq(MessageType.Outgoing.ExecuteResult.toString.getBytes))
+                .withHeader(MessageType.Outgoing.ExecuteResult)
+                .withContentString(executeResult).build
+              relayMsg(executeResultMsg, relayActor)
+            }
           }
 
         case Failure(error: Throwable) =>
@@ -148,6 +158,7 @@
                          skeletonBuilder: KMBuilder) {
     val executeReplyMsg = skeletonBuilder
       .withHeader(MessageType.Outgoing.ExecuteReply)
+      .withMetadata(Metadata("status" -> replyError.status))
       .withContentString(replyError).build
 
     val errorContent: ErrorContent =  ErrorContent(
diff --git a/protocol/src/main/scala/org/apache/toree/kernel/protocol/v5/package.scala b/protocol/src/main/scala/org/apache/toree/kernel/protocol/v5/package.scala
index b84af70..466a3ec 100644
--- a/protocol/src/main/scala/org/apache/toree/kernel/protocol/v5/package.scala
+++ b/protocol/src/main/scala/org/apache/toree/kernel/protocol/v5/package.scala
@@ -35,7 +35,8 @@
       if(kv.isEmpty) {
         Map.empty
       } else {
-        kv.toMap.mapValues(v => Json.parse(v))
+        // triple quotes due https://github.com/scala/bug/issues/6476
+        kv.toMap.mapValues(v => Json.parse(s""""$v""""))
       }
     }
 
diff --git a/protocol/src/test/scala/org/apache/toree/kernel/protocol/v5/KMBuilderSpec.scala b/protocol/src/test/scala/org/apache/toree/kernel/protocol/v5/KMBuilderSpec.scala
index 3b09ad8..d6c957d 100644
--- a/protocol/src/test/scala/org/apache/toree/kernel/protocol/v5/KMBuilderSpec.scala
+++ b/protocol/src/test/scala/org/apache/toree/kernel/protocol/v5/KMBuilderSpec.scala
@@ -68,6 +68,12 @@
         val metadata = builder.build(includeDefaultMetadata = false).metadata
         metadata should be(Metadata())
       }
+
+      it("should merge metadata with default") {
+        val builder = new KM2
+        val metadata = builder.withMetadata(Metadata("some" -> "value")).build.metadata
+        metadata should contain key("some")
+      }
     }
 
     describe("withXYZ"){
diff --git a/scala-interpreter/src/main/scala-2.11/org/apache/toree/kernel/interpreter/scala/ScalaInterpreterSpecific.scala b/scala-interpreter/src/main/scala-2.11/org/apache/toree/kernel/interpreter/scala/ScalaInterpreterSpecific.scala
index 902bb1d..c941935 100644
--- a/scala-interpreter/src/main/scala-2.11/org/apache/toree/kernel/interpreter/scala/ScalaInterpreterSpecific.scala
+++ b/scala-interpreter/src/main/scala-2.11/org/apache/toree/kernel/interpreter/scala/ScalaInterpreterSpecific.scala
@@ -426,7 +426,7 @@
         ExecuteError(
           ex.getClass.getName,
           ex.getLocalizedMessage,
-          formattedException.slice(1, formattedException.size - 1).toList
+          formattedException.toList
         )
       // Compile time error, need to check internal reporter
       case _ =>