Reworks message generation

- Reworks message generation to reduce duplicate code.
- Switches to using Slack's attachment messages.
- Adds reference to change number in all messages.
- Updates copyright year.

Change-Id: I87d4c04327e117910e71274a031ad8e722b5f6e9
diff --git a/pom.xml b/pom.xml
index dd3dfb0..dba6eca 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
 <!--
-  ~ Copyright 2016 Cisco Systems, Inc.
+  ~ Copyright 2017 Cisco Systems, Inc.
   ~
   ~ 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
diff --git a/src/main/java/com/cisco/gerrit/plugins/slack/PublishEventListener.java b/src/main/java/com/cisco/gerrit/plugins/slack/PublishEventListener.java
index 677a586..228f477 100644
--- a/src/main/java/com/cisco/gerrit/plugins/slack/PublishEventListener.java
+++ b/src/main/java/com/cisco/gerrit/plugins/slack/PublishEventListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
diff --git a/src/main/java/com/cisco/gerrit/plugins/slack/client/WebhookClient.java b/src/main/java/com/cisco/gerrit/plugins/slack/client/WebhookClient.java
index 6f6ad56..889c818 100644
--- a/src/main/java/com/cisco/gerrit/plugins/slack/client/WebhookClient.java
+++ b/src/main/java/com/cisco/gerrit/plugins/slack/client/WebhookClient.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
diff --git a/src/main/java/com/cisco/gerrit/plugins/slack/config/ProjectConfig.java b/src/main/java/com/cisco/gerrit/plugins/slack/config/ProjectConfig.java
index 6c83713..d2afcc4 100644
--- a/src/main/java/com/cisco/gerrit/plugins/slack/config/ProjectConfig.java
+++ b/src/main/java/com/cisco/gerrit/plugins/slack/config/ProjectConfig.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
diff --git a/src/main/java/com/cisco/gerrit/plugins/slack/message/ChangeMergedMessageGenerator.java b/src/main/java/com/cisco/gerrit/plugins/slack/message/ChangeMergedMessageGenerator.java
index 35d885e..85d75bb 100644
--- a/src/main/java/com/cisco/gerrit/plugins/slack/message/ChangeMergedMessageGenerator.java
+++ b/src/main/java/com/cisco/gerrit/plugins/slack/message/ChangeMergedMessageGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
@@ -18,7 +18,6 @@
 package com.cisco.gerrit.plugins.slack.message;
 
 import com.cisco.gerrit.plugins.slack.config.ProjectConfig;
-import com.cisco.gerrit.plugins.slack.util.ResourceHelper;
 import com.google.gerrit.server.events.ChangeMergedEvent;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -29,7 +28,7 @@
  *
  * @author Matthew Montgomery
  */
-public class ChangeMergedMessageGenerator extends MessageGenerator
+public class ChangeMergedMessageGenerator implements MessageGenerator
 {
     /**
      * The class logger instance.
@@ -46,7 +45,7 @@
      *
      * @param event The ChangeMergedEvent instance to generate a message for.
      */
-    protected ChangeMergedMessageGenerator(ChangeMergedEvent event,
+    ChangeMergedMessageGenerator(ChangeMergedEvent event,
             ProjectConfig config)
     {
         if (event == null)
@@ -72,26 +71,19 @@
 
         try
         {
-            String template;
-            template = ResourceHelper.loadNamedResourceAsString(
-                    "basic-message-template.json");
+            MessageTemplate template;
+            template = new MessageTemplate();
 
-            StringBuilder text;
-            text = new StringBuilder();
+            template.setChannel(config.getChannel());
+            template.setName(event.submitter.get().name);
+            template.setAction("merged");
+            template.setNumber(event.change.get().number);
+            template.setProject(event.change.get().project);
+            template.setBranch(event.change.get().branch);
+            template.setUrl(event.change.get().url);
+            template.setMessage(event.change.get().commitMessage.split("\n")[0]);
 
-            text.append(escape(event.submitter.get().name));
-            text.append(" merged\\n>>>");
-            text.append(escape(event.change.get().project));
-            text.append(" (");
-            text.append(escape(event.change.get().branch));
-            text.append("): ");
-            text.append(escape(event.change.get().commitMessage.split("\n")[0]));
-            text.append(" (");
-            text.append(escape(event.change.get().url));
-            text.append(")");
-
-            message = String.format(template, text, config.getChannel(),
-                    config.getUsername());
+            message = template.render();
         }
         catch (Exception e)
         {
diff --git a/src/main/java/com/cisco/gerrit/plugins/slack/message/CommentAddedMessageGenerator.java b/src/main/java/com/cisco/gerrit/plugins/slack/message/CommentAddedMessageGenerator.java
index b319ee1..663d154 100644
--- a/src/main/java/com/cisco/gerrit/plugins/slack/message/CommentAddedMessageGenerator.java
+++ b/src/main/java/com/cisco/gerrit/plugins/slack/message/CommentAddedMessageGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
@@ -18,7 +18,6 @@
 package com.cisco.gerrit.plugins.slack.message;
 
 import com.cisco.gerrit.plugins.slack.config.ProjectConfig;
-import com.cisco.gerrit.plugins.slack.util.ResourceHelper;
 import com.google.common.base.Ascii;
 import com.google.gerrit.server.events.CommentAddedEvent;
 import org.slf4j.Logger;
@@ -29,8 +28,9 @@
  * a comment added event.
  *
  * @author Kenneth Pedersen
+ * @author Matthew Montgomery
  */
-public class CommentAddedMessageGenerator extends MessageGenerator
+public class CommentAddedMessageGenerator implements MessageGenerator
 {
     /**
      * The class logger instance.
@@ -47,7 +47,7 @@
      *
      * @param event The CommentAddedEvent instance to generate a message for.
      */
-    protected CommentAddedMessageGenerator(CommentAddedEvent event,
+    CommentAddedMessageGenerator(CommentAddedEvent event,
                                            ProjectConfig config)
     {
         if (event == null)
@@ -73,28 +73,19 @@
 
         try
         {
-            String template;
-            template = ResourceHelper.loadNamedResourceAsString(
-                    "basic-message-template.json");
+            MessageTemplate template;
+            template = new MessageTemplate();
 
-            StringBuilder text;
-            text = new StringBuilder();
+            template.setChannel(config.getChannel());
+            template.setName(event.author.get().name);
+            template.setAction("commented on");
+            template.setNumber(event.change.get().number);
+            template.setProject(event.change.get().project);
+            template.setBranch(event.change.get().branch);
+            template.setUrl(event.change.get().url);
+            template.setMessage(Ascii.truncate(event.comment, 200, "..."));
 
-            text.append(escape(event.author.get().name));
-            text.append(" commented to ");
-            text.append(escape(event.change.get().owner.name));
-            text.append("\\n>>>");
-            text.append(escape(event.change.get().project));
-            text.append(" (");
-            text.append(escape(event.change.get().branch));
-            text.append("): ");
-            text.append(escape(Ascii.truncate(event.comment, 200, "...")));
-            text.append(" (");
-            text.append(escape(event.change.get().url));
-            text.append(")");
-
-            message = String.format(template, text, config.getChannel(),
-                    config.getUsername());
+            message = template.render();
         }
         catch (Exception e)
         {
diff --git a/src/main/java/com/cisco/gerrit/plugins/slack/message/MessageGenerator.java b/src/main/java/com/cisco/gerrit/plugins/slack/message/MessageGenerator.java
index 16b5b4f..365922b 100644
--- a/src/main/java/com/cisco/gerrit/plugins/slack/message/MessageGenerator.java
+++ b/src/main/java/com/cisco/gerrit/plugins/slack/message/MessageGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
@@ -22,38 +22,19 @@
  *
  * @author Matthew Montgomery
  */
-public abstract class MessageGenerator
+public interface MessageGenerator
 {
     /**
      * Whether or not the generated message should be published.
      *
      * @return True if the message should be published, otherwise false
      */
-    public abstract boolean shouldPublish();
+    boolean shouldPublish();
 
     /**
      * Generates an event specific message suitable for publishing.
      *
      * @return The generated message.
      */
-    public abstract String generate();
-
-    /**
-     * Escapes the double quote character.
-     *
-     * @param message The message in which to search escape double quote
-     *                characters
-     *
-     * @return The message with all occurrences of the double quote character
-     * escaped.
-     */
-    protected String escape(String message)
-    {
-        if (message != null)
-        {
-            message = message.replace("\"", "\\\"");
-        }
-
-        return message;
-    }
+    String generate();
 }
diff --git a/src/main/java/com/cisco/gerrit/plugins/slack/message/MessageGeneratorFactory.java b/src/main/java/com/cisco/gerrit/plugins/slack/message/MessageGeneratorFactory.java
index 90147d9..5f37434 100644
--- a/src/main/java/com/cisco/gerrit/plugins/slack/message/MessageGeneratorFactory.java
+++ b/src/main/java/com/cisco/gerrit/plugins/slack/message/MessageGeneratorFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
diff --git a/src/main/java/com/cisco/gerrit/plugins/slack/message/MessageTemplate.java b/src/main/java/com/cisco/gerrit/plugins/slack/message/MessageTemplate.java
new file mode 100644
index 0000000..f73517b
--- /dev/null
+++ b/src/main/java/com/cisco/gerrit/plugins/slack/message/MessageTemplate.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2017 Cisco Systems, Inc.
+ *
+ * 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.cisco.gerrit.plugins.slack.message;
+
+import com.cisco.gerrit.plugins.slack.util.ResourceHelper;
+import java.io.IOException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Defines a message template.
+ *
+ * @author Matthew Montgomery
+ */
+public class MessageTemplate
+{
+    /**
+     * The class logger instance.
+     */
+    private static final Logger LOGGER =
+            LoggerFactory.getLogger(MessageTemplate.class);
+
+    private String channel;
+    private String name;
+    private String action;
+    private int number;
+    private String project;
+    private String branch;
+    private String url;
+    private String message;
+
+
+    public String getChannel()
+    {
+        return escape(channel);
+    }
+
+    public void setChannel(String channel)
+    {
+        this.channel = channel;
+    }
+
+    public String getName()
+    {
+        return escape(name);
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+    public String getAction()
+    {
+        return escape(action);
+    }
+
+    public void setAction(String action)
+    {
+        this.action = action;
+    }
+
+    public int getNumber()
+    {
+        return number;
+    }
+
+    public void setNumber(int number)
+    {
+        this.number = number;
+    }
+
+    public String getProject()
+    {
+        return escape(project);
+    }
+
+    public void setProject(String project)
+    {
+        this.project = project;
+    }
+
+    public String getBranch()
+    {
+        return escape(branch);
+    }
+
+    public void setBranch(String branch)
+    {
+        this.branch = branch;
+    }
+
+    public String getMessage()
+    {
+        return escape(message);
+    }
+
+    public void setMessage(String message)
+    {
+        this.message = message;
+    }
+
+    public String getUrl()
+    {
+        return escape(url);
+    }
+
+    public void setUrl(String url)
+    {
+        this.url = url;
+    }
+
+
+    /**
+     * Renders the message template into a String.
+     *
+     * @return A String representation of the rendered template.
+     */
+    public String render()
+    {
+        String result;
+        result = "";
+
+        try
+        {
+            String template;
+            template = ResourceHelper.loadNamedResourceAsString(
+                    "message-template.json");
+
+            result = String.format(template, getChannel(), getName(),
+                    getAction(), getProject(), getBranch(), getNumber(),
+                    getUrl(), getMessage(), "good");
+        }
+        catch (IOException e)
+        {
+            LOGGER.error("Error rendering template: " + e.getMessage(), e);
+        }
+
+        return result;
+    }
+
+    /**
+     * Escapes the double quote character.
+     *
+     * @param str The message in which to search escape double quote
+     *            characters
+     *
+     * @return The message with all occurrences of the double quote character
+     * escaped.
+     */
+    private String escape(String str)
+    {
+        if (str != null)
+        {
+            str = str.replace("\"", "\\\"");
+        }
+
+        return str;
+    }
+}
diff --git a/src/main/java/com/cisco/gerrit/plugins/slack/message/PatchSetCreatedMessageGenerator.java b/src/main/java/com/cisco/gerrit/plugins/slack/message/PatchSetCreatedMessageGenerator.java
index 2d7ddf5..b3d119b 100644
--- a/src/main/java/com/cisco/gerrit/plugins/slack/message/PatchSetCreatedMessageGenerator.java
+++ b/src/main/java/com/cisco/gerrit/plugins/slack/message/PatchSetCreatedMessageGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
@@ -18,7 +18,6 @@
 package com.cisco.gerrit.plugins.slack.message;
 
 import com.cisco.gerrit.plugins.slack.config.ProjectConfig;
-import com.cisco.gerrit.plugins.slack.util.ResourceHelper;
 import com.google.gerrit.server.events.PatchSetCreatedEvent;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -32,7 +31,7 @@
  *
  * @author Matthew Montgomery
  */
-public class PatchSetCreatedMessageGenerator extends MessageGenerator
+public class PatchSetCreatedMessageGenerator implements MessageGenerator
 {
     private static final Logger LOGGER =
             LoggerFactory.getLogger(PatchSetCreatedMessageGenerator.class);
@@ -47,7 +46,7 @@
      * @param event The PatchSetCreatedEvent instance to generate a
      *              message for.
      */
-    protected PatchSetCreatedMessageGenerator(PatchSetCreatedEvent event,
+    PatchSetCreatedMessageGenerator(PatchSetCreatedEvent event,
             ProjectConfig config)
     {
         if (event == null)
@@ -97,26 +96,19 @@
 
         try
         {
-            String template;
-            template = ResourceHelper.loadNamedResourceAsString(
-                    "basic-message-template.json");
+            MessageTemplate template;
+            template = new MessageTemplate();
 
-            StringBuilder text;
-            text = new StringBuilder();
+            template.setChannel(config.getChannel());
+            template.setName(event.uploader.get().name);
+            template.setAction("proposed");
+            template.setNumber(event.change.get().number);
+            template.setProject(event.change.get().project);
+            template.setBranch(event.change.get().branch);
+            template.setUrl(event.change.get().url);
+            template.setMessage(event.change.get().commitMessage.split("\n")[0]);
 
-            text.append(escape(event.uploader.get().name));
-            text.append(" proposed\\n>>>");
-            text.append(escape(event.change.get().project));
-            text.append(" (");
-            text.append(escape(event.change.get().branch));
-            text.append("): ");
-            text.append(escape(event.change.get().commitMessage.split("\n")[0]));
-            text.append(" (");
-            text.append(escape(event.change.get().url));
-            text.append(")");
-
-            message = String.format(template, text, config.getChannel(),
-                    config.getUsername());
+            message = template.render();
         }
         catch (Exception e)
         {
diff --git a/src/main/java/com/cisco/gerrit/plugins/slack/message/ReviewerAddedMessageGenerator.java b/src/main/java/com/cisco/gerrit/plugins/slack/message/ReviewerAddedMessageGenerator.java
index 587b599..ef9413d 100644
--- a/src/main/java/com/cisco/gerrit/plugins/slack/message/ReviewerAddedMessageGenerator.java
+++ b/src/main/java/com/cisco/gerrit/plugins/slack/message/ReviewerAddedMessageGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
@@ -18,8 +18,6 @@
 package com.cisco.gerrit.plugins.slack.message;
 
 import com.cisco.gerrit.plugins.slack.config.ProjectConfig;
-import com.cisco.gerrit.plugins.slack.util.ResourceHelper;
-import com.google.common.base.Ascii;
 import com.google.gerrit.server.events.ReviewerAddedEvent;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -30,7 +28,7 @@
  *
  * @author Nathan Wall
  */
-public class ReviewerAddedMessageGenerator extends MessageGenerator
+public class ReviewerAddedMessageGenerator implements MessageGenerator
 {
     /**
      * The class logger instance.
@@ -47,7 +45,7 @@
      *
      * @param event The ReviewerAddedEvent instance to generate a message for.
      */
-    protected ReviewerAddedMessageGenerator(ReviewerAddedEvent event,
+    ReviewerAddedMessageGenerator(ReviewerAddedEvent event,
                                             ProjectConfig config)
     {
         if (event == null)
@@ -73,26 +71,19 @@
 
         try
         {
-            String template;
-            template = ResourceHelper.loadNamedResourceAsString(
-                    "basic-message-template.json");
+            MessageTemplate template;
+            template = new MessageTemplate();
 
-            StringBuilder text;
-            text = new StringBuilder();
+            template.setChannel(config.getChannel());
+            template.setName(event.reviewer.get().name);
+            template.setAction("was added to review");
+            template.setNumber(event.change.get().number);
+            template.setProject(event.change.get().project);
+            template.setBranch(event.change.get().branch);
+            template.setUrl(event.change.get().url);
+            template.setMessage(event.change.get().commitMessage.split("\n")[0]);
 
-            text.append(escape(event.reviewer.get().name));
-            text.append(" was added to review\\n>>>");
-            text.append(escape(event.change.get().project));
-            text.append(" (");
-            text.append(escape(event.change.get().branch));
-            text.append("): ");
-            text.append(escape(event.change.get().commitMessage.split("\n")[0]));
-            text.append(" (");
-            text.append(escape(event.change.get().url));
-            text.append(")");
-
-            message = String.format(template, text, config.getChannel(),
-                    config.getUsername());
+            message = template.render();
         }
         catch (Exception e)
         {
diff --git a/src/main/java/com/cisco/gerrit/plugins/slack/message/UnsupportedMessageGenerator.java b/src/main/java/com/cisco/gerrit/plugins/slack/message/UnsupportedMessageGenerator.java
index ac5922b..2b7a7c3 100644
--- a/src/main/java/com/cisco/gerrit/plugins/slack/message/UnsupportedMessageGenerator.java
+++ b/src/main/java/com/cisco/gerrit/plugins/slack/message/UnsupportedMessageGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
@@ -27,13 +27,12 @@
  *
  * @author Matthew Montgomery
  */
-public class UnsupportedMessageGenerator extends MessageGenerator
+public class UnsupportedMessageGenerator implements MessageGenerator
 {
     private ProjectConfig config;
     private Event event;
 
-    protected UnsupportedMessageGenerator(Event event,
-            ProjectConfig config)
+    UnsupportedMessageGenerator(Event event, ProjectConfig config)
     {
         if (event == null)
         {
diff --git a/src/main/java/com/cisco/gerrit/plugins/slack/util/ResourceHelper.java b/src/main/java/com/cisco/gerrit/plugins/slack/util/ResourceHelper.java
index b7482dc..8adddc6 100644
--- a/src/main/java/com/cisco/gerrit/plugins/slack/util/ResourceHelper.java
+++ b/src/main/java/com/cisco/gerrit/plugins/slack/util/ResourceHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
diff --git a/src/main/resources/basic-message-template.json b/src/main/resources/basic-message-template.json
deleted file mode 100644
index f61b2bf..0000000
--- a/src/main/resources/basic-message-template.json
+++ /dev/null
@@ -1 +0,0 @@
-{"text": "%s","channel": "#%s","username": "%s"}
diff --git a/src/main/resources/message-template.json b/src/main/resources/message-template.json
new file mode 100644
index 0000000..b1c9614
--- /dev/null
+++ b/src/main/resources/message-template.json
@@ -0,0 +1,13 @@
+{
+  "channel": "#%1$s",
+  "attachments": [
+    {
+      "fallback": "%2$s %3$s - %4$s (%5$s) - change %6$s - %7$s",
+      "pretext": "%2$s %3$s",
+      "title": "%4$s (%5$s) - change %6$s",
+      "title_link": "%7$s",
+      "text": "%8$s",
+      "color": "%9$s"
+    }
+  ]
+}
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/PublishEventListenerTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/PublishEventListenerTest.java
index 0e3bc5a..50b3f16 100644
--- a/src/test/java/com/cisco/gerrit/plugins/slack/PublishEventListenerTest.java
+++ b/src/test/java/com/cisco/gerrit/plugins/slack/PublishEventListenerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/client/WebhookClientIntegrationTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/client/WebhookClientIntegrationTest.java
index c52651c..2b2b284 100644
--- a/src/test/java/com/cisco/gerrit/plugins/slack/client/WebhookClientIntegrationTest.java
+++ b/src/test/java/com/cisco/gerrit/plugins/slack/client/WebhookClientIntegrationTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
@@ -17,6 +17,7 @@
 
 package com.cisco.gerrit.plugins.slack.client;
 
+import com.cisco.gerrit.plugins.slack.message.MessageTemplate;
 import com.cisco.gerrit.plugins.slack.util.ResourceHelper;
 import org.junit.Test;
 
@@ -43,12 +44,21 @@
 
         testProperties.close();
 
-        String message;
-        message = "{\"text\": \"Integration Test Message\"}";
+        MessageTemplate template;
+        template = new MessageTemplate();
+
+        template.setChannel("general");
+        template.setName("Integration Tester");
+        template.setAction("proposed");
+        template.setNumber(1234);
+        template.setProject("project");
+        template.setBranch("master");
+        template.setUrl("http://gerrit/1234");
+        template.setMessage("This is a really great commit.");
 
         String webhookUrl;
         webhookUrl = properties.getProperty("webhook-url");
 
-        assertTrue(client.publish(message, webhookUrl));
+        assertTrue(client.publish(template.render(), webhookUrl));
     }
 }
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/config/ProjectConfigTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/config/ProjectConfigTest.java
index a0cfc78..0810de5 100644
--- a/src/test/java/com/cisco/gerrit/plugins/slack/config/ProjectConfigTest.java
+++ b/src/test/java/com/cisco/gerrit/plugins/slack/config/ProjectConfigTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/message/ChangeMergedMessageGeneratorTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/message/ChangeMergedMessageGeneratorTest.java
index 8322b46..46baea0 100644
--- a/src/test/java/com/cisco/gerrit/plugins/slack/message/ChangeMergedMessageGeneratorTest.java
+++ b/src/test/java/com/cisco/gerrit/plugins/slack/message/ChangeMergedMessageGeneratorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
@@ -24,6 +24,7 @@
 import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.data.AccountAttribute;
 import com.google.gerrit.server.data.ChangeAttribute;
+import com.google.gerrit.server.data.PatchSetAttribute;
 import com.google.gerrit.server.events.ChangeMergedEvent;
 import org.junit.Before;
 import org.junit.Test;
@@ -117,7 +118,7 @@
         // Setup mocks
         ProjectConfig config = getConfig();
         mockEvent.change = Suppliers.ofInstance(mockChange);
-        mockChange.commitMessage = "This is a title\nAnd a the body.";
+        mockChange.commitMessage = "This is a title\nand a the body.";
 
         // Test
         MessageGenerator messageGenerator;
@@ -133,7 +134,7 @@
         // Setup mocks
         ProjectConfig config = getConfig();
         mockEvent.change = Suppliers.ofInstance(mockChange);
-        mockChange.commitMessage = "WIP:This is a title\nAnd a the body.";
+        mockChange.commitMessage = "WIP:This is a title\nand a the body.";
 
         // Test
         MessageGenerator messageGenerator;
@@ -149,7 +150,7 @@
         // Setup mocks
         ProjectConfig config = getConfig(false /* publishOnChangeMerged */);
         mockEvent.change = Suppliers.ofInstance(mockChange);
-        mockChange.commitMessage = "This is a title\nAnd a the body.";
+        mockChange.commitMessage = "This is a title\nand a the body.";
 
         // Test
         MessageGenerator messageGenerator;
@@ -163,8 +164,7 @@
     public void handlesInvalidIgnorePatterns() throws Exception
     {
         ProjectConfig config = getConfig();
-        when(mockPluginConfig.getString("ignore", ""))
-                .thenReturn(null);
+        when(mockPluginConfig.getString("ignore", "")).thenReturn(null);
 
         // Test
         MessageGenerator messageGenerator;
@@ -179,13 +179,15 @@
     {
         // Setup mocks
         ProjectConfig config = getConfig();
+
         mockEvent.change = Suppliers.ofInstance(mockChange);
         mockEvent.submitter = Suppliers.ofInstance(mockAccount);
 
+        mockChange.number = 1234;
         mockChange.project = "testproject";
         mockChange.branch = "master";
         mockChange.url = "https://change/";
-        mockChange.commitMessage = "This is a title\nAnd a the body.";
+        mockChange.commitMessage = "This is a title\nand a the body.";
 
         mockAccount.name = "Unit Tester";
 
@@ -195,9 +197,19 @@
                 mockEvent, config);
 
         String expectedResult;
-        expectedResult = "{\"text\": \"Unit Tester merged\\n>>>" +
-                "testproject (master): This is a title (https://change/)\"," +
-                "\"channel\": \"#testchannel\",\"username\": \"testuser\"}\n";
+        expectedResult = "{\n" +
+                "  \"channel\": \"#testchannel\",\n" +
+                "  \"attachments\": [\n" +
+                "    {\n" +
+                "      \"fallback\": \"Unit Tester merged - testproject (master) - change 1234 - https://change/\",\n" +
+                "      \"pretext\": \"Unit Tester merged\",\n" +
+                "      \"title\": \"testproject (master) - change 1234\",\n" +
+                "      \"title_link\": \"https://change/\",\n" +
+                "      \"text\": \"This is a title\",\n" +
+                "      \"color\": \"good\"\n" +
+                "    }\n" +
+                "  ]\n" +
+                "}\n";
 
         String actualResult;
         actualResult = messageGenerator.generate();
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/message/CommentAddedMessageGeneratorTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/message/CommentAddedMessageGeneratorTest.java
index a29d215..5877edc 100644
--- a/src/test/java/com/cisco/gerrit/plugins/slack/message/CommentAddedMessageGeneratorTest.java
+++ b/src/test/java/com/cisco/gerrit/plugins/slack/message/CommentAddedMessageGeneratorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
@@ -24,6 +24,7 @@
 import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.data.AccountAttribute;
 import com.google.gerrit.server.data.ChangeAttribute;
+import com.google.gerrit.server.data.PatchSetAttribute;
 import com.google.gerrit.server.events.CommentAddedEvent;
 import org.junit.Before;
 import org.junit.Test;
@@ -183,6 +184,7 @@
 
         mockEvent.comment = "This is the first line\nAnd the second line.";
 
+        mockChange.number = 1234;
         mockChange.project = "testproject";
         mockChange.branch = "master";
         mockChange.url = "https://change/";
@@ -197,10 +199,20 @@
                 mockEvent, config);
 
         String expectedResult;
-        expectedResult = "{\"text\": \"Unit Tester commented to Owner\\n>>>" +
-                "testproject (master): This is the first line\n" +
-                "And the second line. (https://change/)\"," +
-                "\"channel\": \"#testchannel\",\"username\": \"testuser\"}\n";
+        expectedResult = "{\n" +
+                "  \"channel\": \"#testchannel\",\n" +
+                "  \"attachments\": [\n" +
+                "    {\n" +
+                "      \"fallback\": \"Unit Tester commented on - testproject (master) - change 1234 - https://change/\",\n" +
+                "      \"pretext\": \"Unit Tester commented on\",\n" +
+                "      \"title\": \"testproject (master) - change 1234\",\n" +
+                "      \"title_link\": \"https://change/\",\n" +
+                "      \"text\": \"This is the first line\n" +
+                "And the second line.\",\n" +
+                "      \"color\": \"good\"\n" +
+                "    }\n" +
+                "  ]\n" +
+                "}\n";
 
         String actualResult;
         actualResult = messageGenerator.generate();
@@ -221,6 +233,7 @@
                 "sem neque ornare eros, vel sodales magna risus et diam. Maecenas ultricies justo dictum orci " +
                 "scelerisque consequat a vel purus.";
 
+        mockChange.number = 1234;
         mockChange.project = "testproject";
         mockChange.branch = "master";
         mockChange.url = "https://change/";
@@ -235,14 +248,23 @@
                 mockEvent, config);
 
         String expectedResult;
-        expectedResult = "{\"text\": \"Unit Tester commented to Owner\\n>>>" +
-                "testproject (master): " + mockEvent.comment.substring(0, 197) + "... (https://change/)\"," +
-                "\"channel\": \"#testchannel\",\"username\": \"testuser\"}\n";
+        expectedResult = "{\n" +
+                "  \"channel\": \"#testchannel\",\n" +
+                "  \"attachments\": [\n" +
+                "    {\n" +
+                "      \"fallback\": \"Unit Tester commented on - testproject (master) - change 1234 - https://change/\",\n" +
+                "      \"pretext\": \"Unit Tester commented on\",\n" +
+                "      \"title\": \"testproject (master) - change 1234\",\n" +
+                "      \"title_link\": \"https://change/\",\n" +
+                "      \"text\": \"" + mockEvent.comment.substring(0, 197) + "...\",\n" +
+                "      \"color\": \"good\"\n" +
+                "    }\n" +
+                "  ]\n" +
+                "}\n";
 
         String actualResult;
         actualResult = messageGenerator.generate();
 
         assertThat(actualResult, is(equalTo(expectedResult)));
     }
-
 }
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/message/MessageGeneratorTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/message/MessageGeneratorTest.java
deleted file mode 100644
index 63be613..0000000
--- a/src/test/java/com/cisco/gerrit/plugins/slack/message/MessageGeneratorTest.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2016 Cisco Systems, Inc.
- *
- * 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.cisco.gerrit.plugins.slack.message;
-
-import org.junit.Test;
-
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsEqual.equalTo;
-import static org.junit.Assert.assertThat;
-
-public class MessageGeneratorTest
-{
-
-    @Test
-    public void testEscape() throws Exception
-    {
-        MessageGenerator messageGenerator;
-        messageGenerator = new MessageGenerator()
-        {
-            @Override
-            public boolean shouldPublish()
-            {
-                return false;
-            }
-
-            @Override
-            public String generate()
-            {
-                return null;
-            }
-        };
-
-        assertThat(messageGenerator.escape("\""), is(equalTo("\\\"")));
-    }
-}
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/message/MessageTemplateTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/message/MessageTemplateTest.java
new file mode 100644
index 0000000..0e30b93
--- /dev/null
+++ b/src/test/java/com/cisco/gerrit/plugins/slack/message/MessageTemplateTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017 Cisco Systems, Inc.
+ *
+ * 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.cisco.gerrit.plugins.slack.message;
+
+import org.junit.Test;
+
+public class MessageTemplateTest
+{
+    @Test
+    public void rendersTemplate() throws Exception
+    {
+        MessageTemplate template;
+        template = new MessageTemplate();
+
+        template.setChannel("general");
+        template.setName("Mr. Developer");
+        template.setAction("proposed");
+        template.setNumber(1234);
+        template.setProject("project");
+        template.setBranch("master");
+        template.setUrl("https://gerrit-review.googlesource.com/#/admin/projects/plugins/slack-integration");
+        template.setMessage("This is a really great commit.");
+
+        System.out.println(template.render());
+    }
+}
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/message/PatchSetCreatedMessageGeneratorTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/message/PatchSetCreatedMessageGeneratorTest.java
index 1e32414..38f0485 100644
--- a/src/test/java/com/cisco/gerrit/plugins/slack/message/PatchSetCreatedMessageGeneratorTest.java
+++ b/src/test/java/com/cisco/gerrit/plugins/slack/message/PatchSetCreatedMessageGeneratorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
@@ -24,6 +24,7 @@
 import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.data.AccountAttribute;
 import com.google.gerrit.server.data.ChangeAttribute;
+import com.google.gerrit.server.data.PatchSetAttribute;
 import com.google.gerrit.server.events.PatchSetCreatedEvent;
 import org.junit.Before;
 import org.junit.Test;
@@ -192,10 +193,11 @@
         mockEvent.change = Suppliers.ofInstance(mockChange);
         mockEvent.uploader = Suppliers.ofInstance(mockAccount);
 
+        mockChange.number = 1234;
         mockChange.project = "testproject";
         mockChange.branch = "master";
         mockChange.url = "https://change/";
-        mockChange.commitMessage = "This is a title\nAnd a the body.";
+        mockChange.commitMessage = "This is a title\nand a the body.";
 
         mockAccount.name = "Unit Tester";
 
@@ -205,9 +207,19 @@
                 mockEvent, config);
 
         String expectedResult;
-        expectedResult = "{\"text\": \"Unit Tester proposed\\n>>>" +
-                "testproject (master): This is a title (https://change/)\"," +
-                "\"channel\": \"#testchannel\",\"username\": \"testuser\"}\n";
+        expectedResult = "{\n" +
+                "  \"channel\": \"#testchannel\",\n" +
+                "  \"attachments\": [\n" +
+                "    {\n" +
+                "      \"fallback\": \"Unit Tester proposed - testproject (master) - change 1234 - https://change/\",\n" +
+                "      \"pretext\": \"Unit Tester proposed\",\n" +
+                "      \"title\": \"testproject (master) - change 1234\",\n" +
+                "      \"title_link\": \"https://change/\",\n" +
+                "      \"text\": \"This is a title\",\n" +
+                "      \"color\": \"good\"\n" +
+                "    }\n" +
+                "  ]\n" +
+                "}\n";
 
         String actualResult;
         actualResult = messageGenerator.generate();
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/message/ReviewerAddedMessageGeneratorTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/message/ReviewerAddedMessageGeneratorTest.java
index 875622c..17fa9cd 100644
--- a/src/test/java/com/cisco/gerrit/plugins/slack/message/ReviewerAddedMessageGeneratorTest.java
+++ b/src/test/java/com/cisco/gerrit/plugins/slack/message/ReviewerAddedMessageGeneratorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
@@ -24,6 +24,7 @@
 import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.data.AccountAttribute;
 import com.google.gerrit.server.data.ChangeAttribute;
+import com.google.gerrit.server.data.PatchSetAttribute;
 import com.google.gerrit.server.events.ReviewerAddedEvent;
 import org.junit.Before;
 import org.junit.Test;
@@ -163,6 +164,7 @@
         mockEvent.change = Suppliers.ofInstance(mockChange);
         mockEvent.reviewer = Suppliers.ofInstance(mockAccount);
 
+        mockChange.number = 1234;
         mockChange.project = "testproject";
         mockChange.branch = "master";
         mockChange.commitMessage = "This is the first line\nAnd the second line.";
@@ -176,10 +178,19 @@
                 mockEvent, config);
 
         String expectedResult;
-        expectedResult = "{\"text\": \"Unit Tester was added to review\\n>>>" +
-                "testproject (master): This is the first line" +
-                " (https://change/)\",\"channel\": \"#testchannel\"," +
-                "\"username\": \"testuser\"}\n";
+        expectedResult = "{\n" +
+                "  \"channel\": \"#testchannel\",\n" +
+                "  \"attachments\": [\n" +
+                "    {\n" +
+                "      \"fallback\": \"Unit Tester was added to review - testproject (master) - change 1234 - https://change/\",\n" +
+                "      \"pretext\": \"Unit Tester was added to review\",\n" +
+                "      \"title\": \"testproject (master) - change 1234\",\n" +
+                "      \"title_link\": \"https://change/\",\n" +
+                "      \"text\": \"This is the first line\",\n" +
+                "      \"color\": \"good\"\n" +
+                "    }\n" +
+                "  ]\n" +
+                "}\n";
 
         String actualResult;
         actualResult = messageGenerator.generate();
diff --git a/src/test/java/com/cisco/gerrit/plugins/slack/util/ResourceHelperTest.java b/src/test/java/com/cisco/gerrit/plugins/slack/util/ResourceHelperTest.java
index 3a9c046..d70c0a8 100644
--- a/src/test/java/com/cisco/gerrit/plugins/slack/util/ResourceHelperTest.java
+++ b/src/test/java/com/cisco/gerrit/plugins/slack/util/ResourceHelperTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 Cisco Systems, Inc.
+ * Copyright 2017 Cisco Systems, Inc.
  *
  * 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
diff --git a/src/test/resources/test.properties b/src/test/resources/test.properties
index 615036b..6cfb400 100644
--- a/src/test/resources/test.properties
+++ b/src/test/resources/test.properties
@@ -1,5 +1,5 @@
 #
-# Copyright 2016 Cisco Systems, Inc.
+# Copyright 2017 Cisco Systems, Inc.
 #
 # 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