question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Feature Request: use of inner / nested classes and lambda expressions

See original GitHub issue

Today, I tested a workflow using a lambda expression:

@FunctionalInterface
public interface MyFuncInterface<V> {
        V call() throws Interrupt;
}
        MyFuncInterface test = () -> {
            logger.info("Before wait in lambda");
            wait(WaitMode.FIRST, 15 * 1000, UUID.randomUUID().toString());
            logger.info("After wait in lambda");
            return "hello";
        };
        test.call();

Works fine.

Actually you can’t do

Stream.of(1,2,3).forEach(elem -> {
   // ....
   wait(WaitMode.FIRST, 10*1000, UUID.randomUUID().toString());
   //...
}

by default.

Possible solutions, as suggested by theoDiefenthal:

  • Redeclare Interrupt to be of type RuntimeException. This could potentially break clients in really nasty ways, e.g. if they have a global try … catch (RuntimeException) in their code, their workflows would suddenly stop working in the expected way.
  • Can we manipulate something in bytecode here? For COPPER to work, we have to manipulate the bytecode anyways. So could we trick the java compiler so that e.g. Callable would not just be declared as throws Exception but also Interrupt. The problem here is that the code wouldn’t even compile so we couldn’t use ASM which works on the compiled code and does its manipulations in there.
  • We just live with the way it is as there are no better solutions?!

Issue Analytics

  • State:open
  • Created 5 years ago
  • Comments:12 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
theodiefenthalcommented, Oct 10, 2018

Would it be an option to make Interrupt extend from RuntimeException but to modify the Workflow bytecode in a way that we alter through each try…catch block in order to assert our special handling? We’d just would need to transform from

try { ... 
} catch (RuntimeException e) {
 //some handling here
 }

to

try { ...
} catch (Interrupt e) {
   throw e;
} catch (RuntimeException e) {
   //some handling here
}

We’d also lose the restriction to “define any method which uses wait” with throws Interrupt in it’s signature. And as we modify the workflow bytecode heavily anyway, do that trick on all try catch blocks won’t harm any further. Or would other problems arise?


And for those people like me who didn’t get the trick from @siordache direcly, here is a small manual on how it works: https://www.baeldung.com/java-sneaky-throws

From that I found another alternate solution: “The compiler sees the signature with the throws T inferred to a RuntimeException type, so it allows the unchecked exception to propagate. The Java Runtime doesn’t see any type in the throws as all throws are the same a simple throw e.” and “It’s possible to throw a checked exception using bytecode manipulation, or Thread.stop(Throwable), but it’s messy and not recommended.” => So it should be possible to keep “Interrupt” as a subtype of Throwable and we just remove the throws Interrupt from the signature of wait in a workflow?! We then keep on requiring only that the workflow main method has a signature of throws Interrupt so that the COPPER processor is able to catch that checked Interrupt exception explicitly. If this works, this would in my opinion be the best solution.

1reaction
siordachecommented, Oct 10, 2018

Another option is to provide a helper class as shown below:

import org.copperengine.core.Interrupt;

public class CopperUtil {
    @FunctionalInterface
    public static interface CopperRunnable {
        void run() throws Interrupt;
    }

    public static void runInterruptable(CopperRunnable copperRunnable) {
        try {
            copperRunnable.run();
        } catch (Interrupt interrupt) {
            sneakyThrow(interrupt);
        }
    }

    @SuppressWarnings("unchecked")
    private static <E extends Throwable> void sneakyThrow(Throwable throwable) throws E {
        throw (E) throwable;
    }
}

With the appropriate import static ... you can then write:

Stream.of(1,2,3).forEach(elem -> runInterruptable(() -> {
    // ....
    wait(WaitMode.FIRST, 10*1000, UUID.randomUUID().toString());
    //...
}));
Read more comments on GitHub >

github_iconTop Results From Across the Web

When to Use Nested Classes, Local Classes, Anonymous ...
Use it if you are encapsulating a single unit of behavior that you want to pass to other code. For example, you would...
Read more >
Difference between Anonymous Inner Class and Lambda ...
Inside Anonymous inner class, “this” always refers to current anonymous inner class object but not to outer object. Inside Lambda Expression, “ ...
Read more >
Java Nested Classes and Lambda Expressions
Inner class should be used if access to an enclosing instance's non-public fields and methods are required. Static class should be used if...
Read more >
Interfaces, Lambda Expressions, and Inner Classes in Java
However, the Java inner classes have an additional feature that makes them richer and more useful than nested classes in C++.
Read more >
15.4 Inner Class with Lambda Expression - YouTube
Check out our website: http://www.telusko.com Follow Telusko on Twitter: https://twitter.com/navinreddy20 Follow on Facebook: Telusko ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found