Updated devMode to flow 2.7.1 (vaadin 14.7.1)
This commit is contained in:
parent
19228ebd21
commit
faf7164f68
|
@ -1,11 +1,11 @@
|
|||
package dorkbox.vaadin.devMode
|
||||
|
||||
import com.vaadin.flow.server.frontend.scanner.ClassFinder
|
||||
import com.vaadin.flow.server.startup.DevModeInitializer
|
||||
import java.util.*
|
||||
import javax.servlet.annotation.HandlesTypes
|
||||
|
||||
/**
|
||||
* THIS IS COPIED DIRECTLY FROM VAADIN 14.6.8 (flow 2.4.6)
|
||||
* THIS IS COPIED DIRECTLY FROM VAADIN 14.7.1 (flow 2.7.1)
|
||||
*
|
||||
* CHANGES FROM DEFAULT ARE MANAGED AS DIFFERENT REVISIONS.
|
||||
*
|
||||
|
@ -13,33 +13,33 @@ import javax.servlet.annotation.HandlesTypes
|
|||
*
|
||||
* This file is NOT extensible/configurable AT-ALL, so this is required...
|
||||
*/
|
||||
internal class DevModeClassFinder(classes: Set<Class<*>?>?) : ClassFinder.DefaultClassFinder(classes) {
|
||||
companion object {
|
||||
private val APPLICABLE_CLASS_NAMES = Collections.unmodifiableSet(calculateApplicableClassNames())
|
||||
internal class DevModeClassFinder(classes: Set<Class<*>?>?) : com.vaadin.flow.server.frontend.scanner.ClassFinder.DefaultClassFinder(classes) {
|
||||
companion object {
|
||||
private val APPLICABLE_CLASS_NAMES = Collections.unmodifiableSet(calculateApplicableClassNames())
|
||||
|
||||
private fun calculateApplicableClassNames(): Set<String> {
|
||||
val handlesTypes: HandlesTypes = DevModeInitializer::class.java.getAnnotation(HandlesTypes::class.java)
|
||||
return handlesTypes.value.map { it.qualifiedName!! }.toSet()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAnnotatedClasses(annotation: Class<out Annotation?>): Set<Class<*>> {
|
||||
ensureImplementation(annotation)
|
||||
return super.getAnnotatedClasses(annotation)
|
||||
}
|
||||
|
||||
override fun <T> getSubTypesOf(type: Class<T>): Set<Class<out T>> {
|
||||
ensureImplementation(type)
|
||||
return super.getSubTypesOf(type)
|
||||
}
|
||||
|
||||
private fun ensureImplementation(clazz: Class<*>) {
|
||||
require(APPLICABLE_CLASS_NAMES.contains(clazz.name)) {
|
||||
("Unexpected class name "
|
||||
+ clazz + ". Implementation error: the class finder "
|
||||
+ "instance is not aware of this class. "
|
||||
+ "Fix @HandlesTypes annotation value for "
|
||||
+ DevModeInitializer::class.java.name)
|
||||
}
|
||||
private fun calculateApplicableClassNames(): Set<String> {
|
||||
val handlesTypes: HandlesTypes = DevModeInitializer::class.java.getAnnotation(HandlesTypes::class.java)
|
||||
return handlesTypes.value.map { it.qualifiedName!! }.toSet()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAnnotatedClasses(annotation: Class<out Annotation?>): Set<Class<*>> {
|
||||
ensureImplementation(annotation)
|
||||
return super.getAnnotatedClasses(annotation)
|
||||
}
|
||||
|
||||
override fun <T> getSubTypesOf(type: Class<T>): Set<Class<out T>> {
|
||||
ensureImplementation(type)
|
||||
return super.getSubTypesOf(type)
|
||||
}
|
||||
|
||||
private fun ensureImplementation(clazz: Class<*>) {
|
||||
require(APPLICABLE_CLASS_NAMES.contains(clazz.name)) {
|
||||
("Unexpected class name "
|
||||
+ clazz + ". Implementation error: the class finder "
|
||||
+ "instance is not aware of this class. "
|
||||
+ "Fix @HandlesTypes annotation value for "
|
||||
+ DevModeInitializer::class.java.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -25,6 +25,7 @@ import com.vaadin.flow.function.DeploymentConfiguration
|
|||
import com.vaadin.flow.router.HasErrorParameter
|
||||
import com.vaadin.flow.router.Route
|
||||
import com.vaadin.flow.server.*
|
||||
import com.vaadin.flow.server.DevModeHandler
|
||||
import com.vaadin.flow.server.frontend.FrontendUtils
|
||||
import com.vaadin.flow.server.frontend.NodeTasks
|
||||
import com.vaadin.flow.server.startup.ClassLoaderAwareServletContainerInitializer
|
||||
|
@ -44,6 +45,7 @@ import java.net.URLDecoder
|
|||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.CompletionException
|
||||
import java.util.concurrent.Executor
|
||||
|
@ -55,7 +57,7 @@ import javax.servlet.annotation.HandlesTypes
|
|||
import javax.servlet.annotation.WebListener
|
||||
|
||||
/**
|
||||
* THIS IS COPIED DIRECTLY FROM VAADIN 14.6.8 (flow 2.4.6)
|
||||
* THIS IS COPIED DIRECTLY FROM VAADIN 14.7.1 (flow 2.7.1)
|
||||
*
|
||||
* CHANGES FROM DEFAULT ARE MANAGED AS DIFFERENT REVISIONS.
|
||||
*
|
||||
|
@ -65,10 +67,12 @@ import javax.servlet.annotation.WebListener
|
|||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* Servlet initializer starting node updaters as well as the webpack-dev-mode
|
||||
* server.
|
||||
*
|
||||
*
|
||||
* For internal use only. May be renamed or removed in a future release.
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
@HandlesTypes(
|
||||
|
@ -91,6 +95,61 @@ import javax.servlet.annotation.WebListener
|
|||
)
|
||||
@WebListener
|
||||
class DevModeInitializer : ClassLoaderAwareServletContainerInitializer, Serializable, ServletContextListener {
|
||||
@Throws(ServletException::class)
|
||||
override fun process(classes: Set<Class<*>?>?, context: ServletContext) {
|
||||
val registrations: Collection<ServletRegistration> = context.servletRegistrations.values
|
||||
var vaadinServletRegistration: ServletRegistration? = null
|
||||
|
||||
for (registration in registrations) {
|
||||
if (registration.className != null
|
||||
&& isVaadinServletSubClass(registration.className)
|
||||
) {
|
||||
vaadinServletRegistration = registration
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
val config: DeploymentConfiguration
|
||||
config = if (vaadinServletRegistration != null) {
|
||||
StubServletConfig.createDeploymentConfiguration(
|
||||
context,
|
||||
vaadinServletRegistration, VaadinServlet::class.java
|
||||
)
|
||||
} else {
|
||||
StubServletConfig.createDeploymentConfiguration(
|
||||
context,
|
||||
VaadinServlet::class.java
|
||||
)
|
||||
}
|
||||
|
||||
initDevModeHandler(classes, context, config)
|
||||
}
|
||||
|
||||
private fun isVaadinServletSubClass(className: String): Boolean {
|
||||
return try {
|
||||
VaadinServlet::class.java.isAssignableFrom(Class.forName(className))
|
||||
} catch (exception: ClassNotFoundException) { // NOSONAR
|
||||
log().debug(
|
||||
String.format(
|
||||
"Servlet class name (%s) can't be found!",
|
||||
className
|
||||
)
|
||||
)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun contextInitialized(ctx: ServletContextEvent) {
|
||||
// No need to do anything on init
|
||||
}
|
||||
|
||||
override fun contextDestroyed(ctx: ServletContextEvent) {
|
||||
val handler = DevModeHandler.getDevModeHandler()
|
||||
if (handler != null && !handler.reuseDevServer()) {
|
||||
handler.stop()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val JAR_FILE_REGEX = Pattern.compile(".*file:(.+\\.jar).*")
|
||||
|
||||
|
@ -105,11 +164,10 @@ class DevModeInitializer : ClassLoaderAwareServletContainerInitializer, Serializ
|
|||
private val DIR_REGEX_FRONTEND_DEFAULT = Pattern.compile("^(?:file:0)?(.+)" + Constants.RESOURCES_FRONTEND_DEFAULT + "/?$")
|
||||
|
||||
// allow trailing slash
|
||||
private val DIR_REGEX_COMPATIBILITY_FRONTEND_DEFAULT = Pattern.compile(
|
||||
"^(?:file:)?(.+)"
|
||||
+ Constants.COMPATIBILITY_RESOURCES_FRONTEND_DEFAULT
|
||||
+ "/?$"
|
||||
)
|
||||
private val DIR_REGEX_RESOURCES_JAR_DEFAULT = Pattern.compile("^(?:file:0)?(.+)" + Constants.RESOURCES_THEME_JAR_DEFAULT+ "/?$")
|
||||
|
||||
// allow trailing slash
|
||||
private val DIR_REGEX_COMPATIBILITY_FRONTEND_DEFAULT = Pattern.compile("^(?:file:)?(.+)" + Constants.COMPATIBILITY_RESOURCES_FRONTEND_DEFAULT+ "/?$")
|
||||
|
||||
/**
|
||||
* Initialize the devmode server if not in production mode or compatibility
|
||||
|
@ -127,7 +185,6 @@ class DevModeInitializer : ClassLoaderAwareServletContainerInitializer, Serializ
|
|||
*/
|
||||
@Throws(ServletException::class)
|
||||
fun initDevModeHandler(classes: Set<Class<*>?>?, context: ServletContext?, config: DeploymentConfiguration) {
|
||||
System.err.println("CUSTOM INIT!")
|
||||
if (config.isProductionMode) {
|
||||
log().debug("Skipping DEV MODE because PRODUCTION MODE is set.")
|
||||
return
|
||||
|
@ -142,15 +199,20 @@ class DevModeInitializer : ClassLoaderAwareServletContainerInitializer, Serializ
|
|||
}
|
||||
|
||||
// these are BOTH set by VaadinApplication.kt
|
||||
val baseDir = config.getStringProperty(FrontendUtils.PROJECT_BASEDIR, File("").absolutePath)
|
||||
var baseDir = config.getStringProperty(FrontendUtils.PROJECT_BASEDIR, null)
|
||||
if (baseDir == null) {
|
||||
baseDir = baseDirectoryFallback
|
||||
}
|
||||
|
||||
val generatedDir = System.getProperty(FrontendUtils.PARAM_GENERATED_DIR, FrontendUtils.DEFAULT_GENERATED_DIR)
|
||||
val frontendFolder = config.getStringProperty(
|
||||
FrontendUtils.PARAM_FRONTEND_DIR,
|
||||
System.getProperty(FrontendUtils.PARAM_FRONTEND_DIR, FrontendUtils.DEFAULT_FRONTEND_DIR))
|
||||
val frontendFolder = config.getStringProperty(FrontendUtils.PARAM_FRONTEND_DIR,
|
||||
System.getProperty(FrontendUtils.PARAM_FRONTEND_DIR, FrontendUtils.DEFAULT_FRONTEND_DIR))
|
||||
|
||||
|
||||
|
||||
val builder = NodeTasks.Builder(DevModeClassFinder(classes), File(baseDir), File(generatedDir), File(frontendFolder))
|
||||
val builder = NodeTasks.Builder(
|
||||
DevModeClassFinder(classes),
|
||||
File(baseDir), File(generatedDir),
|
||||
File(frontendFolder)
|
||||
)
|
||||
|
||||
log().info("Starting dev-mode updaters in {} folder.", builder.npmFolder)
|
||||
if (!builder.generatedFolder.exists()) {
|
||||
|
@ -168,11 +230,8 @@ class DevModeInitializer : ClassLoaderAwareServletContainerInitializer, Serializ
|
|||
}
|
||||
val generatedPackages = File(builder.generatedFolder, Constants.PACKAGE_JSON)
|
||||
|
||||
// If we are missing the generated webpack configuration then generate
|
||||
// webpack configurations
|
||||
if (!File(builder.npmFolder, FrontendUtils.WEBPACK_GENERATED).exists()) {
|
||||
builder.withWebpack(builder.npmFolder, FrontendUtils.WEBPACK_CONFIG, FrontendUtils.WEBPACK_GENERATED)
|
||||
}
|
||||
// Always update to auto-generated webpack configuration
|
||||
builder.withWebpack(builder.npmFolder, FrontendUtils.WEBPACK_CONFIG, FrontendUtils.WEBPACK_GENERATED)
|
||||
|
||||
// If we are missing either the base or generated package json files
|
||||
// generate those
|
||||
|
@ -180,7 +239,9 @@ class DevModeInitializer : ClassLoaderAwareServletContainerInitializer, Serializ
|
|||
builder.createMissingPackageJson(true)
|
||||
}
|
||||
|
||||
val frontendLocations = getFrontendLocationsFromClassloader(DevModeInitializer::class.java.classLoader)
|
||||
val frontendLocations = getFrontendLocationsFromClassloader(
|
||||
DevModeInitializer::class.java.classLoader
|
||||
)
|
||||
val useByteCodeScanner = config.getBooleanProperty(
|
||||
InitParameters.SERVLET_PARAMETER_DEVMODE_OPTIMIZE_BUNDLE,
|
||||
java.lang.Boolean.parseBoolean(
|
||||
|
@ -190,7 +251,6 @@ class DevModeInitializer : ClassLoaderAwareServletContainerInitializer, Serializ
|
|||
)
|
||||
)
|
||||
)
|
||||
|
||||
val enablePnpm = config.isPnpmEnabled
|
||||
val useHomeNodeExec = config.getBooleanProperty(InitParameters.REQUIRE_HOME_NODE_EXECUTABLE, false)
|
||||
val vaadinContext: VaadinContext = VaadinServletContext(context)
|
||||
|
@ -233,16 +293,12 @@ class DevModeInitializer : ClassLoaderAwareServletContainerInitializer, Serializ
|
|||
)
|
||||
}
|
||||
|
||||
val nodeTasksFuture =
|
||||
if (service is Executor) {
|
||||
// if there is an executor use it to run the task
|
||||
CompletableFuture.runAsync(
|
||||
runnable,
|
||||
service as Executor?
|
||||
)
|
||||
} else {
|
||||
CompletableFuture.runAsync(runnable)
|
||||
}
|
||||
val nodeTasksFuture = if (service is Executor) {
|
||||
// if there is an executor use it to run the task
|
||||
CompletableFuture.runAsync(runnable, service as Executor?)
|
||||
} else {
|
||||
CompletableFuture.runAsync(runnable)
|
||||
}
|
||||
|
||||
DevModeHandler.start(config, builder.npmFolder, nodeTasksFuture)
|
||||
}
|
||||
|
@ -251,10 +307,39 @@ class DevModeInitializer : ClassLoaderAwareServletContainerInitializer, Serializ
|
|||
return LoggerFactory.getLogger(DevModeInitializer::class.java)
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Accept user.dir or cwd as a fallback only if the directory seems to be a
|
||||
* Maven or Gradle project. Check to avoid cluttering server directories
|
||||
* (see tickets #8249, #8403).
|
||||
*/
|
||||
private val baseDirectoryFallback: String
|
||||
private get() {
|
||||
val baseDirCandidate = System.getProperty("user.dir", ".")
|
||||
val path = Paths.get(baseDirCandidate)
|
||||
return if (path.toFile().isDirectory
|
||||
&& (path.resolve("pom.xml").toFile().exists()
|
||||
|| path.resolve("build.gradle").toFile().exists())
|
||||
) {
|
||||
path.toString()
|
||||
} else {
|
||||
throw IllegalStateException(
|
||||
String.format(
|
||||
"Failed to determine project directory for dev mode. "
|
||||
+ "Directory '%s' does not look like a Maven or "
|
||||
+ "Gradle project. Ensure that you have run the "
|
||||
+ "prepare-frontend Maven goal, which generates "
|
||||
+ "'flow-build-info.json', prior to deploying your "
|
||||
+ "application",
|
||||
path.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns all folders of jar files having files in the
|
||||
* META-INF/resources/frontend folder. We don't use URLClassLoader because
|
||||
* will fail in Java 9+
|
||||
* META-INF/resources/frontend and META-INF/resources/themes folder. We
|
||||
* don't use URLClassLoader because will fail in Java 9+
|
||||
*/
|
||||
@Throws(ServletException::class)
|
||||
fun getFrontendLocationsFromClassloader(classLoader: ClassLoader): Set<File> {
|
||||
|
@ -271,10 +356,15 @@ class DevModeInitializer : ClassLoaderAwareServletContainerInitializer, Serializ
|
|||
Constants.COMPATIBILITY_RESOURCES_FRONTEND_DEFAULT
|
||||
)
|
||||
)
|
||||
frontendFiles.addAll(
|
||||
getFrontendLocationsFromClassloader(
|
||||
classLoader,
|
||||
Constants.RESOURCES_THEME_JAR_DEFAULT
|
||||
)
|
||||
)
|
||||
return frontendFiles
|
||||
}
|
||||
|
||||
|
||||
private fun runNodeTasks(vaadinContext: VaadinContext, tokenFileData: JsonObject, tasks: NodeTasks) {
|
||||
try {
|
||||
tasks.execute()
|
||||
|
@ -289,7 +379,9 @@ class DevModeInitializer : ClassLoaderAwareServletContainerInitializer, Serializ
|
|||
}
|
||||
|
||||
@Throws(ServletException::class)
|
||||
private fun getFrontendLocationsFromClassloader(classLoader: ClassLoader, resourcesFolder: String): Set<File> {
|
||||
private fun getFrontendLocationsFromClassloader(
|
||||
classLoader: ClassLoader, resourcesFolder: String
|
||||
): Set<File> {
|
||||
val frontendFiles: MutableSet<File> = HashSet()
|
||||
try {
|
||||
val en = classLoader.getResources(resourcesFolder) ?: return frontendFiles
|
||||
|
@ -302,6 +394,7 @@ class DevModeInitializer : ClassLoaderAwareServletContainerInitializer, Serializ
|
|||
val jarMatcher = JAR_FILE_REGEX.matcher(path)
|
||||
val zipProtocolJarMatcher = ZIP_PROTOCOL_JAR_FILE_REGEX.matcher(path)
|
||||
val dirMatcher = DIR_REGEX_FRONTEND_DEFAULT.matcher(path)
|
||||
val dirResourcesMatcher = DIR_REGEX_RESOURCES_JAR_DEFAULT.matcher(path)
|
||||
val dirCompatibilityMatcher = DIR_REGEX_COMPATIBILITY_FRONTEND_DEFAULT.matcher(path)
|
||||
val jarVfsMatcher = VFS_FILE_REGEX.matcher(urlString)
|
||||
val dirVfsMatcher = VFS_DIRECTORY_REGEX.matcher(urlString)
|
||||
|
@ -312,10 +405,7 @@ class DevModeInitializer : ClassLoaderAwareServletContainerInitializer, Serializ
|
|||
getPhysicalFileOfJBossVfsJar(URL(vfsJar))
|
||||
)
|
||||
} else if (dirVfsMatcher.find()) {
|
||||
val vfsDirUrl = URL(
|
||||
urlString.substring(0,urlString.lastIndexOf(resourcesFolder)
|
||||
)
|
||||
)
|
||||
val vfsDirUrl = URL(urlString.substring(0, urlString.lastIndexOf(resourcesFolder)))
|
||||
frontendFiles.add(getPhysicalFileOfJBossVfsDirectory(vfsDirUrl))
|
||||
} else if (jarMatcher.find()) {
|
||||
frontendFiles.add(File(jarMatcher.group(1)))
|
||||
|
@ -324,6 +414,8 @@ class DevModeInitializer : ClassLoaderAwareServletContainerInitializer, Serializ
|
|||
frontendFiles.add(File(zipProtocolJarMatcher.group(1)))
|
||||
} else if (dirMatcher.find()) {
|
||||
frontendFiles.add(File(dirMatcher.group(1)))
|
||||
} else if (dirResourcesMatcher.find()) {
|
||||
frontendFiles.add(File(dirResourcesMatcher.group(1)))
|
||||
} else if (dirCompatibilityMatcher.find()) {
|
||||
frontendFiles.add(File(dirCompatibilityMatcher.group(1)))
|
||||
} else {
|
||||
|
@ -404,9 +496,7 @@ class DevModeInitializer : ClassLoaderAwareServletContainerInitializer, Serializ
|
|||
if (!(isFileMethod.invoke(child) as Boolean)) continue
|
||||
|
||||
val relativePath = getPathNameRelativeToMethod.invoke(child, jarVirtualFile) as String
|
||||
|
||||
val inputStream = openStreamMethod.invoke(child) as InputStream
|
||||
|
||||
val zipEntry = ZipEntry(relativePath)
|
||||
zipOutputStream.putNextEntry(zipEntry)
|
||||
IOUtils.copy(inputStream, zipOutputStream)
|
||||
|
@ -415,47 +505,4 @@ class DevModeInitializer : ClassLoaderAwareServletContainerInitializer, Serializ
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Throws(ServletException::class)
|
||||
override fun process(classes: Set<Class<*>?>?, context: ServletContext) {
|
||||
val registrations: Collection<ServletRegistration> = context.servletRegistrations.values
|
||||
var vaadinServletRegistration: ServletRegistration? = null
|
||||
|
||||
for (registration in registrations) {
|
||||
try {
|
||||
if (registration.className != null && isVaadinServletSubClass(registration.className)) {
|
||||
vaadinServletRegistration = registration
|
||||
break
|
||||
}
|
||||
} catch (e: ClassNotFoundException) {
|
||||
throw ServletException(String.format("Servlet class name (%s) can't be found!", registration.className), e)
|
||||
}
|
||||
}
|
||||
|
||||
val config =
|
||||
if (vaadinServletRegistration != null) {
|
||||
StubServletConfig.createDeploymentConfiguration(context, vaadinServletRegistration, VaadinServlet::class.java)
|
||||
} else {
|
||||
StubServletConfig.createDeploymentConfiguration(context, VaadinServlet::class.java)
|
||||
}
|
||||
|
||||
initDevModeHandler(classes, context, config)
|
||||
}
|
||||
|
||||
@Throws(ClassNotFoundException::class)
|
||||
private fun isVaadinServletSubClass(className: String): Boolean {
|
||||
return VaadinServlet::class.java.isAssignableFrom(Class.forName(className))
|
||||
}
|
||||
|
||||
override fun contextInitialized(ctx: ServletContextEvent) {
|
||||
// No need to do anything on init
|
||||
}
|
||||
|
||||
override fun contextDestroyed(ctx: ServletContextEvent) {
|
||||
val handler = DevModeHandler.devModeHandler
|
||||
if (handler != null && !handler.reuseDevServer()) {
|
||||
handler.stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package dorkbox.vaadin.devMode
|
||||
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
|
@ -24,17 +25,13 @@ import java.net.Socket
|
|||
import java.nio.charset.StandardCharsets
|
||||
|
||||
/**
|
||||
* THIS IS COPIED DIRECTLY FROM VAADIN 14.6.8 (flow 2.4.6)
|
||||
* THIS IS COPIED DIRECTLY FROM VAADIN 14.7.1 (flow 2.7.1)
|
||||
*
|
||||
* CHANGES FROM DEFAULT ARE MANAGED AS DIFFERENT REVISIONS.
|
||||
*
|
||||
* The initial commit is exactly as-is from vaadin.
|
||||
*
|
||||
* This file is NOT extensible/configurable AT-ALL, so this is required...
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* Opens a server socket which is supposed to be opened until dev mode is active
|
||||
* inside JVM.
|
||||
*
|
||||
|
@ -42,31 +39,20 @@ import java.nio.charset.StandardCharsets
|
|||
* If this socket is closed then there is no anymore Java "client" for the
|
||||
* webpack dev server and it should be stopped.
|
||||
*
|
||||
*
|
||||
* For internal use only. May be renamed or removed in a future release.
|
||||
*
|
||||
* @author Vaadin Ltd
|
||||
* @since 2.0
|
||||
*/
|
||||
internal class DevServerWatchDog {
|
||||
private class WatchDogServer() : Runnable {
|
||||
private val logger = LoggerFactory.getLogger(WatchDogServer::class.java)
|
||||
var server: ServerSocket
|
||||
|
||||
init {
|
||||
try {
|
||||
server = ServerSocket(0)
|
||||
server.soTimeout = 0
|
||||
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug("Watchdog server has started on port {}", server.localPort)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw RuntimeException("Could not open a server socket", e)
|
||||
}
|
||||
}
|
||||
private class WatchDogServer internal constructor() : Runnable {
|
||||
internal var server: ServerSocket? = null
|
||||
|
||||
override fun run() {
|
||||
while (!server.isClosed) {
|
||||
while (!server!!.isClosed) {
|
||||
try {
|
||||
val accept = server.accept()
|
||||
val accept = server!!.accept()
|
||||
accept.soTimeout = 0
|
||||
enterReloadMessageReadLoop(accept)
|
||||
} catch (e: IOException) {
|
||||
|
@ -78,46 +64,67 @@ internal class DevServerWatchDog {
|
|||
}
|
||||
|
||||
fun stop() {
|
||||
try {
|
||||
server.close()
|
||||
} catch (e: IOException) {
|
||||
logger.debug(
|
||||
"Error occurred during close the server socket", e
|
||||
)
|
||||
val server = server
|
||||
if (server != null) {
|
||||
try {
|
||||
server.close()
|
||||
} catch (e: IOException) {
|
||||
logger.debug(
|
||||
"Error occurred during close the server socket", e
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val logger: Logger
|
||||
private get() = LoggerFactory.getLogger(WatchDogServer::class.java)
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun enterReloadMessageReadLoop(accept: Socket) {
|
||||
val lineIn = BufferedReader(InputStreamReader(accept.getInputStream(), StandardCharsets.UTF_8))
|
||||
val `in` = BufferedReader(
|
||||
InputStreamReader(
|
||||
accept.getInputStream(), StandardCharsets.UTF_8
|
||||
)
|
||||
)
|
||||
|
||||
var line = lineIn.readLine()
|
||||
while (line != null) {
|
||||
val devModeHandler = DevModeHandler.devModeHandler
|
||||
|
||||
val liveReload = devModeHandler?.liveReload
|
||||
if (liveReload != null && "reload" == line ) {
|
||||
liveReload.reload()
|
||||
var line: String
|
||||
while (`in`.readLine().also { line = it } != null) {
|
||||
val devModeHandler: DevModeHandler? = DevModeHandler.devModeHandler
|
||||
if ("reload" == line) {
|
||||
devModeHandler?.liveReload?.reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
line = lineIn.readLine()
|
||||
init {
|
||||
try {
|
||||
val server = ServerSocket(0)
|
||||
this.server = server
|
||||
|
||||
server.soTimeout = 0
|
||||
if (logger.isDebugEnabled) {
|
||||
logger.debug(
|
||||
"Watchdog server has started on port {}",
|
||||
server.localPort
|
||||
)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw RuntimeException("Could not open a server socket", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val watchDogServer: WatchDogServer = WatchDogServer()
|
||||
|
||||
private val watchDogServer: WatchDogServer
|
||||
val watchDogPort: Int
|
||||
get() = watchDogServer.server.localPort
|
||||
get() = watchDogServer.server!!.localPort
|
||||
|
||||
fun stop() {
|
||||
watchDogServer.stop()
|
||||
}
|
||||
|
||||
init {
|
||||
val serverThread = Thread(watchDogServer, "DevServer-Watchdog")
|
||||
watchDogServer = WatchDogServer()
|
||||
val serverThread = Thread(watchDogServer)
|
||||
serverThread.isDaemon = true
|
||||
serverThread.start()
|
||||
}
|
||||
|
|
|
@ -1,12 +1,24 @@
|
|||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
/*
|
||||
* Copyright 2000-2020 Vaadin Ltd.
|
||||
*
|
||||
* 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.devMode
|
||||
|
||||
import com.vaadin.flow.server.VaadinRequest
|
||||
import com.vaadin.flow.server.VaadinService
|
||||
import com.vaadin.flow.server.VaadinSession
|
||||
import com.vaadin.flow.shared.ApplicationConstants
|
||||
import java.io.Serializable
|
||||
import java.io.UnsupportedEncodingException
|
||||
import java.net.URLDecoder
|
||||
|
@ -16,31 +28,83 @@ import java.util.function.BiConsumer
|
|||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
* THIS IS COPIED DIRECTLY FROM VAADIN 14.6.8 (flow 2.4.6)
|
||||
* THIS IS COPIED DIRECTLY FROM VAADIN 14.7.1 (flow 2.7.1)
|
||||
*
|
||||
* CHANGES FROM DEFAULT ARE MANAGED AS DIFFERENT REVISIONS.
|
||||
*
|
||||
* The initial commit is exactly as-is from vaadin.
|
||||
*
|
||||
* This file is NOT extensible/configurable AT-ALL, so this is required...
|
||||
*
|
||||
* Contains helper methods for [VaadinServlet] and generally for handling
|
||||
* [VaadinRequests][VaadinRequest].
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
object HandlerHelper : Serializable {
|
||||
// static final SystemMessages DEFAULT_SYSTEM_MESSAGES = new SystemMessages();
|
||||
/**
|
||||
* The default SystemMessages (read-only).
|
||||
*/
|
||||
// static final SystemMessages DEFAULT_SYSTEM_MESSAGES = new SystemMessages(); // NOTE: not compatible with how we pull out this class
|
||||
|
||||
/**
|
||||
* The pattern of error message shown when the URL path contains unsafe
|
||||
* double encoding.
|
||||
*/
|
||||
const val UNSAFE_PATH_ERROR_MESSAGE_PATTERN = "Blocked attempt to access file: {}"
|
||||
private val PARENT_DIRECTORY_REGEX = Pattern.compile("(/|\\\\)\\.\\.(/|\\\\)?", 2)
|
||||
fun isRequestType(request: VaadinRequest, requestType: RequestType): Boolean {
|
||||
return requestType.identifier == request.getParameter("v-r")
|
||||
private val PARENT_DIRECTORY_REGEX = Pattern
|
||||
.compile("(/|\\\\)\\.\\.(/|\\\\)?", Pattern.CASE_INSENSITIVE)
|
||||
|
||||
/**
|
||||
* Returns whether the given request is of the given type.
|
||||
*
|
||||
* @param request
|
||||
* the request to check
|
||||
* @param requestType
|
||||
* the type to check for
|
||||
* @return `true` if the request is of the given type,
|
||||
* `false` otherwise
|
||||
*/
|
||||
fun isRequestType(
|
||||
request: VaadinRequest,
|
||||
requestType: RequestType
|
||||
): Boolean {
|
||||
return requestType.identifier == request
|
||||
.getParameter(ApplicationConstants.REQUEST_TYPE_PARAMETER)
|
||||
}
|
||||
|
||||
fun findLocale(session: VaadinSession?, request: VaadinRequest?): Locale {
|
||||
/**
|
||||
* Helper to find the most most suitable Locale. These potential sources are
|
||||
* checked in order until a Locale is found:
|
||||
*
|
||||
* 1. The passed component (or UI) if not null
|
||||
* 1. [UI.getCurrent] if defined
|
||||
* 1. The passed session if not null
|
||||
* 1. [VaadinSession.getCurrent] if defined
|
||||
* 1. The passed request if not null
|
||||
* 1. [VaadinService.getCurrentRequest] if defined
|
||||
* 1. [Locale.getDefault]
|
||||
*
|
||||
*
|
||||
* @param session
|
||||
* the session that is searched for locale or `null`
|
||||
* if not available
|
||||
* @param request
|
||||
* the request that is searched for locale or `null`
|
||||
* if not available
|
||||
* @return the found locale
|
||||
*/
|
||||
fun findLocale(
|
||||
session: VaadinSession?,
|
||||
request: VaadinRequest?
|
||||
): Locale {
|
||||
var session = session
|
||||
var request = request
|
||||
if (session == null) {
|
||||
session = VaadinSession.getCurrent()
|
||||
}
|
||||
var locale: Locale?
|
||||
if (session != null) {
|
||||
locale = session.locale
|
||||
val locale = session.locale
|
||||
if (locale != null) {
|
||||
return locale
|
||||
}
|
||||
|
@ -49,7 +113,7 @@ object HandlerHelper : Serializable {
|
|||
request = VaadinService.getCurrentRequest()
|
||||
}
|
||||
if (request != null) {
|
||||
locale = request.locale
|
||||
val locale = request.locale
|
||||
if (locale != null) {
|
||||
return locale
|
||||
}
|
||||
|
@ -57,14 +121,34 @@ object HandlerHelper : Serializable {
|
|||
return Locale.getDefault()
|
||||
}
|
||||
|
||||
fun setResponseNoCacheHeaders(headerSetter: BiConsumer<String?, String?>, longHeaderSetter: BiConsumer<String?, Long?>) {
|
||||
/**
|
||||
* Sets no cache headers to the specified response.
|
||||
*
|
||||
* @param headerSetter
|
||||
* setter for string value headers
|
||||
* @param longHeaderSetter
|
||||
* setter for long value headers
|
||||
*/
|
||||
fun setResponseNoCacheHeaders(
|
||||
headerSetter: BiConsumer<String?, String?>,
|
||||
longHeaderSetter: BiConsumer<String?, Long?>
|
||||
) {
|
||||
headerSetter.accept("Cache-Control", "no-cache, no-store")
|
||||
headerSetter.accept("Pragma", "no-cache")
|
||||
longHeaderSetter.accept("Expires", 0L)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a relative path that cancels the provided path. This essentially
|
||||
* adds one .. for each part of the path to cancel.
|
||||
*
|
||||
* @param pathToCancel
|
||||
* the path that should be canceled
|
||||
* @return a relative path that cancels out the provided path segment
|
||||
*/
|
||||
fun getCancelingRelativePath(pathToCancel: String): String {
|
||||
val sb = StringBuilder(".")
|
||||
// Start from i = 1 to ignore first slash
|
||||
for (i in 1 until pathToCancel.length) {
|
||||
if (pathToCancel[i] == '/') {
|
||||
sb.append("/..")
|
||||
|
@ -73,19 +157,56 @@ object HandlerHelper : Serializable {
|
|||
return sb.toString()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
/**
|
||||
* Checks if the given URL path contains the directory change instruction
|
||||
* (dot-dot), taking into account possible double encoding in hexadecimal
|
||||
* format, which can be injected maliciously.
|
||||
*
|
||||
* @param path
|
||||
* the URL path to be verified.
|
||||
* @return `true`, if the given path has a directory change
|
||||
* instruction, `false` otherwise.
|
||||
*/
|
||||
fun isPathUnsafe(path: String?): Boolean {
|
||||
// Check that the path does not have '/../', '\..\', %5C..%5C,
|
||||
// %2F..%2F, nor '/..', '\..', %5C.., %2F..
|
||||
var path = path
|
||||
path = try {
|
||||
URLDecoder.decode(path, StandardCharsets.UTF_8.name())
|
||||
} catch (var2: UnsupportedEncodingException) {
|
||||
throw RuntimeException("An error occurred during decoding URL.", var2)
|
||||
} catch (e: UnsupportedEncodingException) {
|
||||
throw RuntimeException(
|
||||
"An error occurred during decoding URL.",
|
||||
e
|
||||
)
|
||||
}
|
||||
return PARENT_DIRECTORY_REGEX.matcher(path).find()
|
||||
}
|
||||
|
||||
enum class RequestType(val identifier: String) {
|
||||
UIDL("uidl"), HEARTBEAT("heartbeat"), PUSH("push");
|
||||
/**
|
||||
* Framework internal enum for tracking the type of a request.
|
||||
*/
|
||||
enum class RequestType(
|
||||
/**
|
||||
* Returns the identifier for the request type.
|
||||
*
|
||||
* @return the identifier
|
||||
*/
|
||||
val identifier: String
|
||||
) {
|
||||
/**
|
||||
* UIDL requests.
|
||||
*/
|
||||
UIDL(ApplicationConstants.REQUEST_TYPE_UIDL),
|
||||
|
||||
/**
|
||||
* Heartbeat requests.
|
||||
*/
|
||||
HEARTBEAT(ApplicationConstants.REQUEST_TYPE_HEARTBEAT),
|
||||
|
||||
/**
|
||||
* Push requests (any transport).
|
||||
*/
|
||||
PUSH(ApplicationConstants.REQUEST_TYPE_PUSH);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue