diff --git a/pom.xml b/pom.xml
index 6baea7b..97b50c4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -73,6 +73,16 @@
             <version>1.5.4</version>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>18.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.3.2</version>
+        </dependency>
         <dependency>
             <groupId>log4j</groupId>
             <artifactId>log4j</artifactId>
@@ -91,6 +101,12 @@
             <version>6.8.8</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.11</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -164,6 +180,24 @@
                     </execution>
                 </executions>
             </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>2.18.1</version>
+                <!-- For running both, junit and testng tests. -->
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.maven.surefire</groupId>
+                        <artifactId>surefire-junit4</artifactId>
+                        <version>2.18.1</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>org.apache.maven.surefire</groupId>
+                        <artifactId>surefire-testng</artifactId>
+                        <version>2.18.1</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
         </plugins>
     </build>
 
diff --git a/src/main/java/org/graylog2/syslog4j/impl/AbstractSyslog.java b/src/main/java/org/graylog2/syslog4j/impl/AbstractSyslog.java
index 0331430..97364f5 100644
--- a/src/main/java/org/graylog2/syslog4j/impl/AbstractSyslog.java
+++ b/src/main/java/org/graylog2/syslog4j/impl/AbstractSyslog.java
@@ -1,8 +1,5 @@
 package org.graylog2.syslog4j.impl;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import org.graylog2.syslog4j.SyslogBackLogHandlerIF;
 import org.graylog2.syslog4j.SyslogConfigIF;
 import org.graylog2.syslog4j.SyslogIF;
@@ -16,6 +13,9 @@ import org.graylog2.syslog4j.impl.message.structured.StructuredSyslogMessage;
 import org.graylog2.syslog4j.impl.message.structured.StructuredSyslogMessageIF;
 import org.graylog2.syslog4j.util.SyslogUtility;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * AbstractSyslog provides a base abstract implementation of the SyslogIF.
  * <p/>
@@ -115,7 +115,7 @@ public abstract class AbstractSyslog implements SyslogIF {
 
     public void log(int level, String message) {
         if (this.syslogConfig.isUseStructuredData()) {
-            StructuredSyslogMessageIF structuredMessage = new StructuredSyslogMessage(null, null, message);
+            StructuredSyslogMessageIF structuredMessage = new StructuredSyslogMessage(null, null, null, message);
 
             log(getStructuredMessageProcessor(), level, structuredMessage.createMessage());
 
diff --git a/src/main/java/org/graylog2/syslog4j/impl/message/structured/StructuredSyslogMessage.java b/src/main/java/org/graylog2/syslog4j/impl/message/structured/StructuredSyslogMessage.java
index 5f9f009..0219a5e 100644
--- a/src/main/java/org/graylog2/syslog4j/impl/message/structured/StructuredSyslogMessage.java
+++ b/src/main/java/org/graylog2/syslog4j/impl/message/structured/StructuredSyslogMessage.java
@@ -1,13 +1,16 @@
 package org.graylog2.syslog4j.impl.message.structured;
 
-import java.util.HashMap;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import org.apache.commons.lang3.StringUtils;
+import org.graylog2.syslog4j.SyslogConstants;
+import org.graylog2.syslog4j.impl.message.AbstractSyslogMessage;
+
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
 
-import org.graylog2.syslog4j.SyslogConstants;
-import org.graylog2.syslog4j.impl.message.AbstractSyslogMessage;
-
 /**
  * SyslogStructuredMessage extends AbstractSyslogMessage's ability to provide
  * support for turning POJO (Plain Ol' Java Objects) into Syslog messages. It
@@ -28,26 +31,26 @@ import org.graylog2.syslog4j.impl.message.AbstractSyslogMessage;
  * @version $Id: StructuredSyslogMessage.java,v 1.5 2010/09/11 16:49:24 cvs Exp $
  */
 public class StructuredSyslogMessage extends AbstractSyslogMessage implements StructuredSyslogMessageIF {
-    private static final long serialVersionUID = 3669887659567965965L;
-
     private String messageId;
-    private Map structuredData;
+    private Map<String, Map<String, String>> structuredData;
     private String message;
+    private String procId;
 
     private StructuredSyslogMessage() {
         this.messageId = null;
         this.message = null;
+        this.procId = null;
         this.structuredData = null;
     }
 
     /**
      * Constructs the {@link StructuredSyslogMessage} using MSGID,
      * STRUCTURED-DATA and MSG fields, as described in:
-     * <p/>
+     *
      * <p>
      * http://tools.ietf.org/html/draft-ietf-syslog-protocol-23#section-6
      * </p>
-     * <p/>
+     *
      * The Map must be a String -> (Map of String -> String), which encompasses
      * the STRUCTURED-DATA field described in above document.
      *
@@ -56,45 +59,14 @@ public class StructuredSyslogMessage extends AbstractSyslogMessage implements St
      * @param message
      */
     public StructuredSyslogMessage(final String messageId,
-                                   final Map structuredData, final String message) {
+                                   final String procId,
+                                   final Map<String, Map<String, String>> structuredData,
+                                   final String message) {
         super();
         this.messageId = messageId;
+        this.procId = procId;
         this.structuredData = structuredData;
         this.message = message;
-
-        ensureCorrectMapType();
-    }
-
-    private void ensureCorrectMapType() {
-        if (!(getStructuredData() == null)) {
-            Set sdEntrySet = getStructuredData().entrySet();
-            for (Iterator it = sdEntrySet.iterator(); it.hasNext(); ) {
-                Map.Entry sdEntry = (Map.Entry) it.next();
-                if (!(sdEntry.getKey() instanceof String)) {
-                    throw new IllegalArgumentException(
-                            "Structured data map must be a map of String -> (Map of String,String)");
-                }
-                if (!(sdEntry.getValue() instanceof Map)) {
-                    throw new IllegalArgumentException(
-                            "Structured data map must be a map of String -> (Map of String,String)");
-                }
-
-                Set entrySet = ((Map) sdEntry.getValue()).entrySet();
-                for (Iterator it2 = entrySet.iterator(); it2.hasNext(); ) {
-                    Map.Entry entry = (Map.Entry) it2.next();
-
-                    if (!(entry.getKey() instanceof String)) {
-                        throw new IllegalArgumentException(
-                                "Structured data map must be a map of String -> (Map of String,String)");
-                    }
-                    if (!(entry.getValue() instanceof String)) {
-                        throw new IllegalArgumentException(
-                                "Structured data map must be a map of String -> (Map of String,String)");
-                    }
-                }
-            }
-
-        }
     }
 
     /**
@@ -110,91 +82,82 @@ public class StructuredSyslogMessage extends AbstractSyslogMessage implements St
         return syslogMessage;
     }
 
-    private void deserialize(final String stringMessage) {
-        // Check correct format
-        if (stringMessage.indexOf('[') <= 0)
-            throw new IllegalArgumentException("Invalid Syslog string format: "
-                    + stringMessage);
+    private void  deserialize(final String stringMessage) {
 
+        int start = stringMessage.indexOf('[');
+        int end = -1;  
+
+         // Check correct format
+        if (start <= 0)
+            throw new IllegalArgumentException("Invalid Syslog string format: " + stringMessage);
+
+        //SYSLOG HEADER
         // Divide the string in 2 sections
-        final String syslogHeader = stringMessage.substring(0, stringMessage
-                .indexOf('['));
-        String structuredDataString = stringMessage.substring(stringMessage
-                .indexOf('['), stringMessage.lastIndexOf(']') + 1);
-
-        if ((stringMessage.lastIndexOf(']') + 2) <= stringMessage.length())
-            this.message = stringMessage.substring(stringMessage.lastIndexOf(']') + 2);
-
-        else {
-            this.message = "";
-        }
-
+        final String syslogHeader = stringMessage.substring(0, stringMessage.indexOf('['));
         // Split into tokens
         final String[] tokens = syslogHeader.split(" ");
 
         // Check number of tokens must be 1 -- rest of the header should already
         // be stripped
         if (tokens.length != 1) {
-            throw new IllegalArgumentException("Invalid Syslog string format: "
-                    + stringMessage);
+            throw new IllegalArgumentException("Invalid Syslog string format: " + stringMessage);
+        }
+        this.messageId = SyslogConstants.STRUCTURED_DATA_NILVALUE.equals(tokens[0]) ? null : tokens[0];
+
+        //STRUCTURED_DATA
+        if (stringMessage.contains(SyslogConstants.STRUCTURED_DATA_EMPTY_VALUE)){
+            this.structuredData = Collections.emptyMap();
+            end=stringMessage.indexOf(SyslogConstants.STRUCTURED_DATA_EMPTY_VALUE)+4;
+        } else {
+
+            final Map<String, Map<String, String>> structuredDataMap = Maps.newHashMap();
+
+            while(start < stringMessage.length() && matchChar(stringMessage, start, '[') == start) {
+                Preconditions.checkArgument(stringMessage.charAt(start) == '[', "Invalid structured data in syslog message '%s'", stringMessage);
+                end = matchChar(stringMessage, start, ']');
+                Preconditions.checkArgument(end != -1 && stringMessage.charAt(end) == ']', "Invalid structured data in syslog message '%s'", stringMessage);
+
+                String key = null;
+                Map<String, String> keyMap = Maps.newHashMap();
+                while (start < end) {
+                    if (key == null) {
+                        final int keyEnd = matchChar(stringMessage, ++start, ']', ' '); // Key can be terminated by a space (then more fields to follow) or a ]
+                        key = stringMessage.substring(start, keyEnd);
+                        start = keyEnd; // start either points after the end (then the while terminates) or at the first char of the first field.
+                    } else {
+                        Preconditions.checkArgument(start < stringMessage.length() && stringMessage.charAt(start) == ' ', "Invalid structured data in syslog message '%s'", stringMessage);
+                        start = start + 1; // Start points at the space behind either the key or the previous value
+                        Preconditions.checkArgument(key != null, "Invalid structured data in syslog message '%s'", stringMessage);
+                        final int equalsIndex = stringMessage.indexOf('=', start); // Equals terminates the field name.
+                        Preconditions.checkArgument(equalsIndex != -1, "Invalid structured data in syslog message '%s'", stringMessage);
+                        Preconditions.checkArgument(stringMessage.charAt(equalsIndex + 1) == '"', "Invalid structured data in syslog message '%s'", stringMessage);
+
+                        // Look for the end of the value. It needs to be terminated by "
+                        final int valueEnd = matchChar(stringMessage, equalsIndex + 2, '"');
+                        Preconditions.checkArgument(valueEnd !=  -1 && stringMessage.charAt(valueEnd) == '"', "Invalid structured data in syslog message '%s'", stringMessage);
+
+                        keyMap.put(stringMessage.substring(start, equalsIndex), unescape(stringMessage.substring(equalsIndex + 2, valueEnd)));
+                        start = valueEnd + 1;
+                    }
+                }
+                start++;
+                structuredDataMap.put(key, keyMap);
+            }
+            this.structuredData = structuredDataMap;
         }
 
-        this.messageId = SyslogConstants.STRUCTURED_DATA_NILVALUE.equals(tokens[0]) ? null
-                : tokens[0];
-
-        this.structuredData = new HashMap();
-        if (!SyslogConstants.STRUCTURED_DATA_EMPTY_VALUE.equals(structuredDataString)) {
-            while (!"".equals(structuredDataString)) {
-                if (!structuredDataString.startsWith("[")
-                        || structuredDataString.indexOf(']') == -1) {
-                    throw new IllegalArgumentException(
-                            "Invalid structured data format in Syslog message: "
-                                    + stringMessage);
-                }
-
-                final String structuredDataIteration = structuredDataString
-                        .substring(1, structuredDataString.indexOf(']'));
-
-                final Map iterMap = new HashMap();
-
-                final String[] params = structuredDataIteration.split(" ");
-
-                for (int i = 1; i < params.length; i++) {
-                    final String[] paramIter = params[i].split("=");
-                    if (paramIter.length != 2) {
-                        throw new IllegalArgumentException(
-                                "Invalid structured data format in Syslog message: "
-                                        + stringMessage);
-                    }
-
-                    if (!paramIter[1].startsWith("\"")
-                            || !paramIter[1].endsWith("\"")) {
-                        throw new IllegalArgumentException(
-                                "Invalid structured data format in Syslog message: "
-                                        + stringMessage);
-                    }
-
-                    iterMap.put(paramIter[0], paramIter[1].substring(1,
-                            paramIter[1].length() - 1));
-                }
-
-                this.structuredData.put(params[0], iterMap);
-
-                if (structuredDataString.indexOf(']') != structuredDataString
-                        .lastIndexOf(']')) {
-                    structuredDataString = structuredDataString
-                            .substring(structuredDataString.indexOf(']') + 1);
-                } else {
-                    structuredDataString = "";
-                }
-            }
+        //MESSAGE
+        if ((end + 2) <= stringMessage.length()){
+            this.message = stringMessage.substring(end + 2);
+        } else {
+            this.message = "";
         }
     }
 
     /**
      * Returns the MSGID field of the structured message format, as described
      * in:
-     * <p/>
+     *
      * <p>
      * http://tools.ietf.org/html/draft-ietf-syslog-protocol-23#section-6
      * </p>
@@ -208,20 +171,20 @@ public class StructuredSyslogMessage extends AbstractSyslogMessage implements St
     /**
      * Returns the structured data map. The Map is a String -> (Map of String ->
      * String), which encompasses the STRUCTURED-DATA field, as described in:
-     * <p/>
+     *
      * <p>
      * http://tools.ietf.org/html/draft-ietf-syslog-protocol-23#section-6
      * </p>
      *
      * @return Returns a Map object containing structured data.
      */
-    public Map getStructuredData() {
+    public Map<String, Map<String, String>> getStructuredData() {
         return this.structuredData;
     }
 
     /**
      * Returns the MSG field of the structured message format, as described in:
-     * <p/>
+     *
      * <p>
      * http://tools.ietf.org/html/draft-ietf-syslog-protocol-23#section-6
      * </p>
@@ -232,10 +195,15 @@ public class StructuredSyslogMessage extends AbstractSyslogMessage implements St
         return this.message;
     }
 
+    public String getProcId()
+    {
+        return procId;
+    }
+
     /*
      * (non-Javadoc)
      *
-     * @seeorg.productivity.java.syslog4j.impl.message.AbstractSyslogMessage#
+     * @see com.nesscomputing.syslog4j.impl.message.AbstractSyslogMessage#
      * createMessage()
      */
     public String createMessage() {
@@ -259,40 +227,31 @@ public class StructuredSyslogMessage extends AbstractSyslogMessage implements St
             // structured data present
             sb.append(SyslogConstants.STRUCTURED_DATA_EMPTY_VALUE);
         } else {
-            Set sdEntrySet = getStructuredData().entrySet();
-            for (Iterator it = sdEntrySet.iterator(); it.hasNext(); ) {
-                final Map.Entry sdElement = (Map.Entry) it.next();
-                final String sdId = (String) sdElement.getKey();
+            Set<Map.Entry<String, Map<String, String>>> sdEntrySet = getStructuredData().entrySet();
+            for (Iterator<Map.Entry<String, Map<String, String>>> it = sdEntrySet.iterator(); it.hasNext();) {
+                final Map.Entry<String, Map<String, String>> sdElement = it.next();
+                final String sdId = sdElement.getKey();
 
-                if (sdId == null || sdId.length() == 0
-                        || !StructuredSyslogMessage.checkIsPrintable(sdId)) {
-                    throw new IllegalArgumentException(
-                            "Illegal structured data id: " + sdId);
+                if (StringUtils.isBlank(sdId) || !StructuredSyslogMessage.checkIsPrintable(sdId)) {
+                    throw new IllegalArgumentException("Illegal structured data id: " + sdId);
                 }
 
                 sb.append('[').append(sdId);
 
-                final Map sdParams = (Map) sdElement.getValue();
+                final Map<String, String> sdParams = sdElement.getValue();
 
                 if (sdParams != null) {
-                    Set entrySet = sdParams.entrySet();
-                    for (Iterator it2 = entrySet.iterator(); it2.hasNext(); ) {
-                        Map.Entry entry = (Map.Entry) it2.next();
-                        final String paramName = (String) entry.getKey();
-                        final String paramValue = (String) entry.getValue();
+                    Set<Map.Entry<String, String>> entrySet = sdParams.entrySet();
+                    for (Iterator<Map.Entry<String, String>> it2 = entrySet.iterator(); it2.hasNext();) {
+                        Map.Entry<String, String> entry = it2.next();
+                        final String paramName = entry.getKey();
+                        final String paramValue = entry.getValue();
 
-                        if (paramName == null
-                                || paramName.length() == 0
-                                || !StructuredSyslogMessage
-                                .checkIsPrintable(paramName))
-                            throw new IllegalArgumentException(
-                                    "Illegal structured data parameter name: "
-                                            + paramName);
+                        if (StringUtils.isBlank(paramName) || !StructuredSyslogMessage.checkIsPrintable(paramName))
+                            throw new IllegalArgumentException("Illegal structured data parameter name: " + paramName);
 
                         if (paramValue == null)
-                            throw new IllegalArgumentException(
-                                    "Null structured data parameter value for parameter name: "
-                                            + paramName);
+                            throw new IllegalArgumentException("Null structured data parameter value for parameter name: " + paramName);
 
                         sb.append(' ');
                         sb.append(paramName);
@@ -306,7 +265,7 @@ public class StructuredSyslogMessage extends AbstractSyslogMessage implements St
             }
         }
 
-        if (getMessage() != null && getMessage().length() != 0) {
+        if (!StringUtils.isEmpty(getMessage())) {
             sb.append(' ');
             sb.append(StructuredSyslogMessage.nilProtect(getMessage()));
         }
@@ -342,13 +301,55 @@ public class StructuredSyslogMessage extends AbstractSyslogMessage implements St
     }
 
     public static String nilProtect(final String value) {
-        if (value == null || value.trim().length() == 0) {
+        if (StringUtils.isBlank(value)) {
             return SyslogConstants.STRUCTURED_DATA_NILVALUE;
         }
 
         return value;
     }
 
+    public static int matchChar(final String data, final int start, final char ... matchChars)
+    {
+        int ptr = start;
+        for(;;) {
+            if (ptr >= data.length()) {
+                return -1;
+            }
+
+            if (data.charAt(ptr) == '\\') {
+                ptr++;
+                ptr++;
+                continue;
+            }
+
+            if (ptr >= data.length()) {
+                return -1;
+            }
+
+            for (int i = 0; i < matchChars.length; i++) {
+                if (data.charAt(ptr) == matchChars[i]) {
+                    return ptr;
+                }
+            }
+            ptr++;
+        }
+    }
+
+    private String unescape(final String str)
+    {
+        if (str.indexOf('\\') == -1) {
+            return str;
+        }
+        final StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < str.length(); i++) {
+            if (str.charAt(i) == '\\') {
+                continue;
+            }
+            sb.append(str.charAt(i));
+        }
+        return sb.toString();
+    }
+
     public int hashCode() {
         final int prime = 31;
         int result = 1;
diff --git a/src/main/java/org/graylog2/syslog4j/server/impl/event/structured/StructuredSyslogServerEvent.java b/src/main/java/org/graylog2/syslog4j/server/impl/event/structured/StructuredSyslogServerEvent.java
index c8c2b5a..e7e93de 100644
--- a/src/main/java/org/graylog2/syslog4j/server/impl/event/structured/StructuredSyslogServerEvent.java
+++ b/src/main/java/org/graylog2/syslog4j/server/impl/event/structured/StructuredSyslogServerEvent.java
@@ -146,7 +146,7 @@ public class StructuredSyslogServerEvent extends SyslogServerEvent {
             // throw new SyslogRuntimeException(
             // "Message received is not a valid structured message: "
             // + getMessage(), e);
-            return new StructuredSyslogMessage(null, null, getMessage());
+            return new StructuredSyslogMessage(null, null, null, getMessage());
         }
     }
 }
diff --git a/src/test/java/org/graylog2/syslog4j/server/impl/structured/TestMatchChar.java b/src/test/java/org/graylog2/syslog4j/server/impl/structured/TestMatchChar.java
new file mode 100644
index 0000000..3da8012
--- /dev/null
+++ b/src/test/java/org/graylog2/syslog4j/server/impl/structured/TestMatchChar.java
@@ -0,0 +1,40 @@
+/**
+ *
+ * (C) Copyright 2008-2011 syslog4j.org
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Lesser General Public License
+ * (LGPL) version 2.1 which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/lgpl-2.1.html
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+package org.graylog2.syslog4j.server.impl.structured;
+
+import org.graylog2.syslog4j.impl.message.structured.StructuredSyslogMessage;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestMatchChar
+{
+    @Test
+    public void testMatchChar() throws Exception
+    {
+        Assert.assertEquals(2, StructuredSyslogMessage.matchChar("hello", 0, 'l'));
+        Assert.assertEquals(2, StructuredSyslogMessage.matchChar("hello", 2, 'l'));
+        Assert.assertEquals(3, StructuredSyslogMessage.matchChar("hello", 3, 'l'));
+        Assert.assertEquals(-1, StructuredSyslogMessage.matchChar("hello", 4, 'l'));
+        Assert.assertEquals(-1, StructuredSyslogMessage.matchChar("hello", 10, 'l'));
+
+        Assert.assertEquals(2, StructuredSyslogMessage.matchChar("\\ll", 0, 'l'));
+        Assert.assertEquals(-1, StructuredSyslogMessage.matchChar("\\l\\l", 0, 'l'));
+        Assert.assertEquals(-1, StructuredSyslogMessage.matchChar("\\", 0, 'x'));
+        Assert.assertEquals(-1, StructuredSyslogMessage.matchChar("foo\\", 0, 'x'));
+
+        Assert.assertEquals(-1, StructuredSyslogMessage.matchChar("this\\\"is\\ a\\ test.", 0, ' '));
+    }
+
+}
diff --git a/src/test/java/org/graylog2/syslog4j/test/message/structured/StructuredSyslogMessageTest.java b/src/test/java/org/graylog2/syslog4j/test/message/structured/StructuredSyslogMessageTest.java
new file mode 100644
index 0000000..ed345b4
--- /dev/null
+++ b/src/test/java/org/graylog2/syslog4j/test/message/structured/StructuredSyslogMessageTest.java
@@ -0,0 +1,260 @@
+/**
+ *
+ * (C) Copyright 2008-2011 syslog4j.org
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Lesser General Public License
+ * (LGPL) version 2.1 which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/lgpl-2.1.html
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+package org.graylog2.syslog4j.test.message.structured;
+
+//
+// Cleversafe open-source code header - Version 1.2 - February 15, 2008
+//
+// Cleversafe Dispersed Storage(TM) is software for secure, private and
+// reliable storage of the world's data using information dispersal.
+//
+// Copyright (C) 2005-2008 Cleversafe, Inc.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+// USA.
+//
+// Contact Information: Cleversafe, 224 North Desplaines Street, Suite 500
+// Chicago IL 60661
+// email licensing@cleversafe.org
+//
+// END-OF-HEADER
+// -----------------------
+// @author: mmotwani
+//
+// Date: Jul 15, 2009
+// ---------------------
+
+import com.google.common.collect.Maps;
+import junit.framework.TestCase;
+import org.graylog2.syslog4j.impl.message.structured.StructuredSyslogMessage;
+import org.graylog2.syslog4j.server.impl.event.structured.StructuredSyslogServerEvent;
+import org.junit.Assert;
+
+import java.net.InetAddress;
+import java.util.Map;
+
+public class StructuredSyslogMessageTest extends TestCase
+{
+   public void testFromString1()
+   {
+      final String messageStr = "msgId1 [0@0] my message!!";
+
+      final StructuredSyslogMessage message = StructuredSyslogMessage.fromString(messageStr);
+
+      assertEquals("msgId1 [0@0] my message!!",message.toString());
+      assertEquals(-108931075,message.hashCode());
+
+      assertEquals("my message!!", message.getMessage());
+      assertEquals("msgId1", message.getMessageId());
+      assertTrue(message.getStructuredData().size() == 0);
+   }
+   
+   
+   public void testFromString1a()
+   {
+      final String messageStr = "msgId1 [type a=\"[xx\\] xx\"] [first] my message!!";
+
+      final StructuredSyslogMessage message = StructuredSyslogMessage.fromString(messageStr);
+
+      assertEquals("msgId1 [type a=\"[xx\\] xx\"] [first] my message!!",message.toString());
+
+
+      assertEquals("[first] my message!!", message.getMessage());
+      assertEquals("msgId1", message.getMessageId());
+      assertEquals("[xx] xx", (message.getStructuredData().get("type")).get("a"));
+   }
+   
+   public void testFromString1b()
+   {
+      final String messageStr = "msgId1 [type a=\"[xx\\] xx\"] my [second] message!!";
+
+      final StructuredSyslogMessage message = StructuredSyslogMessage.fromString(messageStr);
+
+      assertEquals("msgId1 [type a=\"[xx\\] xx\"] my [second] message!!",message.toString());
+
+
+      assertEquals("my [second] message!!", message.getMessage());
+      assertEquals("msgId1", message.getMessageId());
+      assertEquals("[xx] xx", (message.getStructuredData().get("type")).get("a"));
+   }
+   
+   public void testFromString1c()
+   {
+      final String messageStr = "msgId1 [type a=\"[xx\\] xx\"][value b=\"c\"] my message!! [last]";
+
+      final StructuredSyslogMessage message = StructuredSyslogMessage.fromString(messageStr);
+
+      assertEquals("my message!! [last]", message.getMessage());
+      assertEquals("msgId1", message.getMessageId());
+      assertEquals("[xx] xx", (message.getStructuredData().get("type")).get("a"));
+      assertEquals("c", (message.getStructuredData().get("value")).get("b"));
+   }
+   
+   public void testFromString2()
+   {
+      final String messageStr = "msgId1 [invalid SD] my message!!";
+
+      try {
+          StructuredSyslogMessage.fromString(messageStr);
+          fail();
+
+      } catch (IllegalArgumentException iae) {
+          //
+      }
+   }
+
+   public void testFromString3()
+   {
+      final String messageStr = "msgId1 [data1 a=b] my message!!";
+
+      try {
+          StructuredSyslogMessage.fromString(messageStr);
+          fail();
+
+       } catch (IllegalArgumentException iae) {
+           //
+       }
+   }
+
+   public void testFromString4()
+   {
+      final String messageStr = "msgId1 [data1 a=\"b] my message!!";
+
+      try {
+          StructuredSyslogMessage.fromString(messageStr);
+          fail();
+
+       } catch (IllegalArgumentException iae) {
+           //
+       }
+   }
+
+   public void testFromString5()
+   {
+      final String messageStr = "msgId1 [data1 a=b\"] my message!!";
+
+      try {
+          StructuredSyslogMessage.fromString(messageStr);
+          fail();
+
+      } catch (IllegalArgumentException iae) {
+          //
+      }
+
+   }
+
+   public void testFromString6()
+   {
+      final String messageStr = "msgId1 [data1 a=\"b\"] my message!!";
+
+      final StructuredSyslogMessage message = StructuredSyslogMessage.fromString(messageStr);
+
+      assertEquals("my message!!", message.getMessage());
+      assertEquals("msgId1", message.getMessageId());
+      assertTrue(message.getStructuredData().size() == 1);
+      assertTrue((message.getStructuredData().get("data1")).size() == 1);
+      assertEquals("b", (message.getStructuredData().get("data1")).get("a"));
+   }
+
+   public void testFromString7()
+   {
+      final String messageStr =
+            "msgId1 [data1 a=\"b\"][data2 a=\"b\" x1=\"c1\" n2=\"f5\"] my message!!";
+
+      final StructuredSyslogMessage message = StructuredSyslogMessage.fromString(messageStr);
+
+      assertEquals("my message!!", message.getMessage());
+      assertEquals("msgId1", message.getMessageId());
+      assertTrue(message.getStructuredData().size() == 2);
+      assertTrue((message.getStructuredData().get("data1")).size() == 1);
+      assertTrue((message.getStructuredData().get("data2")).size() == 3);
+      assertEquals("b", (message.getStructuredData().get("data1")).get("a"));
+      assertEquals("b", (message.getStructuredData().get("data2")).get("a"));
+      assertEquals("c1", (message.getStructuredData().get("data2")).get("x1"));
+      assertEquals("f5", (message.getStructuredData().get("data2")).get("n2"));
+   }
+
+   public void testCreateMessage1()
+   {
+      final StructuredSyslogMessage message = new StructuredSyslogMessage("msgId", null, null, null);
+      assertEquals("msgId [0@0]", message.createMessage());
+   }
+
+   public void testCreateMessage2()
+   {
+      final StructuredSyslogMessage message =
+            new StructuredSyslogMessage("msgId", null, null, "my message");
+      assertEquals("msgId [0@0] my message", message.createMessage());
+   }
+
+   public void testCreateMessage3()
+   {
+      final StructuredSyslogMessage message =
+            new StructuredSyslogMessage("msgId", null, Maps.<String, Map<String, String>>newHashMap(), "my message");
+      assertEquals("msgId [0@0] my message", message.createMessage());
+   }
+
+   public void testCreateMessage4()
+   {
+      final Map<String, Map<String, String>> map = Maps.newHashMap();
+      final StructuredSyslogMessage message =
+            new StructuredSyslogMessage("msgId", null, map, "my message");
+      assertEquals("msgId [0@0] my message", message.createMessage());
+   }
+
+   public void testMessageWithNulls() throws Exception
+   {
+       final String message = "<134>1 2012-07-25T21:32:08.887+00:00 some-server.some.domain noprog qtp583592918-80437 95d42b22c48e4eadb59e61a182c102d4 [l@2 si=\"some-server-s4\" sc=\"/a/b-c/d\" ip=\"1.2.3.4\" m=\"GET\" u=\"http://1.2.3.4:8081/path/PATH:12345/path\" q=\"source=SERVICE\" rc=\"200\" t=\"12\"][co@2 auth-cookie=\"jskldjskldjasskljlaskjas\"][rs@2 some-header=\"4054630f-8d31-457c-b1ff-2f2b465d69ef\"] nomsg";
+       final StructuredSyslogServerEvent ev = new StructuredSyslogServerEvent(message, InetAddress.getLocalHost());
+       final StructuredSyslogMessage msg = ev.getStructuredMessage();
+       Assert.assertEquals("95d42b22c48e4eadb59e61a182c102d4", msg.getMessageId());
+       Assert.assertNotNull(msg.getStructuredData());
+       Assert.assertNotNull(msg.getStructuredData().get("l@2"));
+       Assert.assertEquals("/a/b-c/d", msg.getStructuredData().get("l@2").get("sc"));
+   }
+
+   public void testMessageWithEmptyStruct() throws Exception
+   {
+       final String message = "<134>1 2012-07-25T21:32:08.887+00:00 some-server.some.domain noprog qtp583592918-80437 95d42b22c48e4eadb59e61a182c102d4 [l@2][a@3 a=\"b\\\"c\"]";
+       final StructuredSyslogServerEvent ev = new StructuredSyslogServerEvent(message, InetAddress.getLocalHost());
+       final StructuredSyslogMessage msg = ev.getStructuredMessage();
+       Assert.assertEquals("95d42b22c48e4eadb59e61a182c102d4", msg.getMessageId());
+       Assert.assertNotNull(msg.getStructuredData());
+       Assert.assertNotNull(msg.getStructuredData().get("l@2"));
+       Assert.assertNotNull(msg.getStructuredData().get("a@3"));
+       Assert.assertEquals("b\"c", msg.getStructuredData().get("a@3").get("a"));
+   }
+
+    public void testMessageWithSpace() throws Exception
+    {
+    final String message = "<134>1 2012-07-25T21:32:08.887+00:00 some-server.some.domain noprog qtp583592918-80437 95d42b22c48e4eadb59e61a182c102d4 [rh@12345 xxx=\"hell0 7|133454|00022f444ad7fe10ef5d0d536ae879f1\"]'";
+       final StructuredSyslogServerEvent ev = new StructuredSyslogServerEvent(message, InetAddress.getLocalHost());
+       final StructuredSyslogMessage msg = ev.getStructuredMessage();
+       Assert.assertNotNull(msg.getStructuredData().get("rh@12345"));
+       Assert.assertEquals("hell0 7|133454|00022f444ad7fe10ef5d0d536ae879f1", msg.getStructuredData().get("rh@12345").get("xxx"));
+    }
+}