electronicarts / ea-async Goto Github PK
View Code? Open in Web Editor NEWEA Async implements async-await methods in the JVM.
Home Page: https://go.ea.com/ea-async
License: Other
EA Async implements async-await methods in the JVM.
Home Page: https://go.ea.com/ea-async
License: Other
I have used this code as a basis for an async/await transformer. I have changed the api significantly, which is why I am not going to submit a pull request. I would not mind discussing the changes though, so we can make both projects better or possibly merge them at some point, but this is for another time.
I have retained the licence text in 2 source filed that I used from this project.
Q1: Is it enough to retain the licence text for these files only or does the licence automatically apply to all the artifact?
I would like to publish the compiled artifact to maven central. Since the licence states:
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Q2: Do I need to add anything to the compiled artifact? or just deploying the source jar with the licence text in the source files be enough?
Thanks in advance
Any plans for LTS Java 11? :)
The instrumentation tool takes away all Async.await
occurrences, but does not take away Async::await
. I think this is a bug?
E.g.
final List<CompletableFuture<Void>> futures = new ArrayList<>();
futures.add(CompletableFuture.completedFuture(null));
futures.stream().map(Async::await).collect(Collectors.toList());
The Async::await
stays after instrumentation.
I'm testing using ea-async with a SpringBoot 5.x application I am working on. When I try to compile and run this on Java 9 I get the following error:
java.lang.IncompatibleClassChangeError: Method java.util.function.Function.identity()Ljava/util/function/Function; must be InterfaceMethodref constant
I am doing the instrumentation at compile-time via the maven plugin.
A quick Googlin' seems to indicate that the Java bytecode validator got a little better/stricter with Java 9 so what worked with Java 8 is no longer valid.
public CompletableFuture<Boolean> buyItem(String itemTypeId, int cost) {
return bank.decrement(cost)
.thenCompose(decResult -> {
if (decResult) return completedFuture(false);
return completedFuture(null) // defer error
.thenCompose(_void -> inventory.giveItem(itemTypeId))
.handle((giveResult, t) -> {
if (t == null) return completedFuture(true);
return bank.refund(cost)
.<Boolean>thenApply(refundResult -> {
throw new RuntimeException(t);
});
})
.thenCompose(Function.identity());
});
}
I'm not saying it's prettier than the example, but I don't think this is ugly.
I suggest adding an async static method to the Async class,like this
public static <T>CompletableFuture<T> async(Supplier<T> supplier){
return CompletableFuture.supplyAsync(supplier);
}
public static <T> CompletableFuture<T> async(T object){
return CompletableFuture.completedFuture(object);
}
The reason is that providing a more convenient way to create asynchronous tasks is the default choice
The program will be written like this
public class TestMain {
public static void main(String[] args) throws InterruptedException {
Async.init();
TestMain main = new TestMain();
CompletableFuture<Boolean> call = main.call();
// Boolean aBoolean = call.get();
// System.out.println(aBoolean);
call.thenAccept((result) -> {
System.out.println(result);
});
Thread.sleep(10000);
}
public CompletableFuture<Boolean> call(){
Boolean await = await(isExist());
Boolean await1 = await(timeout());
return async(await);
}
public CompletableFuture<Boolean> isExist(){
return async(() -> true);
}
public CompletableFuture<Boolean> timeout(){
return async(() -> {
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
});
}
}
This will hide the details of constructing CompletableFuture
What do you think?
Something like this? You can argue whether it looks ugly but I still think it's not that bad.
public CompletableFuture<Boolean> buyItem(String itemTypeId, int cost) {
CompletableFuture<Boolean> cf = new CompletableFuture<Boolean>();
bank.decrement(cost).thenAccept(ok -> {
if (!ok) {
cf.complete(false);
} else {
inventory.giveItem(itemTypeId).whenComplete((s, ex) -> {
if (ex == null) {
cf.complete(true);
} else {
bank.refund(cost).thenAccept(ok2 -> {
cf.completeExceptionally(new AppException(ex));
});
}
});
}
});
return cf;
}
:)
This is a strange one. Under very specific circumstances, our code is getting a verification error on startup when using runtime instrumentation. I've tried stripping it down to its bare essence, but it doesn't really make a lot of sense to me. I'm sure there's a larger pattern I'm not seeing.
Basically you need a local String initialized to null then an numeric variable initialized to anything, and a subsequent await statement.
I get this error, along with a lot of error output: java.lang.VerifyError: Bad local variable type
Here's and example I added to BasicTest.java
@Test(timeout = 2_000)
public void testNonInitilized() throws IllegalAccessException, InstantiationException
{
final SomethingWithNonInitialized a = new SomethingWithNonInitialized();
CompletableFuture<String> blocker = new CompletableFuture<>();
final CompletableFuture<Object> res = a.doSomething(blocker);
blocker.complete("x");
assertEquals(":x", res.join());
}
public static class SomethingWithNonInitialized
{
public CompletableFuture<Object> doSomething(CompletableFuture<String> blocker)
{
//these variables must occur in this order. It doesn't matter if they are set later or not.
//Remove the initializers and then it works
String var2 = null; //this variable must be a string and initialized to null
byte var3 = 0; //this variable must be numeric and initialized to anything
return CompletableFuture.completedFuture(":" + await(blocker));
}
}
Sorry for the ugly formatting. Code formatting isn't working. I've also included BasicTest.java
If you forget to instrument the code, the wrong warning message is printed:
WARN com.ea.async.Async - Warning: Illegal call to await, the method invoking await must return a CompletableFuture
instead of
Warning: Illegal call to await, static { Async.init(); } must be added to the main program class and the method invoking await must return a CompletableFuture
As shown by the following simple example:
import java.util.concurrent.CompletableFuture;
import com.ea.async.Async;
public class EAsyncTest {
static CompletableFuture<String> convert(CompletableFuture<?> future) {
Object result = Async.await(future);
return CompletableFuture.completedFuture(result.toString());
}
public static void main(String[] args) {
convert(new CompletableFuture<>());
}
}
This is because calling InitializeAsync.isRunning()
triggers the class initialization, and only enters that method once isRunning = true
. The only ways to have isRunning()
returning false is if either the initialization crashes or if called from multiple threads.
In addition, I think it would be better to throw an exception than calling join()
. In the above example this causes a deadlock, and you don't know which method it is referring to.
Hi, thanks for great jobs!
I tried this in spring boot service, I found that await works like sync function to service, am I right ?
consider codes:
`
@ResponseBody()
@PostMapping("query.do")
public QueryResult query(@RequestParam int pageNum) {
QueryResult result = new QueryResult(); <1>
result.setResult(await(queryResult(pageNum))); <2>
return result; <3>
}
`
In fact, this function only return after <2> queryResult is done?
with this paramter in plugin.xml , plugin will fork some plugin executions ,and run twice ,such as
maven-resources-plugin
maven-compiler-plugin
can you explain the meaning of "executePhase" and "executeGoal" and why should we run the plugin before "process-classes" phase again?
Hi, first I just want to say - great job!
I noticed that when I use this library I cannot debug a method that has been modified, when I try to I receive the following error:
JDWP exit error JVMTI_ERROR_NONE(0): getting frame location [stepControl.c:641]
is this a limitation that you planning to resolve?
await(blockSomeThingFuture());
just only equals to
this.blockSomeThingFuture().toCompletableFuture().join(); // block!!!
Why is it so complicated?
Or I didn’t understand, I look forward to your sharing
Hi,
I am reporting this observation that came up when we noticed our instances getting OOM killed.
This happens in any sort of loop, if the future completes instantly, no issue, but if it enters the synthetic method that the instrumentation creates, all the completable futures will not be GC'd till that method exits.
If you have this loop logic in one method and another method awaits on it, the problem persists, but now in the "awaiting" method.
If you lets say change infinite loops to limited for's, now the same "build-up" of futures will happen on the method that awaits on the looping method to complete (if that awaiting method is looping/retrying itself).
Recursive calls, separate thread completion and so on all retain the same issue.
Is there a way to attempt fixing this, I am willing to contribute but I lack experience on this specific instrumentation topic.
Thank you.
I'm just trying out ea-async on a project... while compiling with maven works great, I'm having trouble integrating this in my dev-workflow where I'm using intellij idea.
Usually, libraries like this one (e.g. immutables.org) provide a annotation-processor that I can plug into my IDE workflow.. at the moment, I'm unable to get idea to run the maven plugin on automatic recompilation (which I have enabled through JRebel)
Hi, is there any way to use this for GWT? I tried to put <inherits name="com.ea.async.Async" />
in my GdxDefinition.gwt.xml and api "com.ea.async:ea-async:$eaAsyncVersion:sources"
in my GWT build.gradle but the build fails. I already used await all over the place in my desktop and Android code so it would be too troublesome to redo all of that and remove this library.
How do you cancel a Promise that was submitted to await() ?
Will I be able to call .cancel() while it's being processed ?
If you initialize a local variable to null, then call an await() within a try catch block, you get ClassNotFoundException on the class in which you did that, during that classes initialization.
Simply by removing the null initialization works around the issue.
@Async
public Task<Void> doStuff(Task<Void> otherStuff) {
String x = null; //bad
//String x; //ok
try {
await(otherStuff);
} catch (Exception e) {
throw e;
}
return Task.done();
}
package test;
import java.util.concurrent.CompletableFuture;
import com.ea.async.Async;
public class TestAsync {
public static CompletableFuture<Void> test() {
for(;;) Async.await(new CompletableFuture<Void>());
}
public static void main(String[] args) {
Async.init();
}
}
java -cp ../lib/asm-debug-all-5.0.4.jar;. -javaagent:../lib/async-stub.jar test.TestAsync
java.lang.RuntimeException: Error instrumenting: test/TestAsync
at com.ea.async.instrumentation.Transformer.transform(Transformer.java:163)
at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
......
Caused by: java.lang.IllegalStateException
at org.objectweb.asm.MethodWriter.visitFrame(MethodWriter.java:659)
at org.objectweb.asm.tree.FrameNode.accept(FrameNode.java:148)
at org.objectweb.asm.tree.InsnList.accept(InsnList.java:162)
at org.objectweb.asm.tree.MethodNode.accept(MethodNode.java:817)
at org.objectweb.asm.tree.MethodNode.accept(MethodNode.java:727)
at org.objectweb.asm.tree.ClassNode.accept(ClassNode.java:412)
at com.ea.async.instrumentation.Transformer.transform(Transformer.java:337)
at com.ea.async.instrumentation.Transformer.transform(Transformer.java:153)
... 15 more
At first, when I tried to run my .jar which uses the ea async dependency, I got the error that it couldn't find the class agend in my manifest. Therefore, I added
Agent-Class: com.ea.async.instrumentation.Agent
to my MANIFEST.MF and it seemed to fix this error, but now I got a new one and I really don't know what I am doing wrong.
It's a maven project so that's what I did:
import static com.ea.async.Async.await;
like on the examples.It works fine if I start it in my IDE (Intellij / Javac 11), but does not if I put the compiled .jar (which contains the com.ea.async dir) on my debian 9 server and try to launch it there (it runs with open jdk 11).
I am grateful for any help! 🙏
This is my error (Servant is the name of my program):
Exception in thread "Attach Listener" java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:513)
at java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:535)
Caused by: java.lang.UnsupportedOperationException: adding retransformable transformers is not supported in this environment
at java.instrument/sun.instrument.InstrumentationImpl.addTransformer(InstrumentationImpl.java:94)
at com.ea.async.instrumentation.Agent.agentmain(Agent.java:52)
... 6 more
Agent failed to start!
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.ea.async.Async.init(Async.java:79)
at servant.Servant.main(Servant.java:79)
Caused by: java.lang.RuntimeException: Error attaching ea-async java agent
at com.ea.async.instrumentation.InitializeAsync.<clinit>(InitializeAsync.java:99)
... 2 more
Caused by: java.lang.RuntimeException: Error activating orbit-async agent from /home/bots/rin/Rin.jar
at com.ea.async.instrumentation.InitializeAsync.loadAgent(InitializeAsync.java:211)
at com.ea.async.instrumentation.InitializeAsync.<clinit>(InitializeAsync.java:80)
... 2 more
Caused by: java.lang.IllegalStateException: Could not self-attach to current VM using external process
at com.ea.async.shaded.net.bytebuddy.agent.ByteBuddyAgent.installExternal(ByteBuddyAgent.java:486)
at com.ea.async.shaded.net.bytebuddy.agent.ByteBuddyAgent.install(ByteBuddyAgent.java:420)
at com.ea.async.shaded.net.bytebuddy.agent.ByteBuddyAgent.attach(ByteBuddyAgent.java:248)
at com.ea.async.shaded.net.bytebuddy.agent.ByteBuddyAgent.attach(ByteBuddyAgent.java:223)
at com.ea.async.shaded.net.bytebuddy.agent.ByteBuddyAgent.attach(ByteBuddyAgent.java:210)
at com.ea.async.instrumentation.InitializeAsync.loadAgent(InitializeAsync.java:205)
... 3 more
First I'd like to congratulate you on going 1.0. I've been watching this project since it was a part of Orbit last year with anticipation as I currently write a lot of asynchronous code in Java and having come from C# found it incredibly tedious, even after the addition of CompletableFuture<T>
.
I am currently trying to play with a sample project to get my feet wet and I'm getting a VerifyError
when I try to run the project. I have the two following source files:
package com.halofour;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
public class Main {
public static void main(String[] args) throws Throwable {
CompletableFuture<String> future = new CompletableFuture<>();
CompletionStage<String> stage = MyAsyncClass.testAsync(future);
}
}
package com.halofour;
import java.util.concurrent.CompletionStage;
import static com.ea.async.Async.await;
import static java.util.concurrent.CompletableFuture.completedFuture;
public class MyAsyncClass {
public static CompletionStage<String> testAsync(CompletionStage<String> stage) {
String result = await(stage);
return completedFuture(result);
}
}
And my pom file is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<name>sandbox</name>
<modelVersion>4.0.0</modelVersion>
<groupId>com.halofour</groupId>
<artifactId>sandbox</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<forkCount>1</forkCount>
<systemPropertyVariables>
<unit-test>true</unit-test>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<maxmem>512m</maxmem>
<meminitial>128m</meminitial>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.5</version>
<configuration>
<archive>
<manifest>
<mainClass>com.halofour.Main</mainClass>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
<manifestEntries>
<Implementation-Build>${project.version}</Implementation-Build>
<Build-Version>${project.version}</Build-Version>
<Build-Time>${maven.build.timestamp}</Build-Time>
</manifestEntries>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<finalName>sandbox</finalName>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.ea.async</groupId>
<artifactId>ea-async-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<goals>
<goal>instrument</goal>
<goal>instrument-test</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.ea.async</groupId>
<artifactId>ea-async</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
As you can see there is not a lot going on here. However, when I build the project and run it I get the following error:
Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
com/halofour/MyAsyncClass.async$testAsync(Ljava/util/concurrent/CompletionStage;Ljava/util/concurrent/CompletionStage;ILjava/lang/Object;)Ljava/util/concurrent/CompletableFuture; @42: invokevirtual
Reason:
Type 'java/util/concurrent/CompletionStage' (current frame, stack[0]) is not assignable to 'java/util/concurrent/CompletableFuture'
Current Frame:
bci: @42
flags: { }
locals: { 'java/util/concurrent/CompletionStage', 'java/util/concurrent/CompletionStage', 'java/util/concurrent/CompletionStage', 'java/lang/Object' }
stack: { 'java/util/concurrent/CompletionStage', 'java/util/function/Function' }
Bytecode:
0x0000000: 1caa 0000 0000 004f 0000 0000 0000 0001
0x0000010: 0000 0017 0000 004b 2a59 b900 1301 00b6
0x0000020: 0019 9a00 194d 2cb8 001f b600 232a 2c11
0x0000030: 0001 ba00 3700 00b6 003a b0b9 0013 0100
0x0000040: b600 3ec0 0040 4c2b b800 43b0 2ba7 ffee
0x0000050: bb00 4a59 b700 4bbf
Stackmap Table:
full_frame(@24,{Object[#15],Object[#15],Integer,Object[#4]},{})
full_frame(@59,{Object[#15]},{Object[#15]})
full_frame(@76,{Object[#15],Object[#15],Integer,Object[#4]},{})
full_frame(@80,{Object[#15],Object[#15],Integer,Object[#4]},{})
at com.halofour.Main.main(Main.java:13)
If I change the parameter type of the first parameter in the MyAsyncClass#testAsync
method from CompletionStage<T>
to CompletableFuture<T>
it does seem to work correctly.
I think you really did a fantastic job.
According to you, Is it possibile to inject, somehow, a custom Executor to let the generated CompletableFutures asynchronously run into a specific Context (in my case, Vert.x)?
If a lib verticalization is needed, what point of the source do you suggest to edit?
java.lang.VerifyError
is being thrown with the following code example:
public class Main2 {
public static void main(String[] args) throws InterruptedException {
Async.init();
test()
.thenRun(() -> System.out.println("DONE"))
.exceptionally(t -> {t.printStackTrace();return null;});
Thread.sleep(1000);
}
private static CompletableFuture<TestModel[]> getTestModels() {
return CompletableFuture.completedFuture(new TestModel[] { new TestModel(1), new TestModel(2) });
}
private static CompletableFuture<Void> test() {
TestModel[] testModelArr = await(getTestModels());
String str = "";
for (TestModel myModel: testModelArr) {
str += ":" + await(CompletableFuture.completedFuture(myModel.myInt));
System.out.println(myModel.myInt.doubleValue());
}
System.out.println(str);
return CompletableFuture.completedFuture(null);
}
private static class TestModel {
public final Integer myInt;
private TestModel(Integer myInt) {
this.myInt = myInt;
}
}
}
With any of the following changes to the test()
method, it no longer throws the error:
System.out.println(myModel.myInt.doubleValue());
:private static CompletableFuture<Void> test() {
TestModel[] testModelArr = await(getTestModels());
String str = "";
for (TestModel myModel: testModelArr) {
str += ":" + await(CompletableFuture.completedFuture(myModel.myInt));
}
System.out.println(str);
return CompletableFuture.completedFuture(null);
}
System.out.println(myModel.myInt.doubleValue());
by 1 line:private static CompletableFuture<Void> test() {
TestModel[] testModelArr = await(getTestModels());
String str = "";
for (TestModel myModel: testModelArr) {
System.out.println(myModel.myInt.doubleValue());
str += ":" + await(CompletableFuture.completedFuture(myModel.myInt));
}
System.out.println(str);
return CompletableFuture.completedFuture(null);
}
testModelArr
to list for the for
loop:private static CompletableFuture<Void> test() {
TestModel[] testModelArr = await(getTestModels());
String str = "";
for (TestModel myModel: Arrays.asList(testModelArr)) {
str += ":" + await(CompletableFuture.completedFuture(myModel.myInt));
System.out.println(myModel.myInt.doubleValue());
}
System.out.println(str);
return CompletableFuture.completedFuture(null);
}
Any idea why this is happening in this specific case?
Hi! Thanks for the great library.
There's a thing in this library that annoys me when I use it, and hope that you can add.
In C#, just like in this library everything after the await
keyword is converted into a callback under the hood.
In addition, async
methods that use the keyword await
must return Task
(or void
, but not recommended), just like in this library where every method that uses the await
method needs to return a CompletableFuture
.
Since when you use the await method you get the desired type on the awaited method, straight ahead, no need to deal with CompletableFutures, but the problem is that you have to return one.
I personally just wrap it with CompletableFuture.completedFuture(awaitedValue)
, but that can be annoying when you have a lot of async methods.
Here's an example:
public CompletableFuture<AudioTrack> search(String query) {
var track = await(youTubeProvider.searchAsync(query));
track.supplier = "Example usage";
track.setDate(LocalTime.now());
return CompletableFuture.completedFuture(track);
}
Imagine having huge amount of methods just like that.
This causes huge frustration and makes the code much longer, since I have to upcast the wrapped value inside the CompletableFuture.
I would be very happy to see the instrumental tool converting a return of any awaited TValue
into a CompletableFuture<TValue>
.
So my code will look like this:
public CompletableFuture<AudioTrack> search(String query) {
var track = await(youTubeProvider.searchAsync(query));
track.supplier = "Example usage";
track.setDate(LocalTime.now());
return track;
}
The track is automatically wrapped into a CompletableFuture<AudioTrack>
.
I hope I managed to express myself and you got my point.
Thank you very much! You're saving my life from callback hell!
P.S. Perhaps I'm using it not correctly, I'll be very happy to learn the appropriate way.
...does not work.
Crashes immediately in Async.init()
2023-06-06 16:57:53.160 3727-3727 AndroidRuntime com.example.myapp E FATAL EXCEPTION: main
Process: com.example.myapp, PID: 3727
java.lang.ExceptionInInitializerError
Hi,
i just found your amazing async await library and wanted to use maven for the integration, but unfortunately i got an error after entering the ea-async-maven-plugin into my pom.xml.
I git the following errors:
Plugin execution not covered by lifecycle configuration: com.ea.async:ea-async-maven-plugin:1.2.3:instrument (execution: default, phase: process-classes)
Plugin execution not covered by lifecycle configuration: com.ea.async:ea-async-maven-plugin:1.2.3:instrument-test (execution: default, phase: process-test-classes)
I used the provided example:
<plugin>
<groupId>com.ea.async</groupId>
<artifactId>ea-async-maven-plugin</artifactId>
<version>1.2.3</version>
<executions>
<execution>
<goals>
<goal>instrument</goal>
<goal>instrument-test</goal>
</goals>
</execution>
</executions>
</plugin>
Any ideas what i am doing wrong?
Thanks in advance.
I have a doubt, I looked at your code, you use Futrue.join () method inside the Aysnc.await method to achieve the wait result returned, so there is no thread scheduling, how can we call it async/ Await?
Currently there is support for instrumenting methods that return/await classes that derive from CompletableFuture<T>
. How about also supporting classes or interfaces that implement or extend CompletionStage<T>
, with which the class would be treated as a CompletionStage<T>
during instrumentation?
I've seen a couple of libraries where methods returns interfaces that extend CompletionStage<T>
. For example, the Lettuce Redis client offers an asynchronous API that returns a RedisFuture<T>
interface which extends CompletionStage<T>
and Future<T>
interfaces as well as providing a few additional methods. The internal implementation of that interface is derived from CompletableFuture<T>
. Because of this the library isn't supported by ea-async.
I’m introducing this project to my team, it’s really convenience.
Now I notice that last commit is in 2020, why no updates coming.
So is this project alive?
I'm testing ea-async with a Spring 5 application I am working on. This is a part of a tech spike/evaluation for implementing asynchronous programming throughout our team. What I've noticed is that the ea-async instrumentation is copying the annotations of the instrumented method to the generated methods and in some cases this is causing problems with Spring.
For example, take the following controller:
@RestController
public class HelloWorldController {
@Inject
private AsyncService service;
@RequestMapping("/hello")
public CompletionStage<String> get() {
CompletionStage<String> stage1 = service.getGreetingAsync();
CompletionStage<String> stage2 = service.getGreetingAsync();
String result1 = await(stage1);
String result2 = await(stage2);
return CompletableFuture.completedFuture(result1 + result2);
}
}
The instrumentation rewrites this into:
@RestController
public class HelloWorldController {
@Inject
private AsyncService service;
public HelloWorldController() {
}
@RequestMapping({"/hello"})
public CompletionStage<String> get() { /* elided */ }
@RequestMapping({"/hello"})
private static CompletableFuture async$get(HelloWorldController var0, CompletionStage stage1, CompletionStage stage2, CompletionStage var3, String result2, int var5, Object var6) { /* elided */ }
}
This causes Spring to fail mapping the controller with the following message:
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'helloWorldController' method
private static java.util.concurrent.CompletableFuture com.comcast.controller.HelloWorldController.async$get(com.comcast.controller.HelloWorldController,java.util.concurrent.CompletionStage,java.util.concurrent.CompletionStage,java.util.concurrent.CompletionStage,java.lang.String,int,java.lang.Object)
to {[/hello]}: There is already 'helloWorldController' bean method
This can be worked around by having only using Async.await
in non-annotated methods, however my concern is that this will make other folks on my team more apprehensive about adopting ea-async.
Nice library guys - have been using it for a while - makes non blocking Rest API code much easier to write reliably.
Just wondered if it could be updated to support the latest Java, which I was trying to use?
I believe the below issue is related to the version of ASM dependencies.
Not massively urgent, but nice to keep your library working with latest versions.
Caused by: java.lang.IllegalArgumentException: Unsupported class file major version 58
at com.ea.async.shaded.org.objectweb.asm.ClassReader.(ClassReader.java:195)
at com.ea.async.shaded.org.objectweb.asm.ClassReader.(ClassReader.java:176)
at com.ea.async.shaded.org.objectweb.asm.ClassReader.(ClassReader.java:162)
at com.ea.async.instrumentation.Transformer.transform(Transformer.java:150)
I use intellij idea
command is mvn clean install
the error is
[INFO] --- maven-install-plugin:2.4:install (default-install) @ async ---
[WARNING] Error injecting: org.apache.maven.artifact.installer.DefaultArtifactInstaller
com.google.inject.ProvisionException: Unable to provision, see the following errors:
1) Error injecting: private org.eclipse.aether.spi.log.Logger org.apache.maven.repository.internal.DefaultVersionRangeResolver.logger
while locating org.apache.maven.repository.internal.DefaultVersionRangeResolver
while locating java.lang.Object annotated with *
at org.eclipse.sisu.wire.LocatorWiring
while locating org.eclipse.aether.impl.VersionRangeResolver
for parameter 2 at org.eclipse.aether.internal.impl.DefaultDependencyCollector.<init>(Unknown Source)
while locating org.eclipse.aether.internal.impl.DefaultDependencyCollector
while locating java.lang.Object annotated with *
at org.eclipse.sisu.wire.LocatorWiring
while locating org.eclipse.aether.impl.DependencyCollector
for parameter 5 at org.eclipse.aether.internal.impl.DefaultRepositorySystem.<init>(Unknown Source)
while locating org.eclipse.aether.internal.impl.DefaultRepositorySystem
while locating java.lang.Object annotated with *
while locating org.apache.maven.artifact.installer.DefaultArtifactInstaller
Caused by: java.lang.IllegalArgumentException: Can not set org.eclipse.aether.spi.log.Logger field org.apache.maven.repository.internal.DefaultVersionRangeResolver.logger to org.eclipse.aether.internal.impl.slf4j.Slf4jLoggerFactory
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
at java.lang.reflect.Field.set(Field.java:764)
at org.eclipse.sisu.bean.BeanPropertyField.set(BeanPropertyField.java:72)
at org.eclipse.sisu.plexus.ProvidedPropertyBinding.injectProperty(ProvidedPropertyBinding.java:48)
at org.eclipse.sisu.bean.BeanInjector.injectMembers(BeanInjector.java:52)
at com.google.inject.internal.MembersInjectorImpl.injectMembers(MembersInjectorImpl.java:140)
at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:114)
at com.google.inject.internal.ConstructorInjector.access$000(ConstructorInjector.java:32)
at com.google.inject.internal.ConstructorInjector$1.call(ConstructorInjector.java:89)
at com.google.inject.internal.ProvisionListenerStackCallback$Provision.provision(ProvisionListenerStackCallback.java:115)
at com.google.inject.internal.ProvisionListenerStackCallback$Provision.provision(ProvisionListenerStackCallback.java:133)
at com.google.inject.internal.ProvisionListenerStackCallback.provision(ProvisionListenerStackCallback.java:68)
at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:87)
at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:267)
at com.google.inject.internal.FactoryProxy.get(FactoryProxy.java:56)
at com.google.inject.internal.InjectorImpl$2$1.call(InjectorImpl.java:1016)
at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1103)
at com.google.inject.internal.InjectorImpl$2.get(InjectorImpl.java:1012)
at org.eclipse.sisu.inject.Guice4$1.get(Guice4.java:162)
at org.eclipse.sisu.inject.LazyBeanEntry.getValue(LazyBeanEntry.java:81)
at org.eclipse.sisu.wire.BeanProviders.firstOf(BeanProviders.java:179)
at org.eclipse.sisu.wire.BeanProviders$7.get(BeanProviders.java:160)
at com.google.inject.internal.ProviderInternalFactory.provision(ProviderInternalFactory.java:81)
at com.google.inject.internal.InternalFactoryToInitializableAdapter.provision(InternalFactoryToInitializableAdapter.java:53)
at com.google.inject.internal.ProviderInternalFactory$1.call(ProviderInternalFactory.java:65)
at com.google.inject.internal.ProvisionListenerStackCallback$Provision.provision(ProvisionListenerStackCallback.java:115)
at com.google.inject.internal.ProvisionListenerStackCallback$Provision.provision(ProvisionListenerStackCallback.java:133)
at com.google.inject.internal.ProvisionListenerStackCallback.provision(ProvisionListenerStackCallback.java:68)
at com.google.inject.internal.ProviderInternalFactory.circularGet(ProviderInternalFactory.java:63)
at com.google.inject.internal.InternalFactoryToInitializableAdapter.get(InternalFactoryToInitializableAdapter.java:45)
at com.google.inject.internal.SingleParameterInjector.inject(SingleParameterInjector.java:38)
at com.google.inject.internal.SingleParameterInjector.getAll(SingleParameterInjector.java:62)
at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:104)
at com.google.inject.internal.ConstructorInjector.access$000(ConstructorInjector.java:32)
at com.google.inject.internal.ConstructorInjector$1.call(ConstructorInjector.java:89)
at com.google.inject.internal.ProvisionListenerStackCallback$Provision.provision(ProvisionListenerStackCallback.java:115)
at com.google.inject.internal.ProvisionListenerStackCallback$Provision.provision(ProvisionListenerStackCallback.java:133)
at com.google.inject.internal.ProvisionListenerStackCallback.provision(ProvisionListenerStackCallback.java:68)
at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:87)
at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:267)
at com.google.inject.internal.FactoryProxy.get(FactoryProxy.java:56)
at com.google.inject.internal.InjectorImpl$2$1.call(InjectorImpl.java:1016)
at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1092)
at com.google.inject.internal.InjectorImpl$2.get(InjectorImpl.java:1012)
at org.eclipse.sisu.inject.Guice4$1.get(Guice4.java:162)
at org.eclipse.sisu.inject.LazyBeanEntry.getValue(LazyBeanEntry.java:81)
at org.eclipse.sisu.wire.BeanProviders.firstOf(BeanProviders.java:179)
at org.eclipse.sisu.wire.BeanProviders$7.get(BeanProviders.java:160)
Too much, I only posted part of it
my pom file is
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dun</groupId>
<artifactId>async</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.ea.async</groupId>
<artifactId>ea-async</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.6.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>com.ea.async</groupId>
<artifactId>ea-async-maven-plugin</artifactId>
<version>1.1.1</version>
<executions>
<execution>
<goals>
<goal>instrument</goal>
<goal>instrument-test</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
I guess it's not related to development tools? You used eclipse when developing this plugin, so it led to some coupling you didn't expect?
The instrumented code:
public final CompletableFuture<Boolean> call(String longString) {
String[] split = longString.split(" ");
String name = split[0];
String surname = split[1];
//again...
name = split[0];
//...
}
Raises me the following exception:
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at it.mvi.core.functionalities.FunctionalitiesHelper.getFunctionality(FunctionalitiesHelper.java:36)
at it.mvi.core.functionalities.FunctionalitiesManager.executeWork(FunctionalitiesManager.java:128)
at it.mvi.core.functionalities.FunctionalitiesManager.executeWork(FunctionalitiesManager.java:34)
at it.mvi.core.functionalities.FunctionalitiesManager.call(FunctionalitiesManager.java:21)
at com.metaring.homepagecontactformbackend.HomepagecontactformbackendFunctionalitiesManager.contact(HomepagecontactformbackendFunctionalitiesManager.java:16)
at com.metaring.homepagecontactformbackend.Main.start(Main.java:20)
at com.metaring.homepagecontactformbackend.Main.main(Main.java:14)
Caused by: java.lang.VerifyError: Inconsistent stackmap frames at branch target 494
Exception Details:
Location:
com/metaring/homepagecontactformbackend/ContactFunctionalityImpl.call(Lcom/metaring/homepagecontactformbackend/ContactForm;)Ljava/util/concurrent/CompletableFuture; @494: new
Reason:
Type 'java/lang/Object' (current frame, locals[8]) is not assignable to 'java/lang/String' (stack map, locals[8])
Current Frame:
bci: @465
flags: { }
locals: { 'com/metaring/homepagecontactformbackend/ContactFunctionalityImpl', 'com/metaring/homepagecontactformbackend/ContactForm', 'it/mvi/core/type/factory/DataRepresentationFactory', 'java/lang/String', '[Ljava/lang/String;', 'java/lang/String', 'java/lang/String', 'java/lang/String', 'java/lang/Object', 'java/lang/String', 'java/lang/String', 'it/mvi/core/type/DataRepresentation', 'java/lang/String', 'it/mvi/core/type/DataRepresentation', 'it/mvi/core/type/DataRepresentation' }
stack: { 'it/mvi/core/type/DataRepresentation' }
Stackmap Frame:
bci: @494
flags: { }
locals: { 'com/metaring/homepagecontactformbackend/ContactFunctionalityImpl', 'com/metaring/homepagecontactformbackend/ContactForm', 'it/mvi/core/type/factory/DataRepresentationFactory', 'java/lang/String', '[Ljava/lang/String;', 'java/lang/String', 'java/lang/String', 'java/lang/String', 'java/lang/String', 'java/lang/String', 'java/lang/String', 'it/mvi/core/type/DataRepresentation', 'java/lang/String', 'it/mvi/core/type/DataRepresentation', 'it/mvi/core/type/DataRepresentation' }
stack: { }
Bytecode:
...
It is raised when the class is loaded into ClassLoader
for the first time in application.
I am sure that the problem is in that piece of code because I tried a lot of alternatives, and solved using:
List<String> split = Arrays.asList(longString.split(" "));
String name = split.get(0);
//again...
name = split.get(0);
But I want my customers enjoy development experience through my SDK...
Can you help, please?
Hi! Thanks for the nice library. But I have a small issue using it. If you want to catch an exception from await
call it always has been wrapped into CompletionException, and you should write extra checks just to verify nested exception.
Example:
CompletableFuture<String> foo() {
return failedFuture(new MyException());
}
CompletableFuture<String> bar() {
try {
return await(foo());
} catch (MyException e) {
// never called
} catch (CompletionException e) {
if (e.getCause() instanceof MyException) {
var ex = (MyException) e.getCause();
// so now you can access MyException instance
} else {
throw e;
}
}
}
It would be great to have an easy way to avoid unnecessary wrapping.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.