reactiverse / es4x

🚀 fast JavaScript 4 Eclipse Vert.x
https://reactiverse.io/es4x/
Apache License 2.0
882 stars 75 forks source link

deployVerticle return type is not es4x type? #460

Closed chengenzhao closed 3 years ago

chengenzhao commented 3 years ago

Hi Paulo:

We try to migrate our vert.x based project to es4x and we did some progress but seems like there is a problem here if we use await vertx.deployVerticle(...) and it just don't work seems like we need es4x promise or sth. rather than vert.x core future here is the source code of main_verticle.js, we try to deploy verticles and await the return future/promise type https://github.com/fantasy0v0/social-vertex/blob/master/src/main/javascript/cn/net/polyglot/main-verticle.js

14112B5DC7794A600D86698DAC8B8C93

chengenzhao commented 3 years ago

We have discussed this topic Okou said it is probably becoz the Future class of Vert.x doesn't contain then method therefore await doesn't apply to this type And seems like es4x-launcher.jar uses FutureBase of its own rather than FutureBase of Vert.x so is it possible to provide a wrapper of Vert.x api e.g. es4x instead of vertx instance then we could use es4x.deployVerticle(...) return Future type with then method and we could await es4x.deployVerticle(...)

pmlopes commented 3 years ago

Yes es4x "patches" the FutureBase class in order to add the extra method:

void then(Value onSuccess, Values onFailure);

However if for some reason the deployVerticle does not call that class (but another implementation) then the await will not work. I need to reproduce this and see the real class that is coming from the deployVerticle.

pmlopes commented 3 years ago

I'm looking at the core code and I'm getting the impression that, as there's is a classloader switch before the deployment that the "patched" FutureBase will not be used, and the "official" one is... If this is the case, then the "patch" is not enough and I need to really hack it to ensure that the right class is loaded.

pmlopes commented 3 years ago

Wait, I'm running your project and I see that it is a maven project so the "patching" never happens, this means that the thenable support isn't there and the transparent usage of JsonObject/JsonArray as js json types either...

chengenzhao commented 3 years ago

Yes, it is a maven project because we also have Kotlin code in the project Is there any way to replace ES4X FutureBase instead of Vert.x Core FutureBase in maven project?

pmlopes commented 3 years ago

I got it to work:

~/Projects/bugs/social-vertex (master)$ java -jar target/social-vertex-0.7.jar 
Current Path: /home/paulo/Projects/bugs/social-vertex
Succeeded in deploying verticle
The configuration file: config.json does not exist or in wrong format, use default config.
class: 6fe41a75-0bf1-4cec-b9ff-d8871b947f82
class: c60235fe-bddb-4c4e-b582-21fb0f047c5c
class: a08c9cd7-7bf4-4449-9b5c-e3f41b4a66e4
class: 3ff7a828-6e37-4390-9285-ff25818c6bc1
class: a397eca8-95c1-46c1-ab95-95b5cff2a9c7
class: 2bc3a368-8825-4c0f-95e8-d4f1d9f7930b
cn.net.polyglot.verticle.im.IMTcpServerVerticle is deployed
cn.net.polyglot.verticle.im.IMServletVerticle is deployed
class: 16509885-e85c-481c-9700-e645ed3e874a
cn.net.polyglot.verticle.web.SessionVerticle is deployed
cn.net.polyglot.verticle.WebServerVerticle is deployed
class: 489dbfc5-c563-4382-82a5-8faf1e89d180
cn.net.polyglot.verticle.community.DefaultVerticle is deployed
class: 5ff3dca4-744e-467d-a516-54c7778bd8b6
cn.net.polyglot.verticle.community.LoginVerticle is deployed
class: a83da962-22e3-4354-9a66-a5a51261a659
cn.net.polyglot.verticle.community.CommunityVerticle is deployed
class: 0dde0dcf-f129-468b-92d0-24d3ea50724c
^C~/Projects/bugs/social-vertex (master)$ 

So the issue is that we miss the "patched" class and this class "must" be loaded first that the remaining jars.

To "hack it" I've run the following code:

package io.reactiverse.es4x;

import io.reactiverse.es4x.asm.FutureBaseVisitor;
import io.reactiverse.es4x.asm.JsonArrayVisitor;
import io.reactiverse.es4x.asm.JsonObjectVisitor;
import io.reactiverse.es4x.commands.Resolver;
import org.eclipse.aether.artifact.Artifact;

import java.io.*;
import java.util.Collections;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

public class VertxPatch {

  public static void main(String[] args) throws IOException {

    System.err.println(args[0]);
    System.err.println(args[1]);

    Resolver resolver = new Resolver();
    File coreJar = null;

    for (Artifact a : resolver.resolve("io.vertx:vertx-core:" + args[0], Collections.emptyList())) {
      if ("io.vertx".equals(a.getGroupId()) && "vertx-core".equals(a.getArtifactId())) {
        coreJar = a.getFile();
        break;
      }
    }

    if (coreJar == null) {
      throw new RuntimeException("Failed to locate the core jar");
    }

    try (JarInputStream jar = new JarInputStream(new FileInputStream(coreJar))) {
      JarEntry je;
      byte[] bytes;
      File target;
      while ((je = jar.getNextJarEntry()) != null) {
        switch (je.getName()) {
          case "io/vertx/core/json/JsonObject.class":
            bytes = new JsonObjectVisitor().rewrite(jar);
            target = new File(args[1], "io/vertx/core/json");
            target.mkdirs();
            try (OutputStream writer = new FileOutputStream(new File(target, "JsonObject.class"))) {
              writer.write(bytes);
            }
            break;

          case "io/vertx/core/json/JsonArray.class":
            bytes = new JsonArrayVisitor().rewrite(jar);
            target = new File(args[1], "io/vertx/core/json");
            target.mkdirs();
            try (OutputStream writer = new FileOutputStream(new File(target, "JsonArray.class"))) {
              writer.write(bytes);
            }
            break;
          case "io/vertx/core/impl/future/FutureBase.class":
            bytes = new FutureBaseVisitor().rewrite(jar);
            target = new File(args[1], "io/vertx/core/impl/future");
            target.mkdirs();
            try (OutputStream writer = new FileOutputStream(new File(target, "FutureBase.class"))) {
              writer.write(bytes);
            }
            break;
        }
      }
    }
  }
}

This generates the patched 3 classes. The classes are written to target/classes so they get bundled with your application (this means it will not work with JPMS (but the regular es4x install does the same and doesn't work either) which isn't a real problem I would say.

With this 3 classes then it all works. I could add this class to es4x-pm jar so it could be used as a "provided" dependency to be run during a "maven build"? Or else we need a maven plugin or something the like:

<plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <executions>
          <execution>
            <id>generate-asm</id>
            <phase>process-test-classes</phase>
            <goals>
              <goal>java</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <mainClass>io.reactiverse.es4x.VertxPatch</mainClass>
          <arguments>
            <argument>${stack.version}</argument>
            <argument>${project.build.testOutputDirectory}</argument>
          </arguments>
          <classpathScope>test</classpathScope>
        </configuration>
      </plugin>
pmlopes commented 3 years ago

This seems to get the basic things working:

https://github.com/reactiverse/es4x/pull/461

Add the pm dependency as "provided" as it's not needed at runtime:

    <dependency>
      <groupId>io.reactiverse</groupId>
      <artifactId>es4x-pm</artifactId>
      <version>${project.version}</version>
      <scope>provided</scope>
    </dependency>

Then in the build run the Patch:

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <executions>
          <execution>
            <id>generate-asm</id>
            <phase>process-classes</phase>
            <goals>
              <goal>java</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <mainClass>io.reactiverse.es4x.cli.VertxPatch</mainClass>
          <arguments>
            <argument>${vertx.version}</argument>
            <argument>${project.build.outputDirectory}</argument>
          </arguments>
          <classpathScope>compile</classpathScope>
        </configuration>
      </plugin>
pmlopes commented 3 years ago

@chengenzhao I've prepared a new release 0.14.0 currently if you're testing using maven you can verify the artifacts if you use the following statging maven repo:

https://oss.sonatype.org/content/repositories/ioreactiverse-1147/

chengenzhao commented 3 years ago

@pmlopes it works thx F47AE55EBF5BB69D29971597B57F8228

chengenzhao commented 3 years ago

but weird thing is if we change the code to: for await then the disorder happened again 1ED73C13569114258E44EED7A01B3771

pmlopes commented 3 years ago

@chengenzhao what do you mean with "disorder"? Are you saying that the deployment doesn't happen on the loop order?

This behavior is not controlled by vertx or es4x, it's graaljs. The only thing es4x is doing is transforming a Future to a JS Thenable. and then graaljs takes over and runs the async operations.

chengenzhao commented 3 years ago

👌,we just wonder for await not works in this way but it is ok, we just use await inside the for loop