Question

In some languages it is possible to get the column number of a line in a stacktrace, however in Java we only have line numbers.

To give you an example, in another language we can have:

Error
    at <anonymous>:2:2
    at Object.InjectedScript._evaluateOn (<anonymous>:641:39)
    at Object.InjectedScript._evaluateAndWrap (<anonymous>:580:52)
    at Object.InjectedScript.evaluate (<anonymous>:495:21)"

Although this might be a bad example as I'm causing the error from the browser console, you can see the column numbers which is really helpful in solving errors.

To give you the example in Java ( Yes, names were changed ) :

Caused by: java.lang.IllegalArgumentException: path was null
    at org.jboss.resteasy.specimpl.ResteasyUriBuilder.path(ResteasyUriBuilder.java:362)
    at enterprise.money.service(AbstractSomething.java:88)

This leads to line 88 which contains

URI uri = uriInfo.getBaseUriBuilder().path(objectA).path(objectB).build();

With the stacktrace I have I can't check which .path call caused the exception. So my question is, are there any solutions that allow me to get the reference of the column?

( To guard from some possible alternative answers, we need a solution to get the column numbers, other answers like how to step through the debugger or refactoring every builder pattern, etc., will not answer the question )

Was it helpful?

Solution

This is not possible.

You can bypass it, by formatting your code in that way:

URI uri = uriInfo
           .getBaseUriBuilder()
           .path(objectA)
           .path(objectB)
           .build();

In Oracle Java 8 you can write

public static void main(String... ignored) {
    List<String> s = new ArrayList<>();
    s.add("hello");

    Predicate<? super String> predicate1 = (t) -> t.length() > 0;
    Predicate<? super String> predicate2 = (t) -> t.length() < 8;
    Predicate<? super String> predicate3 = null;

    List<String> collect = s.stream()
            .filter(predicate1) // 16
            .filter(predicate2) // 17
            .filter(predicate3) // 18
            .collect(Collectors.toList());
}

and you get

Exception in thread "main" java.lang.NullPointerException
    at java.util.Objects.requireNonNull(Objects.java:203)
    at java.util.stream.ReferencePipeline.filter(ReferencePipeline.java:161)
    at Example.main(Example.java:18)

For the Oracle JDK, this appears to be only in Java 8, not Java 7.

OTHER TIPS

Reverse engineering this for Java would be pretty painful. In theory you could analyse the original source and work out which expression(s) could throw such an exception.

If you have this as an issue, it is highly likely your lines are too complicated/dense. Most IDEs make it really easy to refactor expressions to extract portions of code. This will give you more resolution of where an exception was thrown.


Another solution to this specific problem is to have methods with @NotNull and the IDE can detect which arguments could be null, but should never be.

URI uri = uriInfo.getBaseUriBuilder().path(objectA)
                                     .path(/*highlighted*/objectB).build();

The IDE can warn you that you are passing a variable to a method which can't take a null If you do this, you will pick up these bugs much earlier and it make it easier to fix. (The IDE comes with some quick fixes)

Note: generally speaking a incorrectly passed null argument the API should throw a NullPointerException IMHO, however often an IllegalArgumentException is thrown somewhat inconsistently.

As I see it, an argument for IllegalArgumentException is that;

  • IllegalArgumentException is something you might expect the application to catch. Catching NullPointerException really smells.
  • The handling of a invalid null is expected to be the same as other incorrect arguments such as a length of -1

IMHO passing null to a method which doesn't accept a null is a programming bug, not a matter of happening to provide an incorrect input.

You can't get the columns from the stack trace.

What you can do is using intermediate variables, so that you have each method call on a different line:

UriBuilder builder = uriInfo.getBaseUriBuilder();
builder = builder.path(objectA);
builder = builder.path(objectB);
URI uri = builder.build();

This is painful, but it can temporarily help finding the problem, then you can put it back as it was.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top