diff --git a/guide/backend/src/main/scala/io/udash/web/server/ApplicationServer.scala b/guide/backend/src/main/scala/io/udash/web/server/ApplicationServer.scala index 0605c72ba..61e9b9e2b 100644 --- a/guide/backend/src/main/scala/io/udash/web/server/ApplicationServer.scala +++ b/guide/backend/src/main/scala/io/udash/web/server/ApplicationServer.scala @@ -1,7 +1,8 @@ package io.udash.web.server -import io.udash.rest._ -import io.udash.rpc._ +import com.avsystem.commons.universalOps +import io.udash.rest.* +import io.udash.rpc.* import io.udash.rpc.utils.CallLogging import io.udash.web.guide.demos.activity.{Call, CallLogger} import io.udash.web.guide.demos.rest.MainServerREST @@ -9,11 +10,12 @@ import io.udash.web.guide.rest.ExposedRestInterfaces import io.udash.web.guide.rpc.ExposedRpcInterfaces import io.udash.web.guide.{GuideExceptions, MainServerRPC} import monix.execution.Scheduler +import org.eclipse.jetty.ee10.servlet.{DefaultServlet, ServletContextHandler, ServletHolder} +import org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer +import org.eclipse.jetty.rewrite.handler.{RewriteHandler, RewriteRegexRule} import org.eclipse.jetty.server.Server import org.eclipse.jetty.server.handler.ContextHandlerCollection import org.eclipse.jetty.server.handler.gzip.GzipHandler -import org.eclipse.jetty.servlet.{DefaultServlet, ServletContextHandler, ServletHolder} -import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer class ApplicationServer(val port: Int, homepageResourceBase: String, guideResourceBase: String)(implicit scheduler: Scheduler) { private val server = new Server(port) @@ -27,13 +29,13 @@ class ApplicationServer(val port: Int, homepageResourceBase: String, guideResour } private val homepage = { - val ctx = createContextHandler(Array("udash.io", "www.udash.io", "udash.local", "127.0.0.1")) + val ctx = createContextHandler("udash.io", "www.udash.io", "udash.local", "127.0.0.1") ctx.addServlet(createStaticHandler(homepageResourceBase), "/*") ctx } private val guide = { - val ctx = createContextHandler(Array("guide.udash.io", "www.guide.udash.io", "guide.udash.local", "127.0.0.2", "localhost")) + val ctx = createContextHandler("guide.udash.io", "www.guide.udash.io", "guide.udash.local", "127.0.0.2", "localhost") ctx.getSessionHandler.addEventListener(new org.atmosphere.cpr.SessionSupport()) ctx.addServlet(createStaticHandler(guideResourceBase), "/*") @@ -56,7 +58,7 @@ class ApplicationServer(val port: Int, homepageResourceBase: String, guideResour ctx.addServlet(atmosphereHolder, "/atm/*") //required for org.atmosphere.container.JSR356AsyncSupport - JavaxWebSocketServletContainerInitializer.configure(ctx, null) + JakartaWebSocketServletContainerInitializer.configure(ctx, null) val restHolder = new ServletHolder( RestServlet[MainServerREST](new ExposedRestInterfaces) @@ -67,30 +69,18 @@ class ApplicationServer(val port: Int, homepageResourceBase: String, guideResour } private val contexts = new ContextHandlerCollection - contexts.setHandlers(Array(homepage, guide)) - - private val rewriteHandler = { - import org.eclipse.jetty.rewrite.handler.RewriteRegexRule - val rewrite = new org.eclipse.jetty.rewrite.handler.RewriteHandler() - rewrite.setRewriteRequestURI(true) - rewrite.setRewritePathInfo(false) - - val spaRewrite = new RewriteRegexRule - spaRewrite.setRegex("^/(?!assets|scripts|styles|atm|rest_api)(.*/?)*$") - spaRewrite.setReplacement("/") - rewrite.addRule(spaRewrite) - rewrite.setHandler(contexts) - rewrite - } + contexts.setHandlers(homepage, guide) + + private val rewriteHandler = + new RewriteHandler(contexts).setup(_.addRule(new RewriteRegexRule("^/(?!assets|scripts|styles|atm|rest_api)(.*/?)*$", "/"))) server.setHandler(rewriteHandler) - private def createContextHandler(hosts: Array[String]): ServletContextHandler = { - val context = new ServletContextHandler(ServletContextHandler.SESSIONS) - context.insertHandler(new GzipHandler) - context.setVirtualHosts(hosts) - context - } + private def createContextHandler(hosts: String*): ServletContextHandler = + new ServletContextHandler(ServletContextHandler.SESSIONS).setup { context => + context.insertHandler(new GzipHandler) + context.addVirtualHosts(hosts *) + } private def createStaticHandler(resourceBase: String): ServletHolder = { val appHolder = new ServletHolder(new DefaultServlet) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 7299da0df..0f7d522c7 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -13,11 +13,11 @@ object Dependencies { val scalaTagsVersion = "0.13.1" val scalaCssVersion = "1.0.0" - val servletVersion = "4.0.1" + val servletVersion = "6.0.0" val avsCommonsVersion = "2.17.0" val atmosphereJSVersion = "3.1.3" - val atmosphereVersion = "2.7.14" + val atmosphereVersion = "3.0.8" val upickleVersion = "4.0.0" // Tests only val circeVersion = "0.14.9" // Tests only @@ -28,7 +28,7 @@ object Dependencies { val scalaLoggingVersion = "3.9.5" - val jettyVersion = "10.0.22" + val jettyVersion = "12.0.12" val typesafeConfigVersion = "1.4.3" val flexmarkVersion = "0.64.8" val logbackVersion = "1.3.14" @@ -38,6 +38,7 @@ object Dependencies { val scalatestVersion = "3.2.19" val scalaJsSecureRandomVersion = "1.0.0" // Tests only + val slf4jVersion = "2.0.16" // Tests only val bootstrap4Version = "4.1.3" val bootstrap4DatepickerVersion = "5.39.0" val momentJsVersion = "2.30.1" @@ -52,6 +53,7 @@ object Dependencies { val commonTestDeps = Def.setting(Seq( "org.scalatest" %%% "scalatest" % scalatestVersion, + "org.slf4j" % "slf4j-simple" % slf4jVersion, ).map(_ % Test)) val commonJsTestDeps = Def.setting(Seq( @@ -90,7 +92,7 @@ object Dependencies { )) val rpcJvmDeps = Def.setting(rpcCrossDeps.value ++ Seq( - "javax.servlet" % "javax.servlet-api" % servletVersion, + "jakarta.servlet" % "jakarta.servlet-api" % servletVersion, "org.atmosphere" % "atmosphere-runtime" % atmosphereVersion )) @@ -111,10 +113,9 @@ object Dependencies { )) val restJvmDeps = Def.setting(restCrossDeps.value ++ Seq( - "javax.servlet" % "javax.servlet-api" % servletVersion, + "jakarta.servlet" % "jakarta.servlet-api" % servletVersion, "com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion, - "org.eclipse.jetty" % "jetty-server" % jettyVersion % Test, - "org.eclipse.jetty" % "jetty-servlet" % jettyVersion % Test + "org.eclipse.jetty.ee10" % "jetty-ee10-servlet" % jettyVersion % Test )) val restSjsDeps = restCrossDeps @@ -157,12 +158,11 @@ object Dependencies { )) val backendDeps = Def.setting(Seq( - "com.typesafe.scala-logging" %% "scala-logging" % scalaLoggingVersion, "ch.qos.logback" % "logback-classic" % logbackVersion, "org.codehaus.janino" % "janino" % janinoVersion, //conditional processing in logback "org.eclipse.jetty" % "jetty-rewrite" % jettyVersion, - "org.eclipse.jetty.websocket" % "websocket-javax-server" % jettyVersion, + "org.eclipse.jetty.ee10.websocket" % "jetty-ee10-websocket-jakarta-server" % jettyVersion, "com.typesafe" % "config" % typesafeConfigVersion, diff --git a/rest/.jvm/src/main/scala/io/udash/rest/RestServlet.scala b/rest/.jvm/src/main/scala/io/udash/rest/RestServlet.scala index d09c73086..81bb64b31 100644 --- a/rest/.jvm/src/main/scala/io/udash/rest/RestServlet.scala +++ b/rest/.jvm/src/main/scala/io/udash/rest/RestServlet.scala @@ -1,21 +1,21 @@ package io.udash package rest -import com.avsystem.commons._ +import com.avsystem.commons.* import com.avsystem.commons.annotation.explicitGenerics import com.typesafe.scalalogging.LazyLogging -import io.udash.rest.RestServlet._ -import io.udash.rest.raw._ +import io.udash.rest.RestServlet.* +import io.udash.rest.raw.* import io.udash.utils.URLEncoder +import jakarta.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse} +import jakarta.servlet.{AsyncEvent, AsyncListener} import monix.eval.Task import monix.execution.Scheduler import java.io.ByteArrayOutputStream import java.util.concurrent.atomic.AtomicBoolean -import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse} -import javax.servlet.{AsyncEvent, AsyncListener} import scala.annotation.tailrec -import scala.concurrent.duration._ +import scala.concurrent.duration.* object RestServlet { final val DefaultHandleTimeout = 30.seconds @@ -49,7 +49,7 @@ class RestServlet( scheduler: Scheduler ) extends HttpServlet with LazyLogging { - import RestServlet._ + import RestServlet.* override def service(request: HttpServletRequest, response: HttpServletResponse): Unit = { val asyncContext = request.startAsync() diff --git a/rest/.jvm/src/main/scala/io/udash/rest/openapi/OpenApiServlet.scala b/rest/.jvm/src/main/scala/io/udash/rest/openapi/OpenApiServlet.scala index 073a568e8..74267c8c3 100644 --- a/rest/.jvm/src/main/scala/io/udash/rest/openapi/OpenApiServlet.scala +++ b/rest/.jvm/src/main/scala/io/udash/rest/openapi/OpenApiServlet.scala @@ -4,7 +4,7 @@ package rest.openapi import com.avsystem.commons.OptArg import com.avsystem.commons.annotation.explicitGenerics import com.avsystem.commons.serialization.json.JsonStringOutput -import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse} +import jakarta.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse} object OpenApiServlet { @explicitGenerics def apply[RestApi: OpenApiMetadata]( diff --git a/rest/.jvm/src/test/scala/io/udash/rest/EndpointsIntegrationTest.scala b/rest/.jvm/src/test/scala/io/udash/rest/EndpointsIntegrationTest.scala index ddc750489..a20c7fb30 100644 --- a/rest/.jvm/src/test/scala/io/udash/rest/EndpointsIntegrationTest.scala +++ b/rest/.jvm/src/test/scala/io/udash/rest/EndpointsIntegrationTest.scala @@ -1,13 +1,12 @@ package io.udash package rest -import io.udash.rest.raw._ +import io.udash.rest.raw.* import io.udash.testing.UdashSharedTest import monix.execution.Scheduler +import org.eclipse.jetty.ee10.servlet.{ServletContextHandler, ServletHolder, SessionHandler} import org.eclipse.jetty.server.Server import org.eclipse.jetty.server.handler.gzip.GzipHandler -import org.eclipse.jetty.server.session.SessionHandler -import org.eclipse.jetty.servlet.{ServletContextHandler, ServletHolder} import org.scalatest.BeforeAndAfterAll import org.scalatest.concurrent.{Eventually, ScalaFutures} import org.scalatest.time.{Millis, Seconds, Span} @@ -23,15 +22,18 @@ class EndpointsIntegrationTest extends UdashSharedTest with BeforeAndAfterAll wi val port = 44598 val contextPrefix = "/rest_api" val baseUri = s"http://127.0.0.1:$port$contextPrefix" - val server = new Server(port) - val context = new ServletContextHandler() - context.setSessionHandler(new SessionHandler) - context.insertHandler(new GzipHandler) - private val servlet = io.udash.rest.RestServlet[TestServerRESTInterface](new TestServerRESTInterfaceImpl) - val holder = new ServletHolder(servlet) + val holder = new ServletHolder(io.udash.rest.RestServlet[TestServerRESTInterface](new TestServerRESTInterfaceImpl)) holder.setAsyncSupported(true) - context.addServlet(holder, s"$contextPrefix/*") + + val contextHandler = new ServletContextHandler() + contextHandler.setSessionHandler(new SessionHandler) + contextHandler.addServlet(holder, s"$contextPrefix/*") + + val context = new GzipHandler() + context.setHandler(contextHandler) + + val server = new Server(port) server.setHandler(context) def futureHandle(rawHandle: RawRest.HandleRequest): RestRequest => Future[RestResponse] = diff --git a/rest/.jvm/src/test/scala/io/udash/rest/ServletBasedRestApiTest.scala b/rest/.jvm/src/test/scala/io/udash/rest/ServletBasedRestApiTest.scala index e81a2fcd6..6796c4e57 100644 --- a/rest/.jvm/src/test/scala/io/udash/rest/ServletBasedRestApiTest.scala +++ b/rest/.jvm/src/test/scala/io/udash/rest/ServletBasedRestApiTest.scala @@ -1,10 +1,10 @@ package io.udash package rest +import org.eclipse.jetty.ee10.servlet.{ServletContextHandler, ServletHolder} import org.eclipse.jetty.server.Server -import org.eclipse.jetty.servlet.{ServletHandler, ServletHolder} -import scala.concurrent.duration._ +import scala.concurrent.duration.* abstract class ServletBasedRestApiTest extends RestApiTest with UsesHttpServer { override implicit def patienceConfig: PatienceConfig = PatienceConfig(10.seconds) @@ -14,9 +14,8 @@ abstract class ServletBasedRestApiTest extends RestApiTest with UsesHttpServer { protected def setupServer(server: Server): Unit = { val servlet = new RestServlet(serverHandle, serverTimeout, maxPayloadSize) - val holder = new ServletHolder(servlet) - val handler = new ServletHandler - handler.addServletWithMapping(holder, "/api/*") + val handler = new ServletContextHandler() + handler.addServlet(new ServletHolder(servlet), "/api/*") server.setHandler(handler) } } diff --git a/rest/.jvm/src/test/scala/io/udash/rest/UsesHttpServer.scala b/rest/.jvm/src/test/scala/io/udash/rest/UsesHttpServer.scala index 45be1abb2..6fbe61a85 100644 --- a/rest/.jvm/src/test/scala/io/udash/rest/UsesHttpServer.scala +++ b/rest/.jvm/src/test/scala/io/udash/rest/UsesHttpServer.scala @@ -1,20 +1,25 @@ package io.udash package rest -import org.eclipse.jetty.server.{AbstractNetworkConnector, Server} +import org.eclipse.jetty.http.UriCompliance +import org.eclipse.jetty.server.{AbstractNetworkConnector, HttpConnectionFactory, Server} import org.scalatest.{BeforeAndAfterAll, Suite} trait UsesHttpServer extends BeforeAndAfterAll { this: Suite => private val server: Server = new Server(0) - protected final def port: Int = server.getConnectors.head.asInstanceOf[AbstractNetworkConnector].getLocalPort + private val connector = server.getConnectors.head.asInstanceOf[AbstractNetworkConnector] + protected final def port: Int = connector.getLocalPort protected final def baseUrl = s"http://localhost:$port" protected def setupServer(server: Server): Unit override protected def beforeAll(): Unit = { super.beforeAll() + connector.getConnectionFactory(classOf[HttpConnectionFactory]).getHttpConfiguration.setUriCompliance(UriCompliance.UNSAFE) + setupServer(server) server.start() + } override protected def afterAll(): Unit = { diff --git a/rest/.jvm/src/test/scala/io/udash/rest/examples/ServerMain.scala b/rest/.jvm/src/test/scala/io/udash/rest/examples/ServerMain.scala index 4b3253558..8dd4b3027 100644 --- a/rest/.jvm/src/test/scala/io/udash/rest/examples/ServerMain.scala +++ b/rest/.jvm/src/test/scala/io/udash/rest/examples/ServerMain.scala @@ -3,8 +3,8 @@ package rest.examples import io.udash.rest.RestServlet import monix.execution.Scheduler +import org.eclipse.jetty.ee10.servlet.{ServletContextHandler, ServletHolder} import org.eclipse.jetty.server.Server -import org.eclipse.jetty.servlet.{ServletContextHandler, ServletHolder} import scala.concurrent.Future @@ -23,6 +23,7 @@ object ServerMain { val server = new Server(9090) val handler = new ServletContextHandler + handler.addServlet(new ServletHolder(RestServlet[UserApi](new UserApiImpl)), "/*") server.setHandler(handler) server.start() diff --git a/rest/jetty/src/main/scala/io/udash/rest/jetty/JettyRestClient.scala b/rest/jetty/src/main/scala/io/udash/rest/jetty/JettyRestClient.scala index cec290ae3..c294b16d4 100644 --- a/rest/jetty/src/main/scala/io/udash/rest/jetty/JettyRestClient.scala +++ b/rest/jetty/src/main/scala/io/udash/rest/jetty/JettyRestClient.scala @@ -1,19 +1,16 @@ package io.udash package rest.jetty -import com.avsystem.commons._ +import com.avsystem.commons.* import com.avsystem.commons.annotation.explicitGenerics -import io.udash.rest.raw._ +import io.udash.rest.raw.* import io.udash.utils.URLEncoder import monix.eval.Task -import org.eclipse.jetty.client.HttpClient -import org.eclipse.jetty.client.api.Result -import org.eclipse.jetty.client.util.{BufferingResponseListener, BytesRequestContent, StringRequestContent} -import org.eclipse.jetty.http.{HttpHeader, MimeTypes} +import org.eclipse.jetty.client.* +import org.eclipse.jetty.http.{HttpCookie, HttpHeader, MimeTypes} -import java.net.HttpCookie import java.nio.charset.Charset -import scala.concurrent.duration._ +import scala.concurrent.duration.* import scala.util.{Failure, Success} object JettyRestClient { @@ -46,8 +43,8 @@ object JettyRestClient { case (name, PlainValue(value)) => httpReq.headers(headers => headers.add(name, value)) } request.parameters.cookies.entries.foreach { - case (name, PlainValue(value)) => httpReq.cookie(new HttpCookie( - URLEncoder.encode(name, spaceAsPlus = true), URLEncoder.encode(value, spaceAsPlus = true))) + case (name, PlainValue(value)) => httpReq.cookie(HttpCookie.build( + URLEncoder.encode(name, spaceAsPlus = true), URLEncoder.encode(value, spaceAsPlus = true)).build()) } request.body match { diff --git a/rpc/.jvm/src/main/scala/io/udash/rpc/AtmosphereService.scala b/rpc/.jvm/src/main/scala/io/udash/rpc/AtmosphereService.scala index 4f89e9671..ee5b19de6 100644 --- a/rpc/.jvm/src/main/scala/io/udash/rpc/AtmosphereService.scala +++ b/rpc/.jvm/src/main/scala/io/udash/rpc/AtmosphereService.scala @@ -1,17 +1,16 @@ package io.udash.rpc -import java.util.UUID - -import com.avsystem.commons._ +import com.avsystem.commons.* import com.avsystem.commons.serialization.json.{JsonStringInput, JsonStringOutput} import com.typesafe.scalalogging.{Logger, StrictLogging} -import io.udash.rpc.internals._ +import io.udash.rpc.internals.* import io.udash.rpc.serialization.ExceptionCodecRegistry -import javax.servlet.ServletInputStream -import javax.servlet.http.HttpServletResponse +import jakarta.servlet.ServletInputStream +import jakarta.servlet.http.HttpServletResponse +import org.atmosphere.cpr.* import org.atmosphere.cpr.AtmosphereResource.TRANSPORT -import org.atmosphere.cpr._ +import java.util.UUID import scala.concurrent.Future import scala.concurrent.duration.{DurationInt, FiniteDuration} import scala.util.{Failure, Success, Try} diff --git a/rpc/.jvm/src/main/scala/io/udash/rpc/RpcServlet.scala b/rpc/.jvm/src/main/scala/io/udash/rpc/RpcServlet.scala index 6ed262a45..857b39697 100644 --- a/rpc/.jvm/src/main/scala/io/udash/rpc/RpcServlet.scala +++ b/rpc/.jvm/src/main/scala/io/udash/rpc/RpcServlet.scala @@ -1,8 +1,8 @@ package io.udash.rpc -import javax.servlet.ServletConfig -import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse} -import org.atmosphere.cpr._ +import jakarta.servlet.ServletConfig +import jakarta.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse} +import org.atmosphere.cpr.* /** * Servlet for RPC endpoint. diff --git a/rpc/.jvm/src/main/scala/io/udash/rpc/utils/DefaultAtmosphereFramework.scala b/rpc/.jvm/src/main/scala/io/udash/rpc/utils/DefaultAtmosphereFramework.scala index eec0e3786..81a4a9f34 100644 --- a/rpc/.jvm/src/main/scala/io/udash/rpc/utils/DefaultAtmosphereFramework.scala +++ b/rpc/.jvm/src/main/scala/io/udash/rpc/utils/DefaultAtmosphereFramework.scala @@ -3,10 +3,9 @@ package io.udash.rpc.utils import com.typesafe.scalalogging.Logger import io.udash.rpc.serialization.{DefaultExceptionCodecRegistry, ExceptionCodecRegistry} import io.udash.rpc.{AtmosphereService, AtmosphereServiceConfig} +import jakarta.servlet.ServletConfig import org.atmosphere.cpr.{ApplicationConfig, AtmosphereFramework} -import javax.servlet.ServletConfig - /** AtmosphereFramework with default configuration for Udash. */ class DefaultAtmosphereFramework( config: AtmosphereServiceConfig[_], diff --git a/rpc/.jvm/src/main/scala/io/udash/rpc/utils/FileDownloadServlet.scala b/rpc/.jvm/src/main/scala/io/udash/rpc/utils/FileDownloadServlet.scala index 64251757b..8b3beb021 100644 --- a/rpc/.jvm/src/main/scala/io/udash/rpc/utils/FileDownloadServlet.scala +++ b/rpc/.jvm/src/main/scala/io/udash/rpc/utils/FileDownloadServlet.scala @@ -1,8 +1,9 @@ package io.udash.rpc.utils +import jakarta.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse} + import java.io.File import java.nio.file.Files -import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse} /** Template of a servlet handling files download. */ abstract class FileDownloadServlet extends HttpServlet { diff --git a/rpc/.jvm/src/main/scala/io/udash/rpc/utils/FileUploadServlet.scala b/rpc/.jvm/src/main/scala/io/udash/rpc/utils/FileUploadServlet.scala index c00f6499a..6ee64b140 100644 --- a/rpc/.jvm/src/main/scala/io/udash/rpc/utils/FileUploadServlet.scala +++ b/rpc/.jvm/src/main/scala/io/udash/rpc/utils/FileUploadServlet.scala @@ -1,9 +1,10 @@ package io.udash.rpc.utils +import com.avsystem.commons.* +import jakarta.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse} + import java.io.InputStream import java.nio.file.Paths -import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse} -import com.avsystem.commons._ /** Template of a servlet handling files upload. It takes files from the request and passes data to the `handleFile` method. * @param fileFields Names of file inputs in the HTTP request. */