Skip to content

Commit

Permalink
feat: allow disable component tracker
Browse files Browse the repository at this point in the history
Adds a configuration parameter that allows developers to
disable component tracking.

Fixes #18130
  • Loading branch information
mcollovati committed Dec 22, 2023
1 parent c827658 commit 1cad00c
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.vaadin.flow.component.Component;
import com.vaadin.flow.router.internal.AbstractNavigationStateRenderer;
import com.vaadin.flow.server.AbstractConfiguration;
import com.vaadin.flow.server.InitParameters;
import com.vaadin.flow.server.VaadinContext;
import com.vaadin.flow.server.VaadinService;
import com.vaadin.flow.server.startup.ApplicationConfiguration;
Expand All @@ -45,7 +46,7 @@ public class ComponentTracker {
private static Map<Component, Location> attachLocation = Collections
.synchronizedMap(new WeakHashMap<>());

private static Boolean productionMode = null;
private static Boolean disabled = null;
private static String[] prefixesToSkip = new String[] {
"com.vaadin.flow.component.", "com.vaadin.flow.di.",
"com.vaadin.flow.dom.", "com.vaadin.flow.internal.",
Expand Down Expand Up @@ -159,7 +160,7 @@ public static Location findCreate(Component component) {
* the component to track
*/
public static void trackCreate(Component component) {
if (isProductionMode()) {
if (isDisabled()) {
return;
}
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
Expand Down Expand Up @@ -192,7 +193,7 @@ public static Location findAttach(Component component) {
* the component to track
*/
public static void trackAttach(Component component) {
if (isProductionMode()) {
if (isDisabled()) {
return;
}
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
Expand Down Expand Up @@ -270,17 +271,22 @@ private static Location findRelevantLocation(
}

/**
* Checks if the application is running in production mode.
* Checks if the component tracking is disabled.
*
* Tracking is disabled when application is running in production mode or if
* the configuration property
* {@literal vaadin.devmode.componentTracker.enabled} is set to
* {@literal false}.
*
* When unsure, reports that production mode is true so tracking does not
* take place in production.
*
* @return true if in production mode or the mode is unclear, false if in
* development mode
**/
private static boolean isProductionMode() {
if (productionMode != null) {
return productionMode;
private static boolean isDisabled() {
if (disabled != null) {
return disabled;
}

VaadinService service = VaadinService.getCurrent();
Expand All @@ -300,8 +306,11 @@ private static boolean isProductionMode() {
return true;
}

productionMode = applicationConfiguration.isProductionMode();
return productionMode;
disabled = applicationConfiguration.isProductionMode()
|| !applicationConfiguration.getBooleanProperty(
InitParameters.APPLICATION_PARAMETER_DEVMODE_ENABLE_COMPONENT_TRACKER,
true);
return disabled;
}

private static Location toLocation(StackTraceElement stackTraceElement) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ public class InitParameters implements Serializable {
*/
public static final String APPLICATION_PARAMETER_DEVMODE_ENABLE_SERIALIZE_SESSION = "devmode.sessionSerialization.enabled";

/**
* Configuration parameter name for enabling component tracking in
* development mode. If not set, tracking is enabled by default.
*
* @since
*/
public static final String APPLICATION_PARAMETER_DEVMODE_ENABLE_COMPONENT_TRACKER = "devmode.componentTracker.enabled";

/**
* I18N provider property.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,20 @@ public Layout(Component... components) {
}
}

private Object previousProdMode;
private Field prodModeField;
private Object previousDisabled;
private Field disabledField;

@Before
public void setup() throws Exception {
prodModeField = ComponentTracker.class
.getDeclaredField("productionMode");
prodModeField.setAccessible(true);
previousProdMode = prodModeField.get(null);
prodModeField.set(null, false);
disabledField = ComponentTracker.class.getDeclaredField("disabled");
disabledField.setAccessible(true);
previousDisabled = disabledField.get(null);
disabledField.set(null, false);
}

@After
public void teardown() throws Exception {
prodModeField.set(null, previousProdMode);
disabledField.set(null, previousDisabled);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* 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;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.function.Consumer;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;

import com.vaadin.flow.component.internal.ComponentTracker;
import com.vaadin.flow.function.DeploymentConfiguration;
import com.vaadin.flow.internal.CurrentInstance;
import com.vaadin.flow.server.InitParameters;
import com.vaadin.flow.server.MockVaadinServletService;
import com.vaadin.flow.server.VaadinService;
import com.vaadin.flow.server.startup.ApplicationConfiguration;

public class DisableComponentTrackerTest {
private Object previousDisabled;
private Field disabledField;

@Before
public void setup() throws Exception {
disabledField = ComponentTracker.class.getDeclaredField("disabled");
disabledField.setAccessible(true);
previousDisabled = disabledField.get(null);
disabledField.set(null, null);
}

@After
public void teardown() throws Exception {
disabledField.set(null, previousDisabled);
}

@Test
public void trackCreate_disabledInProductionMode() {
withVaadinEnvironment(appCfg -> {
Mockito.when(appCfg.isProductionMode()).thenReturn(true);
Mockito.when(appCfg.getBooleanProperty(ArgumentMatchers.eq(
InitParameters.APPLICATION_PARAMETER_DEVMODE_ENABLE_COMPONENT_TRACKER),
ArgumentMatchers.anyBoolean())).then(i -> i.getArgument(1));

ComponentTrackerTest.Component1 c1 = new ComponentTrackerTest.Component1();
Assert.assertNull(ComponentTracker.findCreate(c1));
});
}

@Test
public void trackAttach_disabledInProductionMode() {
withVaadinEnvironment(appCfg -> {
Mockito.when(appCfg.isProductionMode()).thenReturn(true);
Mockito.when(appCfg.getBooleanProperty(ArgumentMatchers.eq(
InitParameters.APPLICATION_PARAMETER_DEVMODE_ENABLE_COMPONENT_TRACKER),
ArgumentMatchers.anyBoolean())).then(i -> i.getArgument(1));

ComponentTrackerTest.Component1 c1 = new ComponentTrackerTest.Component1();
ComponentTrackerTest.Layout layout = new ComponentTrackerTest.Layout(
c1);
Assert.assertNull(ComponentTracker.findAttach(c1));
});
}

@Test
public void trackCreate_disabledByConfiguration() {
withVaadinEnvironment(appCfg -> {
Mockito.when(appCfg.isProductionMode()).thenReturn(false);
Mockito.when(appCfg.getBooleanProperty(ArgumentMatchers.eq(
InitParameters.APPLICATION_PARAMETER_DEVMODE_ENABLE_COMPONENT_TRACKER),
ArgumentMatchers.anyBoolean())).thenReturn(false);

ComponentTrackerTest.Component1 c1 = new ComponentTrackerTest.Component1();
Assert.assertNull(ComponentTracker.findCreate(c1));
});
}

@Test
public void trackAttach_disabledByConfiguration() {
withVaadinEnvironment(appCfg -> {
Mockito.when(appCfg.isProductionMode()).thenReturn(false);
Mockito.when(appCfg.getBooleanProperty(ArgumentMatchers.eq(
InitParameters.APPLICATION_PARAMETER_DEVMODE_ENABLE_COMPONENT_TRACKER),
ArgumentMatchers.anyBoolean())).thenReturn(false);

ComponentTrackerTest.Component1 c1 = new ComponentTrackerTest.Component1();
ComponentTrackerTest.Layout layout = new ComponentTrackerTest.Layout(
c1);
Assert.assertNull(ComponentTracker.findAttach(c1));
});
}

private void withVaadinEnvironment(
Consumer<ApplicationConfiguration> action) {
DeploymentConfiguration configuration = Mockito
.mock(DeploymentConfiguration.class);
ApplicationConfiguration applicationConfiguration = Mockito
.mock(ApplicationConfiguration.class);
Map<Class<?>, CurrentInstance> instances = CurrentInstance
.getInstances();
CurrentInstance.clearAll();
VaadinService service = new MockVaadinServletService(configuration);
service.getContext().setAttribute(ApplicationConfiguration.class,
applicationConfiguration);
try {
CurrentInstance.set(VaadinService.class, service);
action.accept(applicationConfiguration);
} finally {
CurrentInstance.restoreInstances(instances);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1956,11 +1956,10 @@ public void cannotMoveComponentsToOtherUI() {
}

private void resetComponentTrackerProductionMode() throws Exception {
Field productionMode = ComponentTracker.class
.getDeclaredField("productionMode");
productionMode.setAccessible(true);
productionMode.set(null, null);
productionMode.setAccessible(false);
Field disabled = ComponentTracker.class.getDeclaredField("disabled");
disabled.setAccessible(true);
disabled.set(null, null);
disabled.setAccessible(false);
}

}

0 comments on commit 1cad00c

Please sign in to comment.