Skip to content

Commit

Permalink
feat: Caching for Spring Boot DevTools reload (#17621)
Browse files Browse the repository at this point in the history
Adds some cached data for use in case of Spring Boot DevTools reload.

CustomResourceLoader: added a few packages to default never-scan list
DevModeServletContextListener: Dynamically generates a white list for dev mode package scanner
RouteServletContextListener: Dynamically generates a white list for route package scanner
LookupInitializerListener: Cache found classes
VaadinAppShellContextListener: Cache found classes
CustomResourceLoader: Cache valid/skipped status of resources for later use
Also adds a configuration property (vaadin.devmode-caching) for toggling the feature.

Fixes #17487
  • Loading branch information
tepi authored Sep 15, 2023
1 parent daecc25 commit 349cc11
Show file tree
Hide file tree
Showing 20 changed files with 951 additions and 86 deletions.
1 change: 1 addition & 0 deletions flow-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@
<module>test-live-reload-multimodule/pom-devbundle.xml</module>
<module>test-theme-editor</module>
<module>test-redeployment</module>
<module>test-redeployment-no-cache</module>

<module>test-scalability</module>
<module>test-servlet</module>
Expand Down
109 changes: 109 additions & 0 deletions flow-tests/test-redeployment-no-cache/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>flow-tests</artifactId>
<groupId>com.vaadin</groupId>
<version>24.2-SNAPSHOT</version>
</parent>
<artifactId>flow-test-redeployment-no-cache</artifactId>
<name>Test to ensure dev mode caching is not used when it's disabled</name>
<packaging>jar</packaging>

<properties>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>${jackson.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-test-resources</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-test-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-dev-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-spring</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-test-lumo</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>${spring.boot.version}</version>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<id>start-server</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
</execution>
<execution>
<id>stop-server</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.vaadin</groupId>
<artifactId>flow-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2000-2023 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 com.vaadin.flow.uitest.ui;

import com.vaadin.flow.component.page.AppShellConfigurator;
import com.vaadin.flow.theme.Theme;

public class AppShell implements AppShellConfigurator {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2000-2023 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 com.vaadin.flow.uitest.ui;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright 2000-2023 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 com.vaadin.flow.uitest.ui;

import java.lang.reflect.Field;
import java.util.Set;

import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.router.Route;

@Route(value = "com.vaadin.flow.uitest.ui.DevModeNoClassCacheView")
public class DevModeNoClassCacheView extends Div {

@SuppressWarnings("unchecked")
public DevModeNoClassCacheView() {

try {
Class<?> reloadCacheClass = Class
.forName("com.vaadin.flow.spring.ReloadCache");

// appShellClasses;
Field appShellClassesField = reloadCacheClass
.getDeclaredField("appShellClasses");
appShellClassesField.setAccessible(true);
Set<Class<?>> appShellClasses = (Set<Class<?>>) appShellClassesField
.get(null);
add(new Span(
"appshell class count:" + (appShellClasses == null ? "0"
: appShellClasses.size())));

// lookupClasses;
Field lookupClassesField = reloadCacheClass
.getDeclaredField("lookupClasses");
lookupClassesField.setAccessible(true);
Set<Class<?>> lookupClasses = (Set<Class<?>>) lookupClassesField
.get(null);
add(new Span("lookup class count:"
+ (lookupClasses == null ? "0" : lookupClasses.size())));

// validResources
Field validResourcesField = reloadCacheClass
.getDeclaredField("validResources");
validResourcesField.setAccessible(true);
Set<String> validResources = (Set<String>) validResourcesField
.get(null);
add(new Span("valid resource count:"
+ (validResources == null ? "0" : validResources.size())));

// skippedResources
Field skippedResourcesField = reloadCacheClass
.getDeclaredField("skippedResources");
skippedResourcesField.setAccessible(true);
Set<String> skippedResources = (Set<String>) skippedResourcesField
.get(null);
add(new Span(
"skipped resource count:" + (skippedResources == null ? "0"
: skippedResources.size())));

// dynamicWhiteList;
Field dynamicWhiteListField = reloadCacheClass
.getDeclaredField("dynamicWhiteList");
dynamicWhiteListField.setAccessible(true);
Set<String> dynamicWhiteList = (Set<String>) dynamicWhiteListField
.get(null);
add(new Span("dynamic white list count:"
+ (dynamicWhiteList == null ? "0"
: dynamicWhiteList.size())));

// routePackages;
Field routePackagesField = reloadCacheClass
.getDeclaredField("routePackages");
routePackagesField.setAccessible(true);
Set<String> routePackages = (Set<String>) routePackagesField
.get(null);
Span span = new Span("route packages count:"
+ (routePackages == null ? "0" : routePackages.size()));
span.setId("last-span");
add(span);

} catch (ClassNotFoundException | NoSuchFieldException
| IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
server.port=8888
vaadin.frontend.hotdeploy=true
vaadin.devmode-caching=false
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2000-2023 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 com.vaadin.flow.uitest.ui;

import java.util.List;

import net.jcip.annotations.NotThreadSafe;
import org.junit.Assert;
import org.junit.Test;
import org.openqa.selenium.By;

import com.vaadin.flow.testutil.ChromeBrowserTest;
import com.vaadin.testbench.TestBenchElement;

@NotThreadSafe
public class DevModeNoClassCacheIT extends ChromeBrowserTest {

@Override
protected String getTestPath() {
return super.getTestPath().replace("/view", "");
}

@Test
public void testDevModeClassCacheNotPopulated() {
open();

waitForElementPresent(By.id("last-span"));

List<TestBenchElement> allSpans = $("span").all();

for (int i = 0; i < 6; i++) {
String[] value = allSpans.get(i).getText().split(":");
Assert.assertEquals("Expected " + value[0] + " to be 0.", 0,
Integer.parseInt(value[1]));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2000-2023 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 com.vaadin.flow.uitest.ui;

import java.util.UUID;

import com.vaadin.flow.component.Text;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.NativeButton;
import com.vaadin.flow.component.html.Span;

public abstract class AbstractReloadView extends Div {

public static final String TRIGGER_RELOAD_ID = "triggerReload";

// This ensures the view is not serializable
private Object preventSerialization = new Object();

public UUID viewId = UUID.randomUUID();

protected void addTriggerButton() {
final NativeButton triggerButton = new NativeButton("Trigger reload",
event -> Application.triggerReload());
triggerButton.setId(TRIGGER_RELOAD_ID);
add(triggerButton);
}

protected void addViewId() {
Div div = new Div();
Span span = new Span(viewId.toString());
span.setId("viewId");
div.add(new Text("The view id is: "), span);
add(div);
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
/*
* Copyright 2000-2023 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 com.vaadin.flow.uitest.ui;

import java.io.File;
Expand Down
Loading

0 comments on commit 349cc11

Please sign in to comment.