Repository: rollbar/rollbar-java (java)

Issue:
Exception in Body.fromError(Throwable)
I get the following Exception when creating a custom Payload from a Throwable:
`com.rollbar.utilities.ArgumentNullException: 'filename' cannot be null`.

I do not understand why the Frame class calls `com.rollbar.utilities.Validate.isNotNullOrWhitespace` during construction. The Throwable which is passed could have StackTraceElements assigned whose filename is set to null.

As a workaround I currently rewrite the StackTraceElements of my Throwables to substitute a null filename with a String such as "[unknown]". But I think the framework should do this.

What do you think?

Here is the full StackTrace without workaround applied:
```
com.rollbar.utilities.ArgumentNullException: 'filename' cannot be null
	at com.rollbar.utilities.Validate.isNotNullOrWhitespace(Validate.java:20)
	at com.rollbar.payload.data.body.Frame.<init>(Frame.java:65)
	at com.rollbar.payload.data.body.Frame.fromStackTraceElement(Frame.java:106)
	at com.rollbar.payload.data.body.Frame.fromThrowable(Frame.java:88)
	at com.rollbar.payload.data.body.Trace.fromThrowable(Trace.java:50)
	at com.rollbar.payload.data.body.Body.trace(Body.java:61)
	at com.rollbar.payload.data.body.Body.fromError(Body.java:49)
	at com.rollbar.payload.data.body.Body.fromError(Body.java:34)
```

Pull request:
Allow filename to be null, turn into [unknown] when serializing to json
Fixes #44 

Patch:
diff --git a/.gitignore b/.gitignore
index 1524529..1850c9d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,7 @@
 *.iml
 .idea
 **/target
-**/.flattened-pom.xml
\ No newline at end of file
+**/.flattened-pom.xml
+.classpath
+.project
+.settings
diff --git a/rollbar-payload/src/main/java/com/rollbar/payload/data/body/Frame.java b/rollbar-payload/src/main/java/com/rollbar/payload/data/body/Frame.java
index b9a4d56..c50021d 100644
--- a/rollbar-payload/src/main/java/com/rollbar/payload/data/body/Frame.java
+++ b/rollbar-payload/src/main/java/com/rollbar/payload/data/body/Frame.java
@@ -26,9 +26,8 @@ public class Frame implements JsonSerializable {
     /**
      * Constructor
      * @param filename the name of the file in which the error occurred
-     * @throws ArgumentNullException if filename is null
      */
-    public Frame(String filename) throws ArgumentNullException {
+    public Frame(String filename) {
         this(filename, null, null, null, null, null, null, null, null);
     }
 
@@ -42,9 +41,8 @@ public class Frame implements JsonSerializable {
      * @param context extra context around the line of code that triggered the error
      * @param args the arguments to the method from the stack frame (if available in your language)
      * @param keywordArgs the keyword arguments to the method from the stack frame (if available in your language)
-     * @throws ArgumentNullException if filename is null
      */
-    public Frame(String filename, Integer lineNumber, Integer columnNumber, String method, String code, CodeContext context, Object[] args, Map<String, Object> keywordArgs) throws ArgumentNullException {
+    public Frame(String filename, Integer lineNumber, Integer columnNumber, String method, String code, CodeContext context, Object[] args, Map<String, Object> keywordArgs) {
         this(filename, lineNumber, columnNumber, method, code, null, context, args, keywordArgs);
     }
 
@@ -59,10 +57,8 @@ public class Frame implements JsonSerializable {
      * @param context extra context around the line of code that triggered the error
      * @param args the arguments to the method from the stack frame (if available in your language)
      * @param keywordArgs the keyword arguments to the method from the stack frame (if available in your language)
-     * @throws ArgumentNullException if filename is null
      */
-    public Frame(String filename, Integer lineNumber, Integer columnNumber, String method, String code, String className, CodeContext context, Object[] args, Map<String, Object> keywordArgs) throws ArgumentNullException {
-        Validate.isNotNullOrWhitespace(filename, "filename");
+    public Frame(String filename, Integer lineNumber, Integer columnNumber, String method, String code, String className, CodeContext context, Object[] args, Map<String, Object> keywordArgs) {
         this.filename = filename;
         this.lineNumber = lineNumber;
         this.columnNumber = columnNumber;
@@ -117,9 +113,8 @@ public class Frame implements JsonSerializable {
     * Set filename on a copy of this Frame
     * @param filename the name of the file in which the error occurred
     * @return a copy of this Frame with the filename overridden
-    * @throws ArgumentNullException if filename is null
     */
-    public Frame filename(String filename) throws ArgumentNullException {
+    public Frame filename(String filename) {
         return new Frame(filename, lineNumber, columnNumber, method, code, className, context, args, keywordArgs);
     }
 
@@ -264,24 +259,31 @@ public class Frame implements JsonSerializable {
         Object[] args = args();
         Map<String, Object> keywordArgs = keywordArgs();
 
-        if (filename != null)
-            obj.put("filename", filename);
-        if (lineNumber != null)
+        obj.put("filename", filename != null ? filename : "[unknown]");
+        if (lineNumber != null) {
             obj.put("lineno", lineNumber);
-        if (columnNumber != null)
+        }
+        if (columnNumber != null) {
             obj.put("colno", columnNumber);
-        if (method != null)
+        }
+        if (method != null) {
             obj.put("method", method);
-        if (code != null)
+        }
+        if (code != null) {
             obj.put("code", code);
-        if (className != null)
+        }
+        if (className != null) {
             obj.put("class_name", className);
-        if (context != null)
+        }
+        if (context != null) {
             obj.put("context", context);
-        if (args != null)
+        }
+        if (args != null) {
             obj.put("args", args);
-        if (keywordArgs != null)
+        }
+        if (keywordArgs != null) {
             obj.put("kwargs", keywordArgs);
+        }
 
         return obj;
     }


Test patch:
diff --git a/rollbar-payload/src/test/java/com/rollbar/payload/data/body/FrameTest.java b/rollbar-payload/src/test/java/com/rollbar/payload/data/body/FrameTest.java
index 35d28f9..5ecaa86 100644
--- a/rollbar-payload/src/test/java/com/rollbar/payload/data/body/FrameTest.java
+++ b/rollbar-payload/src/test/java/com/rollbar/payload/data/body/FrameTest.java
@@ -30,12 +30,7 @@ public class FrameTest {
             }
 
             public Frame set(Frame frame, String val) {
-                try {
-                    return frame.filename(val);
-                } catch (ArgumentNullException e) {
-                    fail("Neither is null");
-                    return null;
-                }
+                return frame.filename(val);
             }
         });
     }
@@ -166,4 +161,12 @@ public class FrameTest {
         assertFalse(json.containsKey("args"));
         assertFalse(json.containsKey("kwargs"));
     }
+
+    @Test
+    public void testNullFilename() {
+        Frame frame = new Frame(null);
+        Map<String, Object> json = frame.asJson();
+        assertTrue(json.containsKey("filename"));
+        assertEquals(json.get("filename"), "[unknown]");
+    }
 }
\ No newline at end of file