Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Printing debug info on errors with java 8 lambda expressions

I want to use a static method as setter helper that catch exceptions and print debug info about the operation that failed. I don't want the exception details only. I want to show what property was being set so that detail help to debug the problem quickly. I am working with Java 8.

How should I provide or detect the property being set?

What I wish is to remove the "name" string in the example and get the same result.

I know I can't use reflection over the supplied setter method supplied that is transformed to lambda expression and then to BiConsumer.

I got this but the property name needs to be provided.

/** setter helper method **/
private static <E, V> void set(E o, BiConsumer<E, V> setter,
        Supplier<V> valueSupplier, String propertyName) {
    try {
        setter.accept(o, valueSupplier.get());
    } catch (RuntimeException e) {
        throw new RuntimeException("Failed to set the value of " + propertyName, e);
    }
}

Example:

    Person p = new Person();
    Supplier<String> nameSupplier1 = () ->  "MyName";
    Supplier<String> nameSupplier2 = () -> { throw new RuntimeException(); };
    set(p, Person::setName, nameSupplier1, "name");
    System.out.println(p.getName()); // prints MyName
    set(p, Person::setName, nameSupplier2, "name"); // throws exception with message  Failed to set the value of name
    System.out.println(p.getName()); // Does not execute

EDIT: I know reflection does not help with the lambdas. I know AOP and I know this can be made with pure reflection too but I want to know if there a better way to get this done with Java 8 that didn't exist with Java 7. It seems it should to me. Now it is possible to do things like to pass a setter method to another one.

like image 335
aalku Avatar asked Feb 18 '14 17:02

aalku


People also ask

How do you debug a lambda expression?

We can use different IDE's like Netbeans, IntelliJ, and Eclipse to debug the lambda expressions in Java. It is always possible to create multi-line lambda expressions and use print statements to display the values of a variable. The debugger can also provide additional information about the state of a java program.

How do I print a list in lambda expression?

// Create a list from a String array List list = new ArrayList(); String[] strArr = { "Chris", "Raju", "Jacky" }; for( int i = 0; i < strArr. length; i++ ) { list. add( strArr[i]); } // // Print the key and value of Map using Lambda expression // list. forEach((value) -> {System.

Does Java 1.8 support lambda expressions?

Lambda expressions are a new and important feature included in Java SE 8. They provide a clear and concise way to represent one method interface using an expression. Lambda expressions also improve the Collection libraries making it easier to iterate through, filter, and extract data from a Collection .

How do you evaluate lambda expression in IntelliJ?

You can use the Run to cursor function on a lambda expression as well. You can use Ctrl+F8 to add a breakpoint. Then the lambda expression that the cursor is placed on will be selected in the list. Pressing Ctrl+F8 again will complete creating a breakpoint on the selected lambda.


2 Answers

In case you expect method references as the only input, you can debug them to printable names with the following trick:

public static void main(String[] args) {
  Person p = new Person();
  Supplier<String> nameSupplier1 = () -> "MyName";
  Supplier<String> nameSupplier2 = () -> { throw new RuntimeException(); };
  set(p, Person::setName, nameSupplier1);
  System.out.println(p.getName()); // prints MyName
  set(p, Person::setName, nameSupplier2); // throws exception with message
  System.out.println(p.getName()); // Does not execute
}

interface DebuggableBiConsumer<A, B> extends BiConsumer<A, B>, Serializable {}

private static <E, V> void set(
    E o, DebuggableBiConsumer<E, V> setter, Supplier<V> valueSupplier) {
  try {
    setter.accept(o, valueSupplier.get());
  } catch (RuntimeException e) {
    throw new RuntimeException("Failed to set the value of "+name(setter), e);
  }
}

private static String name(DebuggableBiConsumer<?, ?> setter) {
  for (Class<?> cl = setter.getClass(); cl != null; cl = cl.getSuperclass()) {
    try {
      Method m = cl.getDeclaredMethod("writeReplace");
      m.setAccessible(true);
      Object replacement = m.invoke(setter);
      if(!(replacement instanceof SerializedLambda))
        break;// custom interface implementation
      SerializedLambda l = (SerializedLambda) replacement;
      return l.getImplClass() + "::" + l.getImplMethodName();
    }
    catch (NoSuchMethodException e) {}
    catch (IllegalAccessException | InvocationTargetException e) {
      break;
    }
  }
  return "unknown property";
}

The limitations are that it will print not very useful method references for lambda expressions (references to the synthetic method containing the lambda code) and "unknown property" for custom implementations of the interface.

like image 63
Holger Avatar answered Oct 24 '22 10:10

Holger


What about that?

Source code:

package test;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;

public class Person {
    public String getName() {
        return "Michael";
    }

    public static void main(String[] args) throws
            NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Serializable s = (Function<Person, String> & Serializable) Person::getName;

        Method method = s.getClass().getDeclaredMethod("writeReplace");
        method.setAccessible(true);
        SerializedLambda serializedLambda = (SerializedLambda) method.invoke(s);

        System.out.println(serializedLambda.getImplClass().replace("/", ".")
                + "::" + serializedLambda.getImplMethodName());
    }
}

Output:

test.Person::getName

You actually need to look up the method "writeReplace", call it and evaluate its return value which is of type SerializedLambda.

like image 29
mmirwaldt Avatar answered Oct 24 '22 09:10

mmirwaldt