vaadin / quarkus

An extension to Quarkus to support Vaadin Flow
Apache License 2.0
28 stars 3 forks source link

Vaadin + Quarkus @Push does not work - always DISABLED #164

Open JasonTheDynamite opened 3 months ago

JasonTheDynamite commented 3 months ago

Describe the bug

I am getting this error on the frontend using vaadin with quarkus: https://192.168.1.100/VAADIN/static/push/vaadinPush.js could not be loaded. Push will not work. Looks like this: image Push does not work at all.

Logs show this: io.package.website.configuration.VaadinAppShell' is not a CDI bean. Falling back to default instantiation.

Currently push works only when there is a user interaction with the frontend - for example a usern needs to click a button to force changes to the frontend.

Gradle properties

quarkusPluginId=io.quarkus quarkusPluginVersion=3.12.0 quarkusPlatformGroupId=io.quarkus.platform quarkusPlatformArtifactId=quarkus-bom quarkusPlatformVersion=3.12.0 vaadinVersion=24.4.4 vaadinPluginVersion=24.4.4 kotlinVersion=2.0.0

build.gradle: `plugins { id "java" id "application" id 'org.jetbrains.kotlin.jvm' version "${kotlinVersion}" id 'org.jetbrains.kotlin.kapt' version "${kotlinVersion}" id "org.jetbrains.kotlin.plugin.allopen" version "${kotlinVersion}" id 'org.jetbrains.kotlin.plugin.jpa' version "${kotlinVersion}" id 'com.github.gmazzo.buildconfig' version '5.3.5' id "com.vaadin" version "${vaadinPluginVersion}" id 'io.quarkus' }

apply plugin: 'kotlin'

repositories { mavenCentral() maven { url = uri("https://plugins.gradle.org/m2/") } maven { url "https://maven.vaadin.com/vaadin-addons" } google() gradlePluginPortal() }

dependencies { implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")

implementation "com.vaadin:vaadin-bom:${vaadinVersion}"
implementation "com.vaadin:vaadin-jandex:${vaadinVersion}"

// implementation "com.vaadin:vaadin-core:${vaadinVersion}" implementation "com.vaadin:vaadin-quarkus-extension:${vaadinVersion}"

implementation "io.quarkus:quarkus-hibernate-orm"
implementation "io.quarkus:quarkus-jdbc-h2"
implementation "io.quarkus:quarkus-hibernate-validator"

implementation "io.quarkus:quarkus-kotlin"

implementation "io.quarkiverse.bucket4j:quarkus-bucket4j:1.0.3"
implementation "io.quarkus:quarkus-jdbc-postgresql"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "io.quarkus:quarkus-arc"
implementation "io.quarkus:quarkus-rest"
implementation "io.quarkus:quarkus-rest-jackson"

implementation("io.quarkus:quarkus-smallrye-jwt")
implementation("io.quarkus:quarkus-smallrye-jwt-build")
implementation "io.quarkus:quarkus-mutiny:3.11.2"
implementation "io.smallrye.reactive:mutiny-kotlin:2.6.0"
implementation("io.quarkus:quarkus-undertow")
implementation("io.quarkus:quarkus-websockets")

implementation "org.apache.tika:tika-core:2.9.2"
implementation("org.apache.commons:commons-text:1.11.0")
implementation("org.apache.syncope.common:syncope-common-lib:2.1.14")
implementation "org.apache.commons:commons-lang3:3.14.0"
implementation "commons-codec:commons-codec:1.17.0"
implementation "commons-io:commons-io:2.16.1"
implementation("com.google.guava:guava:33.2.1-jre")

implementation "net.java.dev.jna:jna:5.14.0"

runtimeOnly "com.h2database:h2:2.2.224"

implementation "org.bouncycastle:bcprov-jdk15to18:1.78"
implementation "org.bouncycastle:bcjmail-lts8on:2.73.6"

implementation "org.jboss.logmanager:jboss-logmanager:3.0.6.Final"

testImplementation "io.quarkus:quarkus-junit5"
testImplementation "io.rest-assured:rest-assured"
testImplementation "io.quarkus:quarkus-jdbc-postgresql"
testImplementation "io.quarkus:quarkus-jdbc-h2"
testImplementation "org.jboss.logmanager:jboss-logmanager:3.0.6.Final"

}

def props = new Properties() props.load(new FileInputStream(new File("src/main/resources/application.properties")))

group 'io.package' version '1.0.0-SNAPSHOT'

java { sourceCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_21 }

test { systemProperty "java.util.logging.manager", "org.jboss.logmanager.LogManager" } allOpen { annotation("jakarta.ws.rs.Path") annotation("jakarta.enterprise.context.ApplicationScoped") annotation("jakarta.persistence.Entity") annotation("io.quarkus.test.junit.QuarkusTest") }

compileKotlin { kotlinOptions.jvmTarget = JavaVersion.VERSION_21 kotlinOptions.javaParameters = true kotlinOptions { freeCompilerArgs += "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi" freeCompilerArgs += "-opt-in=kotlinx.coroutines.ObsoleteCoroutinesApi" freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" freeCompilerArgs += "-opt-in=kotlin.ExperimentalStdlibApi" } }

compileTestKotlin { kotlinOptions.jvmTarget = JavaVersion.VERSION_21 kotlinOptions.javaParameters = true kotlinOptions { freeCompilerArgs += "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi" freeCompilerArgs += "-opt-in=kotlinx.coroutines.ObsoleteCoroutinesApi" freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" freeCompilerArgs += "-opt-in=kotlin.ExperimentalStdlibApi" } }

application { mainClass.set("io.minifyimage.website.App") }

jar { zip64 = true exclude 'META-INF/.RSA', 'META-INF/.SF', 'META-INF/*.DSA' duplicatesStrategy(DuplicatesStrategy.EXCLUDE) from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }

} `

Expected behavior

Push should work.

Actual behavior

Push does not work which means that the website contents does not change.

How to Reproduce?

Steps:

  1. Create project with vaadin + quarkus using examples (gradle)
  2. Add class which extends: AppShellConfigurator with annotation: "@Push(PushMode.AUTOMATIC,transport = Transport.WEBSOCKET)" or just @Push
  3. Create class for website page with annotation @UIScoped
  4. Make a viewModel for the page with which repeats sending same update to the page every X seconds.

Output of uname -a or ver

Darwin MacBook-Air.local 23.5.0 Darwin Kernel Version 23.5.0: Wed May 1 20:14:59 PDT 2024; root:xnu-10063.121.3~5/RELEASE_ARM64_T8122 arm64

Output of java -version

OpenJDK Runtime Environment Zulu22.30+13-CA (build 22.0.1+8) OpenJDK 64-Bit Server VM Zulu22.30+13-CA (build 22.0.1+8, mixed mode, sharing)

Quarkus version or git rev

3.12.0

Build tool (ie. output of mvnw --version or gradlew --version)

8.7

Additional information

No response

Expected-behavior

Push should work.

Reproduction

Steps:

  1. Create project with vaadin + quarkus using examples (gradle)
  2. Add class which extends: AppShellConfigurator with annotation: "@Push(PushMode.AUTOMATIC,transport = Transport.WEBSOCKET)" or just @Push 3/ Create class for website page with annotation @UIScoped 4/ Make a viewModel for the page with which repeats sending same update to the page every X seconds.

System Info

Darwin MacBook-Air.local 23.5.0 Darwin Kernel Version 23.5.0: Wed May 1 20:14:59 PDT 2024; root:xnu-10063.121.3~5/RELEASE_ARM64_T8122 arm64

OpenJDK Runtime Environment Zulu22.30+13-CA (build 22.0.1+8) OpenJDK 64-Bit Server VM Zulu22.30+13-CA (build 22.0.1+8, mixed mode, sharing)

Quarkus version: 3.12.0

Gradle wrapper: 8.7

mcollovati commented 3 months ago

I tried a similar setup based on the Vaadin Quarkus maven starter and cannot reproduce, neither in dev nor in production mode. Push script is loaded correctly and updates happen correctly without user interaction.

@JasonTheDynamite could you please share a full project that reproduces the issue as a GitHub project or attach a zip file to this issue?

Also, is the application behind some reverse-proxy that might block resources from being served to the client?

JasonTheDynamite commented 3 months ago

@mcollovati "Also, is the application behind some reverse-proxy that might block resources from being served to the client?" - no Attaching full reprod. The "https://192.168.1.100/VAADIN/static/push/vaadinPush.js could not be loaded. Push will not work" does not show now but pushmode is still disabled. push-issue_reprod.zip

JasonTheDynamite commented 3 months ago

This "https://192.168.1.100/VAADIN/static/push/vaadinPush.js could not be loaded. Push will not work." is visible when I use the websiite with ssl (https). Without SSL this message is not being displayed. Push stil is DISABLED

mcollovati commented 3 months ago

@JasonTheDynamite I tried your application. I am not familiar with kotlin and gradle, but here are a couple of notes

  1. I don't know much about couroutines in kotlin, but I assume UI changes are done in UI.access() block (In VaadinDispatcher you are using UI.accessSynchronously(), I think it could be UI.access() instead)
  2. According to the application logs, PUSH is enabled and working. PUSH annotation is applied per UI, so you should check the status on it, not on the VaadinSession (e.g. attachEvent!!.ui.pushConfiguration.pushMode instead of VaadinSession.getCurrent().configuration.pushMode);

In the logs below, you can see that push is in AUTOMATIC mode and two changes are sent to the client via PUSH, but then the block in viewModel?.getCounterValueList()?.collectLatest { } gets not invoked anymore.

2024-07-02 14:21:56,102 DEBUG [com.vaa.flo.ser.com.PushHandler] (vert.x-eventloop-thread-1) New push connection for resource 04c339a6-be20-4537-86b8-5c58bc4a77dd with transport WEBSOCKET
2024-07-02 14:21:56,137 ERROR [io.vaa.iss.pag.hom.HomePage] (executor-thread-1) pushMode -> DISABLED
2024-07-02 14:21:56,138 ERROR [io.vaa.iss.pag.hom.HomePage] (executor-thread-1) pushMode UI -> AUTOMATIC
2024-07-02 14:21:56,170 ERROR [io.vaa.iss.pag.hom.HomePage] (executor-thread-1) getCounterValueList / newestValue -> null
2024-07-02 14:21:56,171 ERROR [io.vaa.iss.pag.hom.HomePage] (executor-thread-1) getCounterValueList / counterSpan.text -> 
2024-07-02 14:21:56,179 DEBUG [com.vaa.flo.ser.com.UidlWriter] (executor-thread-1) * Creating response to client
2024-07-02 14:21:56,201 DEBUG [com.vaa.flo.ser.com.PushHandler] (vert.x-eventloop-thread-1) Received message from resource 04c339a6-be20-4537-86b8-5c58bc4a77dd
2024-07-02 14:21:56,204 DEBUG [com.vaa.flo.ser.com.UidlWriter] (vert.x-eventloop-thread-1) * Creating response to client
2024-07-02 14:21:57,157 ERROR [io.vaa.iss.pag.hom.HomePageViewModel] (DefaultDispatcher-worker-1) counterValueList / repeat -> 0
2024-07-02 14:21:57,165 ERROR [io.vaa.iss.pag.hom.HomePage] (DefaultDispatcher-worker-1) getCounterValueList / newestValue -> 0
2024-07-02 14:21:57,165 ERROR [io.vaa.iss.pag.hom.HomePage] (DefaultDispatcher-worker-1) getCounterValueList / counterSpan.text -> 0
2024-07-02 14:21:57,166 DEBUG [com.vaa.flo.ser.com.UidlWriter] (DefaultDispatcher-worker-1) * Creating response to client
2024-07-02 14:21:58,167 ERROR [io.vaa.iss.pag.hom.HomePageViewModel] (DefaultDispatcher-worker-1) counterValueList / repeat -> 1
2024-07-02 14:21:59,169 ERROR [io.vaa.iss.pag.hom.HomePageViewModel] (DefaultDispatcher-worker-1) counterValueList / repeat -> 2
2024-07-02 14:22:00,170 ERROR [io.vaa.iss.pag.hom.HomePageViewModel] (DefaultDispatcher-worker-1) counterValueList / repeat -> 3
2024-07-02 14:22:01,171 ERROR [io.vaa.iss.pag.hom.HomePageViewModel] (DefaultDispatcher-worker-1) counterValueList / repeat -> 4
2024-07-02 14:22:02,172 ERROR [io.vaa.iss.pag.hom.HomePageViewModel] (DefaultDispatcher-worker-1) counterValueList / repeat -> 5
2024-07-02 14:22:03,174 ERROR [io.vaa.iss.pag.hom.HomePageViewModel] (DefaultDispatcher-worker-1) counterValueList / repeat -> 6
2024-07-02 14:22:04,175 ERROR [io.vaa.iss.pag.hom.HomePageViewModel] (DefaultDispatcher-worker-1) counterValueList / repeat -> 7
2024-07-02 14:22:05,175 ERROR [io.vaa.iss.pag.hom.HomePageViewModel] (DefaultDispatcher-worker-1) counterValueList / repeat -> 8
...

Given that, it looks like to me that the problem is not PUSH, but the callback not being invoked on changes applied to getCounterValueList(). Also, clicking the "CLICK to receive all push UI changes" button has no effects. I cannot see any updates to the UI because there a no pending changes.

mcollovati commented 3 months ago

Maybe @mvysny can provide some advice here

JasonTheDynamite commented 3 months ago

@mcollovati ok I will look into it. Then why there is this error message image when ssl is enabled ?

mcollovati commented 3 months ago

Can you please provide the steps to run your application with SSL enabled?

mcollovati commented 3 months ago

Ok, I managed to start the application with SSL. What I can see is that with SSL enabled, the push script is downloaded as a binary file instead of a JavaScript resource. The culprit seems to be this setting quarkus.http.enable-compression=true in application.properties If I remove it, the Vaadin PUSH script is downloaded correctly and there is no error message.

JasonTheDynamite commented 3 months ago

@mcollovati ok so how to use compression if using it crashes push ?

mcollovati commented 3 months ago

Push script is added with a standard <script> tag, so I don't know why it is not working with compression. Other javascript resources are working fine (e.g. index-xxxxx.js). I wonder if it is something specific to Quarkus and SSL.

JasonTheDynamite commented 3 months ago

@mcollovati so it seems I did found a bug ๐Ÿ™‚

mcollovati commented 3 months ago

I wonder if it is a problem of double compression. Flow already provides compressed version of several resources. Maybe Quarkus does not detect that the resource is already compressed and it does an additional compression. It can be tested by downloading vaadinPush.js

JasonTheDynamite commented 3 months ago

It might be this way. I cant test this - javascript is something I am not familiar with

mcollovati commented 3 months ago

I can confirm that the vaadinPush.js is compressed twice. It looks like a Quarkus issue

JasonTheDynamite commented 3 months ago

Hmm so now I should be waiting for a fix ? ๐Ÿ™‚ @mcollovati I am very grateful for your help with this ๐Ÿ™

JasonTheDynamite commented 3 months ago

@mcollovati so I checked everything and it seems that disabling compression helps bu still the UI is not getting updated ๐Ÿ˜ข

mcollovati commented 3 months ago

Unfortunately, I cannot help with Kotlin, but I tried to add the following to the HomePage.onAttach(...) method and I can see the UI updated every second

        val service = Executors.newScheduledThreadPool(1)
        val ui = attachEvent!!.ui
        service.scheduleAtFixedRate({
            ui.access {
                counterSpan.text = LocalTime.now().toString()
            }
        }, 1, 1 , TimeUnit.SECONDS)
mcollovati commented 3 months ago

So, I suppose that there might be some issue in your code that prevents viewModel?.getCounterValueList()?collectLatest() callback being executed.

I hope @mvysny could take a look at your code and provide some suggestion.

JasonTheDynamite commented 3 months ago

@mvysny I did mamy project using vaadin + spring boot and with this combination the same kotlin StateFlow with collect() or collectLatest() works. It does not work with vaadin + quarkus combination

mvysny commented 3 months ago

@JasonTheDynamite regarding Kotlin Coroutines, it's very important to use the correct coroutine dispatcher which calls ui.access(). Please see https://github.com/mvysny/vaadin-coroutines-demo for an example.