Updated to support Vaadin 14.9. Updated version

master Version_14.9
Robinson 2023-01-20 16:02:29 +01:00
parent 792e35d1a7
commit 198775ea51
No known key found for this signature in database
GPG Key ID: 8E7DB78588BD6F5C
7 changed files with 83 additions and 215 deletions

36
LICENSE
View File

@ -1,7 +1,7 @@
- VaadinUndertow - Vaadin support for the Undertow web server
[The Apache Software License, Version 2.0]
https://git.dorkbox.com/dorkbox/VaadinUndertow
Copyright 2022
Copyright 2023
Dorkbox LLC
Extra license information
@ -38,53 +38,47 @@
- kotlinx.coroutines - Library support for Kotlin coroutines with multiplatform support
[The Apache Software License, Version 2.0]
https://github.com/Kotlin/kotlinx.coroutines
Copyright 2022
Copyright 2023
JetBrains s.r.o.
- Undertow - High performance non-blocking webserver
[The Apache Software License, Version 2.0]
https://github.com/undertow-io/undertow
Copyright 2023
JBoss
Red Hat, Inc.
Individual contributors as listed in files
- ClassGraph - An uber-fast parallelized Java classpath scanner and module scanner
[The Apache Software License, Version 2.0]
https://github.com/classgraph/classgraph
Copyright 2022
Copyright 2023
Luke Hutchison
- kotlin-logging - Lightweight logging framework for Kotlin
[The Apache Software License, Version 2.0]
https://github.com/MicroUtils/kotlin-logging
Copyright 2022
Copyright 2023
Ohad Shai
- SLF4J - Simple facade or abstraction for various logging frameworks
[MIT License]
http://www.slf4j.org
Copyright 2022
Copyright 2023
QOS.ch
- JUL to SLF4J - Java Util Logging implemented over SLF4J
[MIT License]
http://www.slf4j.org
Copyright 2022
QOS.ch
- Logback - Logback is a logging framework for Java applications
[The Apache Software License, Version 2.0]
http://logback.qos.ch
Copyright 2022
Copyright 2023
QOS.ch
- Vaadin - An open platform for building modern web apps for Java back ends
[The Apache Software License, Version 2.0]
https://github.com/vaadin/
Copyright 2022
Copyright 2023
Vaadin Ltd.
- Undertow - High performance non-blocking webserver
[The Apache Software License, Version 2.0]
https://github.com/undertow-io/undertow
Copyright 2022
JBoss
Red Hat, Inc.
Individual contributors as listed in files
- Updates - Software Update Management
[The Apache Software License, Version 2.0]
https://git.dorkbox.com/dorkbox/Updates

View File

@ -23,7 +23,7 @@ Maven Info
<dependency>
<groupId>com.dorkbox</groupId>
<artifactId>VaadinUndertow</artifactId>
<version>14.8</version>
<version>14.9</version>
</dependency>
</dependencies>
```
@ -33,7 +33,7 @@ Gradle Info
```
dependencies {
...
implementation "com.dorkbox:VaadinUndertow:14.8"
implementation "com.dorkbox:VaadinUndertow:14.9"
}
````

View File

@ -28,12 +28,12 @@ repositories {
gradle.startParameter.showStacktrace = ShowStacktrace.ALWAYS // always show the stacktrace!
plugins {
id("com.dorkbox.GradleUtils") version "3.3"
id("com.dorkbox.Licensing") version "2.17"
id("com.dorkbox.GradleUtils") version "3.8"
id("com.dorkbox.Licensing") version "2.19"
id("com.dorkbox.VersionUpdate") version "2.5"
id("com.dorkbox.GradlePublish") version "1.13"
id("com.dorkbox.GradlePublish") version "1.17"
kotlin("jvm") version "1.7.20"
kotlin("jvm") version "1.7.22"
}
object Extras {
@ -42,7 +42,7 @@ object Extras {
const val name = "VaadinUndertow"
const val id = "VaadinUndertow"
const val version = "14.8"
const val version = "14.9"
const val vendor = "Dorkbox LLC"
const val vendorUrl = "https://dorkbox.com"
@ -51,8 +51,8 @@ object Extras {
val buildDate = Instant.now().toString()
// These MUST be in lock-step with what the GradleVaadin (other project) + gradle.build.kts + VaadinApplication.kt define, otherwise horrific errors can occur.
const val vaadinVer = "14.8.20"
const val undertowVer = "2.2.21.Final"
const val vaadinVer = "14.9.4"
const val undertowVer = "2.2.22.Final"
}
///////////////////////////////
@ -105,12 +105,12 @@ dependencies {
compileOnly("com.vaadin:vaadin:${Extras.vaadinVer}")
// we use undertow 2, with kotlin coroutines on top (with 1 actor per session)
implementation("io.undertow:undertow-core:${Extras.undertowVer}")
implementation("io.undertow:undertow-servlet:${Extras.undertowVer}")
implementation("io.undertow:undertow-websockets-jsr:${Extras.undertowVer}")
api("io.undertow:undertow-core:${Extras.undertowVer}")
api("io.undertow:undertow-servlet:${Extras.undertowVer}")
api("io.undertow:undertow-websockets-jsr:${Extras.undertowVer}")
// Uber-fast, ultra-lightweight Java classpath and module path scanner
api("io.github.classgraph:classgraph:4.8.151")
api("io.github.classgraph:classgraph:4.8.154")
api("com.dorkbox:Updates:1.1")
@ -126,11 +126,12 @@ dependencies {
api("org.slf4j:jul-to-slf4j:2.0.5")
api("ch.qos.logback:logback-core:1.4.5")
compileOnly("ch.qos.logback:logback-classic:1.4.5")
testImplementation("junit:junit:4.13.2")
// api("ch.qos.logback:logback-core:1.4.5")
// compileOnly("ch.qos.logback:logback-classic:1.4.5")
//
//
// testImplementation("com.vaadin:vaadin:${Extras.vaadinVer}")
// testImplementation("ch.qos.logback:logback-classic:1.4.5")
}
tasks.jar.get().apply {

View File

@ -17,7 +17,6 @@ package dorkbox.vaadin
import com.vaadin.flow.server.VaadinContext
import com.vaadin.flow.server.frontend.FrontendUtils
import com.vaadin.flow.server.startup.DevModeInitializer
import dorkbox.vaadin.undertow.*
import dorkbox.vaadin.util.CallingClass
import dorkbox.vaadin.util.TrieClassLoader
@ -45,9 +44,7 @@ import java.io.File
import java.io.IOException
import java.net.URL
import java.util.*
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.*
import java.util.concurrent.atomic.*
import javax.servlet.Servlet
import javax.servlet.ServletContainerInitializer
@ -67,10 +64,10 @@ class VaadinApplication : ExceptionHandler {
/**
* Gets the version number.
*/
const val version = "14.8"
const val version = "14.9"
// this must match the version information in the build.gradle.kts file (this is automatically passed into the plugin)
const val vaadinVersion = "14.8.20"
const val vaadinVersion = "14.9.4"
const val undertowVersion = "2.2.21.Final"
init {

View File

@ -1,170 +0,0 @@
/*
* Copyright 2020 Dorkbox LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.vaadin.undertow
import dorkbox.vaadin.util.logger
import io.undertow.server.Connectors
import io.undertow.server.HandlerWrapper
import io.undertow.server.HttpHandler
import io.undertow.server.HttpServerExchange
import io.undertow.server.session.Session
import io.undertow.server.session.SessionListener
import io.undertow.util.AttachmentKey
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.channels.actor
import kotlinx.coroutines.channels.onFailure
import kotlinx.coroutines.channels.sendBlocking
import kotlinx.coroutines.channels.trySendBlocking
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
val ACTOR = AttachmentKey.create<SendChannel<HttpServerExchange>>(SendChannel::class.java)!!
/**
* This runs just BEFORE the main servlet handler, and lets us run the servlet logic on an actor. Each servlet has it's own actor,
* and can queue 8 "servlet" requests at a time
*/
class CoroutineHttpWrapper(private val sessionCookieName: String, private val capacity: Int, concurrencyFactor: Int) : HandlerWrapper {
private val logger = logger()
var actorsPerSession = ConcurrentHashMap<String, SendChannel<HttpServerExchange>>(8, 0.9f, concurrencyFactor)
private lateinit var defaultActor: SendChannel<HttpServerExchange>
// Our UI Needs to run on a different thread pool so that if the UI is blocked, it doesn't block our cache.
private val uiThreadPool = Executors.newCachedThreadPool(DaemonThreadFactory("HttpWrapper")) as ExecutorService
private val uiDispatcher = uiThreadPool.asCoroutineDispatcher()
private val handler = CoroutineExceptionHandler { _, exception ->
logger.error { "Uncaught Coroutine Error: $exception" }
}
private val scope = CoroutineScope(uiDispatcher + handler)
val job = Job()
// val mutex = Mutex()
@kotlinx.coroutines.ObsoleteCoroutinesApi
private fun createActor(handler: HttpHandler) = scope.actor<HttpServerExchange>(context = job, capacity = capacity) {
logger.info { "Starting actor $this" }
for (msg in channel) {
Connectors.executeRootHandler(handler, msg)
}
logger.info("stopping actor $this")
}
override fun wrap(handler: HttpHandler): HttpHandler {
@kotlinx.coroutines.ObsoleteCoroutinesApi
defaultActor = createActor(handler)
return HttpHandler { exchange ->
exchange.startBlocking()
// IO is on a single thread
// how to check if we are running in the actor?
if (exchange.isInIoThread) {
// check if we've already created the actor for this exchange
var actor: SendChannel<HttpServerExchange> = exchange.getAttachment(ACTOR) ?: defaultActor
if (actor == defaultActor) {
actor = getOrCreateActor(handler, exchange)
exchange.putAttachment(ACTOR, actor)
}
// suppressed because we DO NOT use threading, we use coroutines, which are semantically different
@Suppress("DEPRECATION")
exchange.dispatch() // this marks the exchange as having been DISPATCHED
actor.trySendBlocking(exchange)
.onFailure { t: Throwable? -> logger.error("Error sending data.", t) }
}
else {
// if we are not in the IO thread, just process as normal
handler.handleRequest(exchange)
}
}
}
private fun getOrCreateActor(handler: HttpHandler, exchange: HttpServerExchange): SendChannel<HttpServerExchange> {
// we key off of the session ID for this. If this is changed, then we have to use whatever is different
@Suppress("MoveVariableDeclarationIntoWhen")
val sessionCookie = exchange.getRequestCookie(sessionCookieName)
return when (sessionCookie) {
null -> defaultActor
else -> {
// we have a session ID, so use that to create/use an actor
var maybeActor = actorsPerSession[sessionCookie.value]
if (maybeActor == null) {
@kotlinx.coroutines.ObsoleteCoroutinesApi
maybeActor = createActor(handler)
// pass coroutine info through java?
// https://stackoverflow.com/questions/51808992/kotlin-suspend-fun/51811597#51811597
// NOTE: For vaadin, we also have to create the lock via QUASAR !!!! since vaadin uses explicit locking (on a lock object) VaadinService.lockSession
// /*
// * No lock found in the session attribute. Ensure only one lock is
// * created and used by everybody by doing double checked locking.
// * Assumes there is a memory barrier for the attribute (i.e. that
// * the CPU flushes its caches and reads the value directly from main
// * memory).
// */
// synchronized (VaadinService.class) {
// lock = getSessionLock(wrappedSession);
// if (lock == null) {
// lock = new ReentrantLock();
// setSessionLock(wrappedSession, lock);
// }
// }
actorsPerSession[sessionCookie.value] = maybeActor
exchange.putAttachment(ACTOR, maybeActor)
}
maybeActor
}
}
}
suspend fun stop() = coroutineScope {
job.cancelAndJoin()
actorsPerSession.clear()
}
}
/**
* Destroys the actor when the session is destroyed
*/
class ActorSessionCleanup(private val map: MutableMap<String, SendChannel<HttpServerExchange>>) : SessionListener {
override fun sessionDestroyed(session: Session,
exchange: HttpServerExchange?,
reason: SessionListener.SessionDestroyedReason) {
// destroy the Actor and remove the session object
map.remove(session.id)?.close()
}
}

View File

@ -0,0 +1,21 @@
package dorkbox.vaadin.undertow
import io.undertow.server.HttpHandler
import java.util.function.Function
class HandleBuilder private constructor(private val function: Function<HttpHandler, HttpHandler>) {
companion object {
fun begin(function: Function<HttpHandler, HttpHandler>): HandleBuilder {
return HandleBuilder(function)
}
}
fun next(before: Function<HttpHandler, HttpHandler>): HandleBuilder {
return HandleBuilder(function.compose(before))
}
fun complete(handler: HttpHandler): HttpHandler {
return function.apply(handler)
}
}

View File

@ -0,0 +1,25 @@
package dorkbox.vaadin.undertow
import io.undertow.server.HttpServerExchange
import io.undertow.util.Headers
import io.undertow.util.StatusCodes
object Redirect {
fun temporary(exchange: HttpServerExchange, location: String) {
exchange.statusCode = StatusCodes.FOUND
exchange.responseHeaders.put(Headers.LOCATION, location)
exchange.endExchange()
}
fun permanent(exchange: HttpServerExchange, location: String) {
exchange.statusCode = StatusCodes.MOVED_PERMANENTLY
exchange.responseHeaders.put(Headers.LOCATION, location)
exchange.endExchange()
}
fun referer(exchange: HttpServerExchange) {
exchange.statusCode = StatusCodes.FOUND
exchange.responseHeaders.put(Headers.LOCATION, exchange.requestHeaders[Headers.REFERER, 0])
exchange.endExchange()
}
}