parent
792e35d1a7
commit
198775ea51
36
LICENSE
36
LICENSE
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
````
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue