eclipse-vertx / vertx-json-schema

Vert.x Json Schema
Other
78 stars 32 forks source link

Threads not being closed properly while using SchemaRouter #50

Closed mejuhi closed 2 years ago

mejuhi commented 3 years ago

Version

Used following Dependency of vertx in pom.xml

<dependency>
     <groupId>io.vertx</groupId>
     <artifactId>vertx-json-schema</artifactId>
     <version>4.2.0.CR1</version>
</dependency>

<dependency>
    <groupId>io.vertx</groupId>
    <artifactId>vertx-core</artifactId>
    <version>4.2.0.CR1</version>
</dependency>

Context

While running the Vertx for doing json validation using following code, we noticed that after the execution, there are some Vertx threads which are not closed properly, so even if function returns back to the main method, main method is not able to terminate the java application properly

Do you have a reproducer?

Isolated this problem using the following java code


import java.io.File;
import java.util.concurrent.CompletableFuture;
import io.vertx.core.Vertx;
import io.vertx.core.json.pointer.JsonPointer;
import io.vertx.json.schema.Schema;
import io.vertx.json.schema.SchemaParser;
import io.vertx.json.schema.SchemaRouter;
import io.vertx.json.schema.SchemaRouterOptions;
import io.vertx.core.json.JsonObject;
import org.apache.commons.lang3.ThreadUtils;

public class JSONValidatorVertx {

    /**
     * This method is used for json validation
     * 
     * @param Data: json data on which validations needs to be performed
     * @param jsonSchema: file path to json schema 
     * @return Boolean
     * @throws Exception: If any error occurs during json validation
     */
    private static Boolean validateJSON(String data, String jsonSchema) throws Exception {
        long beforeTimestamp = System.currentTimeMillis();
        try {
          SchemaRouter ROUTER = SchemaRouter.create(Vertx.vertx(), new SchemaRouterOptions());
          SchemaParser PARSER = SchemaParser.createDraft201909SchemaParser(ROUTER);
          String jsonSchemaContent =  AppUtility.readFileContent(jsonSchema);
          JsonPointer pointer = JsonPointer.fromURI(new File(jsonSchema).toURI());
          Schema schema = PARSER.parse(new JsonObject(jsonSchemaContent), pointer);
          CompletableFuture<Void> future = new CompletableFuture<>();
          schema.validateAsync(new JsonObject(data)).onSuccess(future::complete).onFailure(future::completeExceptionally);
          future.get();
          System.out.println("End of try statment");
      } catch (Exception headerException) {
           System.out.println(headerException.toString());
           throw headerException;
       }
        System.out.println("Performed json validation in " + (System.currentTimeMillis() - beforeTimestamp) + " ms");
        return true;
    }

    public static void main( String[] args ) throws Exception {
        System.out.println( "Hello! You have triggered JSON validator");
        try {
            // Use function to read input json data 
            String inputData = AppUtility.readFileContent(args[0]);
            //String inputValidationSchema = AppUtility.readFileContent(args[1]);
            // Call validation function
            Boolean result = validateJSON(inputData, args[1]);
            String content = new String("JSON validation Result: ").concat(result.toString());
            for (Thread t : ThreadUtils.getAllThreads()) {
                System.out.println(t.getName() + ", " + t.isDaemon());
          }
            System.out.println(content);
        } catch (Exception ex){
            System.out.println("Exception occurred: \n " + ex.getStackTrace());
            throw ex;
        }
    }

}

Output after running gives us following:

Hello! You have triggered JSON validator
End of try statment
Performed json validation in 1010 ms
Reference Handler, true
Finalizer, true
Signal Dispatcher, true
Attach Listener, true
Notification Thread, true
main, false
vertx-blocked-thread-checker, true
Thread-2, true
vert.x-eventloop-thread-0, false
vert.x-internal-blocking-0, false
vert.x-internal-blocking-1, false
Common-Cleaner, true
JSON validation Result: true

Even though the main get the control back from the validateJSON, java application does not terminate gracefully and had to be terminated forcefully by the user

JVM version

openjdk 15.0.2 2021-01-19
OpenJDK Runtime Environment AdoptOpenJDK (build 15.0.2+7)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 15.0.2+7, mixed mode, sharing)
mejuhi commented 3 years ago

Added information,

  1. We are using $ref in our schemas to import schemas stored on filesystem
  2. We also printed state of the threads, we can see that two vertx threads are in waiting state (vert.x-internal-blocking-0 & vert.x-internal-blocking-1), following has more details:
    ThreadName: vert.x-internal-blocking-0, 
            IsDaemon: false, 
            Group: java.lang.ThreadGroup[name=main,maxpri=10] 
            Isalive: true 
            State: WAITING,
            ClassLoader: jdk.internal.loader.ClassLoaders$AppClassLoader@2a139a55
    ThreadName: vert.x-internal-blocking-1, 
            IsDaemon: false, 
            Group: java.lang.ThreadGroup[name=main,maxpri=10],
            IsAlive: true,
            State: WAITING,
            ClassLoader: jdk.internal.loader.ClassLoaders$AppClassLoader@2a139a55
    ThreadName: vertx-blocked-thread-checker, 
            IsDaemon: true, 
            Group: java.lang.ThreadGroup[name=main,maxpri=10] 
            IsAlive: true,
            State: TIMED_WAITING,
            ClassLoader: jdk.internal.loader.ClassLoaders$AppClassLoader@2a139a55
    ThreadName: Thread-2, 
            IsDaemon: true, 
            Group: java.lang.ThreadGroup[name=main,maxpri=10] 
            Isalive: true 
            State: RUNNABLE 
            ClassLoader: jdk.internal.loader.ClassLoaders$AppClassLoader@2a139a55
    ThreadName: vert.x-eventloop-thread-0, 
            IsDaemon: false,
            Group: java.lang.ThreadGroup[name=main,maxpri=10] 
            IsAlive: true,
            State: RUNNABLE, 
            ClassLoader: jdk.internal.loader.ClassLoaders$AppClassLoader@2a139a55
  3. We tried to terminate this open threads by using future.notify(); and future.notifyAll(); but it threw IllegalMonitorStateException, following is the stacktrace:
    Exception in thread "main" java.lang.IllegalMonitorStateException: current thread is not owner
        at java.base/java.lang.Object.notifyAll(Native Method)
        at ve.JSONValidatorVertx.validateJSON(JSONValidatorVertx.java:42)
        at ve.JSONValidatorVertx.sample(JSONValidatorVertx.java:62)
        at ve.JSONValidatorVertx.main(JSONValidatorVertx.java:109)
mejuhi commented 3 years ago

Hello @vietj, do you have any update on this issue?

vietj commented 2 years ago

did you try closing the vertx instance after it is used ?

vietj commented 2 years ago

can you provide a full reproducer ?

mejuhi commented 2 years ago

did you try closing the vertx instance after it is used ?

Are you referring to vetrtex instance initialized here SchemaRouter ROUTER = SchemaRouter.create(Vertx.vertx(), new SchemaRouterOptions()); I was not able to initialize it separately, so did not close it. Can you give an example for this?

mejuhi commented 2 years ago

can you provide a full reproducer ?

Java code:

import java.io.File;
import java.util.concurrent.CompletableFuture;
import io.vertx.core.Vertx;
import io.vertx.core.json.pointer.JsonPointer;
import io.vertx.json.schema.Schema;
import io.vertx.json.schema.SchemaParser;
import io.vertx.json.schema.SchemaRouter;
import io.vertx.json.schema.SchemaRouterOptions;
import io.vertx.core.json.JsonObject;
import org.apache.commons.lang3.ThreadUtils;

public class JSONValidatorVertx {

    /**
     * This method is used for json validation
     * 
     * @param Data: json data on which validations needs to be performed
     * @param jsonSchema: file path to json schema 
     * @return Boolean
     * @throws Exception: If any error occurs during json validation
     */
    private static Boolean validateJSON(String data, String jsonSchema) throws Exception {
        long beforeTimestamp = System.currentTimeMillis();
        try {
          SchemaRouter ROUTER = SchemaRouter.create(Vertx.vertx(), new SchemaRouterOptions());
          SchemaParser PARSER = SchemaParser.createDraft201909SchemaParser(ROUTER);
          String jsonSchemaContent =  AppUtility.readFileContent(jsonSchema);
          JsonPointer pointer = JsonPointer.fromURI(new File(jsonSchema).toURI());
          Schema schema = PARSER.parse(new JsonObject(jsonSchemaContent), pointer);
          CompletableFuture<Void> future = new CompletableFuture<>();
          schema.validateAsync(new JsonObject(data)).onSuccess(future::complete).onFailure(future::completeExceptionally);
          future.get();
          System.out.println("End of try statment");
      } catch (Exception headerException) {
           System.out.println(headerException.toString());
           throw headerException;
       }
        System.out.println("Performed json validation in " + (System.currentTimeMillis() - beforeTimestamp) + " ms");
        return true;
    }

    public static void main( String[] args ) throws Exception {
        System.out.println( "Hello! You have triggered JSON validator");
        try {
            // Use function to read input json data 
            String inputData = AppUtility.readFileContent(args[0]);
            //String inputValidationSchema = AppUtility.readFileContent(args[1]);
            // Call validation function
            Boolean result = validateJSON(inputData, args[1]);
            String content = new String("JSON validation Result: ").concat(result.toString());
            for (Thread t : ThreadUtils.getAllThreads()) {
                System.out.println(t.getName() + ", " + t.isDaemon());
          }
            System.out.println(content);
        } catch (Exception ex){
            System.out.println("Exception occurred: \n " + ex.getStackTrace());
            throw ex;
        }
    }

}

Dependency on pom

<dependency>
     <groupId>io.vertx</groupId>
     <artifactId>vertx-json-schema</artifactId>
     <version>4.2.0.CR1</version>
</dependency>

<dependency>
    <groupId>io.vertx</groupId>
    <artifactId>vertx-core</artifactId>
    <version>4.2.0.CR1</version>
</dependency>

Its a POJO version, while running this class, 1st pratameter required is a json file path and for 2nd parameter requires json schema

vietj commented 2 years ago

you simoply close the object Vertx.vertx() after you are done and this will stop the threads it created.