From bad717d2f866880fdf194bddb9dc0ad5026fb124 Mon Sep 17 00:00:00 2001 From: Ilia Motornyi Date: Thu, 24 May 2018 11:59:22 +0300 Subject: [PATCH] Make classes serializable (#4148) * Make classes serializable * Sponarqube fixes * Fixes * Fixes * Fixes * Fixes * Fixes * Refactoring and optimization * Small fix * SplitPackagesTest ignores flow-test-generic * Make sonarqube happy * Use sl4j * Add a copyright * Optimize, fix sonarqube issues * Make the test friendly for the depended projects * After review fix, small improvements * Small improvements * Javadoc --- .../vaadin/flow/data/binder/HasValidator.java | 4 +- .../flow/data/provider/ArrayUpdater.java | 2 +- .../flow/data/provider/DataCommunicator.java | 2 +- .../flow/data/value/HasValueChangeMode.java | 4 +- flow-server/pom.xml | 7 + .../com/vaadin/flow/component/Component.java | 3 +- .../com/vaadin/flow/component/HasElement.java | 4 +- .../vaadin/flow/component/HasValidation.java | 4 +- .../com/vaadin/flow/component/HasValue.java | 4 +- .../flow/component/PropertyDescriptor.java | 4 +- .../component/internal/EventDataCache.java | 3 +- .../internal/HtmlDependencyParser.java | 3 +- .../java/com/vaadin/flow/dom/ClassList.java | 3 +- .../flow/dom/ElementAttachListener.java | 4 +- .../flow/dom/ElementDetachListener.java | 4 +- .../java/com/vaadin/flow/dom/ThemeList.java | 3 +- .../flow/dom/impl/ImmutableClassList.java | 3 +- .../vaadin/flow/dom/impl/ThemeListImpl.java | 3 +- .../flow/function/SerializablePredicate.java | 19 +- .../com/vaadin/flow/i18n/I18NProvider.java | 3 +- .../flow/i18n/LocaleChangeObserver.java | 4 +- .../vaadin/flow/internal/ReflectionCache.java | 10 +- .../com/vaadin/flow/internal/StateTree.java | 3 +- .../nodefeature/ElementListenerMap.java | 3 +- .../nodefeature/NodeFeatureRegistry.java | 11 +- .../vaadin/flow/router/HasDynamicTitle.java | 4 +- .../vaadin/flow/router/HasErrorParameter.java | 4 +- .../vaadin/flow/router/HasUrlParameter.java | 4 +- .../internal/AfterNavigationHandler.java | 4 +- .../router/internal/BeforeLeaveHandler.java | 4 +- .../router/internal/DefaultRouteResolver.java | 3 +- .../flow/server/InitialPageSettings.java | 3 +- .../vaadin/flow/server/PageConfigurator.java | 4 +- .../server/communication/rpc/RpcDecoder.java | 4 +- .../rpc/RpcInvocationHandler.java | 3 +- .../startup/AbstractAnnotationValidator.java | 3 +- .../AbstractRouteRegistryInitializer.java | 3 +- .../flow/server/startup/CustomElements.java | 3 +- .../flow/server/startup/RouteRegistry.java | 2 +- .../vaadin/flow/shared/VaadinUriResolver.java | 2 +- .../flow/templatemodel/PropertyFilter.java | 2 +- .../flow/templatemodel/TemplateModel.java | 3 +- .../templatemodel/TemplateModelListProxy.java | 3 +- .../TemplateModelProxyHandler.java | 2 +- .../vaadin/packaging/SplitPackagesTest.java | 2 +- .../FlowClassesSerializableTest.java | 7 + flow-test-generic/pom.xml | 26 + .../testutil/ClassesSerializableTest.java | 453 ++++++++++++++++++ pom.xml | 1 + 49 files changed, 613 insertions(+), 53 deletions(-) create mode 100644 flow-server/src/test/java/com/vaadin/tests/server/component/FlowClassesSerializableTest.java create mode 100644 flow-test-generic/pom.xml create mode 100644 flow-test-generic/src/main/java/com/vaadin/flow/testutil/ClassesSerializableTest.java diff --git a/flow-data/src/main/java/com/vaadin/flow/data/binder/HasValidator.java b/flow-data/src/main/java/com/vaadin/flow/data/binder/HasValidator.java index 4b3aeb17df5..0931edf46ed 100644 --- a/flow-data/src/main/java/com/vaadin/flow/data/binder/HasValidator.java +++ b/flow-data/src/main/java/com/vaadin/flow/data/binder/HasValidator.java @@ -15,6 +15,8 @@ */ package com.vaadin.flow.data.binder; +import java.io.Serializable; + /** * A generic interface for field components and other user interface objects * that have a user-editable value that should be validated. @@ -24,7 +26,7 @@ * @param * the value type */ -public interface HasValidator { +public interface HasValidator extends Serializable { /** * Returns a validator that checks the state of the Value. This should be diff --git a/flow-data/src/main/java/com/vaadin/flow/data/provider/ArrayUpdater.java b/flow-data/src/main/java/com/vaadin/flow/data/provider/ArrayUpdater.java index fb69949e1b1..f732fb0bebd 100644 --- a/flow-data/src/main/java/com/vaadin/flow/data/provider/ArrayUpdater.java +++ b/flow-data/src/main/java/com/vaadin/flow/data/provider/ArrayUpdater.java @@ -32,7 +32,7 @@ public interface ArrayUpdater extends Serializable { * Array updater strategy. * */ - public interface Update { + public interface Update extends Serializable { /** * Clears {@code length} elements in array from the {@code start} * position. diff --git a/flow-data/src/main/java/com/vaadin/flow/data/provider/DataCommunicator.java b/flow-data/src/main/java/com/vaadin/flow/data/provider/DataCommunicator.java index 6836a784fe2..5edadac895a 100644 --- a/flow-data/src/main/java/com/vaadin/flow/data/provider/DataCommunicator.java +++ b/flow-data/src/main/java/com/vaadin/flow/data/provider/DataCommunicator.java @@ -101,7 +101,7 @@ public class DataCommunicator implements Serializable { private SerializableConsumer flushRequest; private SerializableConsumer flushUpdatedDataRequest; - private static class SizeVerifier implements Consumer { + private static class SizeVerifier implements Consumer, Serializable { private int size; diff --git a/flow-data/src/main/java/com/vaadin/flow/data/value/HasValueChangeMode.java b/flow-data/src/main/java/com/vaadin/flow/data/value/HasValueChangeMode.java index 42c84daab68..72b31848e51 100644 --- a/flow-data/src/main/java/com/vaadin/flow/data/value/HasValueChangeMode.java +++ b/flow-data/src/main/java/com/vaadin/flow/data/value/HasValueChangeMode.java @@ -16,6 +16,8 @@ package com.vaadin.flow.data.value; +import java.io.Serializable; + import com.vaadin.flow.component.AbstractSinglePropertyField; import com.vaadin.flow.component.HasValue; @@ -30,7 +32,7 @@ * * @author Vaadin Ltd. */ -public interface HasValueChangeMode { +public interface HasValueChangeMode extends Serializable { /** * Gets current value change mode of the component. diff --git a/flow-server/pom.xml b/flow-server/pom.xml index b95252a1ccb..98b645f2379 100644 --- a/flow-server/pom.xml +++ b/flow-server/pom.xml @@ -21,6 +21,13 @@ ${project.version} + + com.vaadin + flow-test-generic + ${project.version} + test + + diff --git a/flow-server/src/main/java/com/vaadin/flow/component/Component.java b/flow-server/src/main/java/com/vaadin/flow/component/Component.java index 9c1eebb4598..cb78d843b4b 100644 --- a/flow-server/src/main/java/com/vaadin/flow/component/Component.java +++ b/flow-server/src/main/java/com/vaadin/flow/component/Component.java @@ -15,6 +15,7 @@ */ package com.vaadin.flow.component; +import java.io.Serializable; import java.util.List; import java.util.Locale; import java.util.Optional; @@ -52,7 +53,7 @@ public abstract class Component * Encapsulates data required for mapping a new component instance to an * existing element. */ - static class MapToExistingElement { + static class MapToExistingElement implements Serializable { Element element = null; private boolean mapElementToComponent = false; diff --git a/flow-server/src/main/java/com/vaadin/flow/component/HasElement.java b/flow-server/src/main/java/com/vaadin/flow/component/HasElement.java index 3604ae963bb..0c97405e0fa 100644 --- a/flow-server/src/main/java/com/vaadin/flow/component/HasElement.java +++ b/flow-server/src/main/java/com/vaadin/flow/component/HasElement.java @@ -15,6 +15,8 @@ */ package com.vaadin.flow.component; +import java.io.Serializable; + import com.vaadin.flow.dom.Element; /** @@ -23,7 +25,7 @@ * @author Vaadin Ltd */ @FunctionalInterface -public interface HasElement { +public interface HasElement extends Serializable { /** * Gets the element associated with this instance. * diff --git a/flow-server/src/main/java/com/vaadin/flow/component/HasValidation.java b/flow-server/src/main/java/com/vaadin/flow/component/HasValidation.java index 442a938f01e..efd8c10a101 100644 --- a/flow-server/src/main/java/com/vaadin/flow/component/HasValidation.java +++ b/flow-server/src/main/java/com/vaadin/flow/component/HasValidation.java @@ -16,12 +16,14 @@ package com.vaadin.flow.component; +import java.io.Serializable; + /** * A component that supports input validation. * * @author Vaadin Ltd. */ -public interface HasValidation { +public interface HasValidation extends Serializable { /** * Sets an error message to the component. diff --git a/flow-server/src/main/java/com/vaadin/flow/component/HasValue.java b/flow-server/src/main/java/com/vaadin/flow/component/HasValue.java index b812769216a..2729174870e 100644 --- a/flow-server/src/main/java/com/vaadin/flow/component/HasValue.java +++ b/flow-server/src/main/java/com/vaadin/flow/component/HasValue.java @@ -34,7 +34,7 @@ * the value type * @author Vaadin Ltd. */ -public interface HasValue, V> { +public interface HasValue, V> extends Serializable { /** * An event fired when the value of a {@code HasValue} changes. @@ -42,7 +42,7 @@ public interface HasValue, V> { * @param * the value type */ - interface ValueChangeEvent { + interface ValueChangeEvent extends Serializable { HasValue getHasValue(); /** diff --git a/flow-server/src/main/java/com/vaadin/flow/component/PropertyDescriptor.java b/flow-server/src/main/java/com/vaadin/flow/component/PropertyDescriptor.java index 470294f5072..ceb298abec0 100644 --- a/flow-server/src/main/java/com/vaadin/flow/component/PropertyDescriptor.java +++ b/flow-server/src/main/java/com/vaadin/flow/component/PropertyDescriptor.java @@ -15,6 +15,8 @@ */ package com.vaadin.flow.component; +import java.io.Serializable; + import com.vaadin.flow.dom.Element; /** @@ -34,7 +36,7 @@ * the type used when getting the property value, this is typically * either S or Optional<S> */ -public interface PropertyDescriptor { +public interface PropertyDescriptor extends Serializable { /** * Sets the property value for the given component. diff --git a/flow-server/src/main/java/com/vaadin/flow/component/internal/EventDataCache.java b/flow-server/src/main/java/com/vaadin/flow/component/internal/EventDataCache.java index f29570f3548..fb93a5fec7c 100644 --- a/flow-server/src/main/java/com/vaadin/flow/component/internal/EventDataCache.java +++ b/flow-server/src/main/java/com/vaadin/flow/component/internal/EventDataCache.java @@ -15,6 +15,7 @@ */ package com.vaadin.flow.component.internal; +import java.io.Serializable; import java.lang.reflect.Constructor; import java.util.LinkedHashMap; import java.util.Optional; @@ -28,7 +29,7 @@ * * @author Vaadin Ltd */ -public class EventDataCache { +public class EventDataCache implements Serializable { private ConcurrentHashMap>, LinkedHashMap>> dataExpressions = new ConcurrentHashMap<>(); private ConcurrentHashMap>, Constructor> eventConstructors = new ConcurrentHashMap<>(); diff --git a/flow-server/src/main/java/com/vaadin/flow/component/internal/HtmlDependencyParser.java b/flow-server/src/main/java/com/vaadin/flow/component/internal/HtmlDependencyParser.java index 09580b35c3b..0df47a43f64 100644 --- a/flow-server/src/main/java/com/vaadin/flow/component/internal/HtmlDependencyParser.java +++ b/flow-server/src/main/java/com/vaadin/flow/component/internal/HtmlDependencyParser.java @@ -17,6 +17,7 @@ import java.io.IOException; import java.io.InputStream; +import java.io.Serializable; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; @@ -45,7 +46,7 @@ * @author Vaadin Ltd * */ -public class HtmlDependencyParser { +public class HtmlDependencyParser implements Serializable { private final String root; diff --git a/flow-server/src/main/java/com/vaadin/flow/dom/ClassList.java b/flow-server/src/main/java/com/vaadin/flow/dom/ClassList.java index 56ae302e9c7..f6b740d6da9 100644 --- a/flow-server/src/main/java/com/vaadin/flow/dom/ClassList.java +++ b/flow-server/src/main/java/com/vaadin/flow/dom/ClassList.java @@ -15,6 +15,7 @@ */ package com.vaadin.flow.dom; +import java.io.Serializable; import java.util.Set; /** @@ -22,7 +23,7 @@ * * @author Vaadin Ltd */ -public interface ClassList extends Set { +public interface ClassList extends Set, Serializable { /** * Sets or removes the given class name, based on the {@code set} parameter. diff --git a/flow-server/src/main/java/com/vaadin/flow/dom/ElementAttachListener.java b/flow-server/src/main/java/com/vaadin/flow/dom/ElementAttachListener.java index 124e01b20fb..b3cc66a578e 100644 --- a/flow-server/src/main/java/com/vaadin/flow/dom/ElementAttachListener.java +++ b/flow-server/src/main/java/com/vaadin/flow/dom/ElementAttachListener.java @@ -15,12 +15,14 @@ */ package com.vaadin.flow.dom; +import java.io.Serializable; + /** * Listener for element attach events. It is invoked when the element is * attached to the UI. */ @FunctionalInterface -public interface ElementAttachListener { +public interface ElementAttachListener extends Serializable { /** * Invoked when an element is attached to the UI. * diff --git a/flow-server/src/main/java/com/vaadin/flow/dom/ElementDetachListener.java b/flow-server/src/main/java/com/vaadin/flow/dom/ElementDetachListener.java index 7acca62f592..2684c29e921 100644 --- a/flow-server/src/main/java/com/vaadin/flow/dom/ElementDetachListener.java +++ b/flow-server/src/main/java/com/vaadin/flow/dom/ElementDetachListener.java @@ -15,12 +15,14 @@ */ package com.vaadin.flow.dom; +import java.io.Serializable; + /** * Listener for element detach events. It is invoked when the element is * detached from the UI. */ @FunctionalInterface -public interface ElementDetachListener { +public interface ElementDetachListener extends Serializable { /** * Invoked when an element is detached from the UI. * diff --git a/flow-server/src/main/java/com/vaadin/flow/dom/ThemeList.java b/flow-server/src/main/java/com/vaadin/flow/dom/ThemeList.java index 0bdbccf0292..04570a770d4 100644 --- a/flow-server/src/main/java/com/vaadin/flow/dom/ThemeList.java +++ b/flow-server/src/main/java/com/vaadin/flow/dom/ThemeList.java @@ -15,6 +15,7 @@ */ package com.vaadin.flow.dom; +import java.io.Serializable; import java.util.Set; /** @@ -22,7 +23,7 @@ * * @author Vaadin Ltd */ -public interface ThemeList extends Set { +public interface ThemeList extends Set, Serializable { /** * Sets or removes the given theme name, based on the {@code set} parameter. diff --git a/flow-server/src/main/java/com/vaadin/flow/dom/impl/ImmutableClassList.java b/flow-server/src/main/java/com/vaadin/flow/dom/impl/ImmutableClassList.java index 12a443318dc..de1cad833ee 100644 --- a/flow-server/src/main/java/com/vaadin/flow/dom/impl/ImmutableClassList.java +++ b/flow-server/src/main/java/com/vaadin/flow/dom/impl/ImmutableClassList.java @@ -15,6 +15,7 @@ */ package com.vaadin.flow.dom.impl; +import java.io.Serializable; import java.util.AbstractSet; import java.util.ArrayList; import java.util.Collection; @@ -29,7 +30,7 @@ * @author Vaadin Ltd */ public class ImmutableClassList extends AbstractSet - implements ClassList { + implements ClassList, Serializable { private static final String CANT_MODIFY_MESSAGE = ImmutableEmptyStyle.CANT_MODIFY_MESSAGE; diff --git a/flow-server/src/main/java/com/vaadin/flow/dom/impl/ThemeListImpl.java b/flow-server/src/main/java/com/vaadin/flow/dom/impl/ThemeListImpl.java index d07a4eb40b4..00bb56ed289 100644 --- a/flow-server/src/main/java/com/vaadin/flow/dom/impl/ThemeListImpl.java +++ b/flow-server/src/main/java/com/vaadin/flow/dom/impl/ThemeListImpl.java @@ -15,6 +15,7 @@ */ package com.vaadin.flow.dom.impl; +import java.io.Serializable; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; @@ -34,7 +35,7 @@ * * @author Vaadin Ltd. */ -public class ThemeListImpl implements ThemeList { +public class ThemeListImpl implements ThemeList, Serializable { public static final String THEME_ATTRIBUTE_NAME = "theme"; private static final String THEME_NAMES_DELIMITER = " "; diff --git a/flow-server/src/main/java/com/vaadin/flow/function/SerializablePredicate.java b/flow-server/src/main/java/com/vaadin/flow/function/SerializablePredicate.java index 18760da0001..6064a6f82e7 100644 --- a/flow-server/src/main/java/com/vaadin/flow/function/SerializablePredicate.java +++ b/flow-server/src/main/java/com/vaadin/flow/function/SerializablePredicate.java @@ -16,6 +16,7 @@ package com.vaadin.flow.function; import java.io.Serializable; +import java.util.Objects; import java.util.function.Predicate; /** @@ -28,5 +29,21 @@ * */ public interface SerializablePredicate extends Predicate, Serializable { - // Only method inherited from Predicate + + @Override + default SerializablePredicate and(Predicate other) { + Objects.requireNonNull(other); + return t -> test(t) && other.test(t); + } + + @Override + default SerializablePredicate negate() { + return t -> !test(t); + } + + @Override + default SerializablePredicate or(Predicate other) { + Objects.requireNonNull(other); + return t -> test(t) || other.test(t); + } } diff --git a/flow-server/src/main/java/com/vaadin/flow/i18n/I18NProvider.java b/flow-server/src/main/java/com/vaadin/flow/i18n/I18NProvider.java index e97b3d35a7d..b30ab4dfa68 100644 --- a/flow-server/src/main/java/com/vaadin/flow/i18n/I18NProvider.java +++ b/flow-server/src/main/java/com/vaadin/flow/i18n/I18NProvider.java @@ -15,13 +15,14 @@ */ package com.vaadin.flow.i18n; +import java.io.Serializable; import java.util.List; import java.util.Locale; /** * I18N provider interface for internationalization usage. */ -public interface I18NProvider { +public interface I18NProvider extends Serializable { /** * Get the locales that we have translations for. The first locale should be diff --git a/flow-server/src/main/java/com/vaadin/flow/i18n/LocaleChangeObserver.java b/flow-server/src/main/java/com/vaadin/flow/i18n/LocaleChangeObserver.java index bb05e864106..27c22365c3b 100644 --- a/flow-server/src/main/java/com/vaadin/flow/i18n/LocaleChangeObserver.java +++ b/flow-server/src/main/java/com/vaadin/flow/i18n/LocaleChangeObserver.java @@ -15,6 +15,8 @@ */ package com.vaadin.flow.i18n; +import java.io.Serializable; + /** * Any {@code com.vaadin.ui.Component} implementing this interface will be * informed when the UI locale is changed and on attach. @@ -22,7 +24,7 @@ * @author Vaadin Ltd */ @FunctionalInterface -public interface LocaleChangeObserver { +public interface LocaleChangeObserver extends Serializable { /** * Method called when the UI locale is changed. diff --git a/flow-server/src/main/java/com/vaadin/flow/internal/ReflectionCache.java b/flow-server/src/main/java/com/vaadin/flow/internal/ReflectionCache.java index 58b894e4681..8dcde6c0fd0 100644 --- a/flow-server/src/main/java/com/vaadin/flow/internal/ReflectionCache.java +++ b/flow-server/src/main/java/com/vaadin/flow/internal/ReflectionCache.java @@ -22,6 +22,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; +import com.vaadin.flow.function.SerializableFunction; + /** * A thread-safe cache for the result of doing some reflection lookup based on a * class. Cached values never expire since it's assumed that the there is a @@ -39,7 +41,7 @@ public class ReflectionCache { private final ConcurrentHashMap, T> values = new ConcurrentHashMap<>(); - private final Function, T> valueProvider; + private final SerializableFunction, T> valueProvider; /** * Creates a new reflection cache with the given value provider. The value @@ -52,7 +54,7 @@ public class ReflectionCache { * a function that computes the cached value for a class, not * null */ - public ReflectionCache(Function, T> valueProvider) { + public ReflectionCache(SerializableFunction, T> valueProvider) { if (valueProvider == null) { throw new IllegalArgumentException("value provider cannot be null"); } @@ -62,8 +64,8 @@ public ReflectionCache(Function, T> valueProvider) { } @SuppressWarnings({ "unchecked", "rawtypes" }) - private static Function, T> wrapValueProvider( - Function, T> valueProvider) { + private static SerializableFunction, T> wrapValueProvider( + SerializableFunction, T> valueProvider) { return type -> { Map, CurrentInstance> instances = CurrentInstance .getInstances(); diff --git a/flow-server/src/main/java/com/vaadin/flow/internal/StateTree.java b/flow-server/src/main/java/com/vaadin/flow/internal/StateTree.java index e20b7fb92e4..9b7cbafbcb8 100644 --- a/flow-server/src/main/java/com/vaadin/flow/internal/StateTree.java +++ b/flow-server/src/main/java/com/vaadin/flow/internal/StateTree.java @@ -16,6 +16,7 @@ package com.vaadin.flow.internal; +import java.io.Serializable; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -77,7 +78,7 @@ public boolean isAttached() { * @see StateTree#beforeClientResponse(StateNode, SerializableConsumer) * @see StateTree#runExecutionsBeforeClientResponse() */ - public static final class BeforeClientResponseEntry { + public static final class BeforeClientResponseEntry implements Serializable { private static final Comparator COMPARING_INDEX = Comparator .comparingInt(BeforeClientResponseEntry::getIndex); diff --git a/flow-server/src/main/java/com/vaadin/flow/internal/nodefeature/ElementListenerMap.java b/flow-server/src/main/java/com/vaadin/flow/internal/nodefeature/ElementListenerMap.java index 3387fdbbdb5..1f8482c4fd7 100644 --- a/flow-server/src/main/java/com/vaadin/flow/internal/nodefeature/ElementListenerMap.java +++ b/flow-server/src/main/java/com/vaadin/flow/internal/nodefeature/ElementListenerMap.java @@ -15,6 +15,7 @@ */ package com.vaadin.flow.internal.nodefeature; +import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -56,7 +57,7 @@ public class ElementListenerMap extends NodeMap { // Server-side only data private Map> listeners; - private static class ExpressionSettings { + private static class ExpressionSettings implements Serializable { private Map> debounceSettings = new HashMap<>(); public void addDebouncePhases(int timeout, Set phases) { diff --git a/flow-server/src/main/java/com/vaadin/flow/internal/nodefeature/NodeFeatureRegistry.java b/flow-server/src/main/java/com/vaadin/flow/internal/nodefeature/NodeFeatureRegistry.java index 843cb7a7084..2b2deb66199 100644 --- a/flow-server/src/main/java/com/vaadin/flow/internal/nodefeature/NodeFeatureRegistry.java +++ b/flow-server/src/main/java/com/vaadin/flow/internal/nodefeature/NodeFeatureRegistry.java @@ -15,12 +15,13 @@ */ package com.vaadin.flow.internal.nodefeature; +import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.function.Function; +import com.vaadin.flow.function.SerializableFunction; import com.vaadin.flow.internal.StateNode; import com.vaadin.flow.internal.nodefeature.PushConfigurationMap.PushConfigurationParametersMap; @@ -36,12 +37,12 @@ public class NodeFeatureRegistry { static final Map, NodeFeatureData> nodeFeatures = new HashMap<>(); private static final Map> idToFeature = new HashMap<>(); - private static class NodeFeatureData { - private final Function factory; + private static class NodeFeatureData implements Serializable { + private final SerializableFunction factory; private final int id; private NodeFeatureData( - Function factory) { + SerializableFunction factory) { this.factory = factory; id = nextNodeFeatureId++; } @@ -90,7 +91,7 @@ private NodeFeatureRegistry() { } private static void registerFeature(Class type, - Function factory) { + SerializableFunction factory) { NodeFeatureData featureData = new NodeFeatureData(factory); nodeFeatures.put(type, featureData); idToFeature.put(featureData.id, type); diff --git a/flow-server/src/main/java/com/vaadin/flow/router/HasDynamicTitle.java b/flow-server/src/main/java/com/vaadin/flow/router/HasDynamicTitle.java index 38f7b1f4c9a..39b08ae7230 100644 --- a/flow-server/src/main/java/com/vaadin/flow/router/HasDynamicTitle.java +++ b/flow-server/src/main/java/com/vaadin/flow/router/HasDynamicTitle.java @@ -16,6 +16,8 @@ package com.vaadin.flow.router; +import java.io.Serializable; + /** * Interface for navigation targets to resolve their title dynamically * at runtime. @@ -26,7 +28,7 @@ * @author Vaadin Ltd. */ @FunctionalInterface -public interface HasDynamicTitle { +public interface HasDynamicTitle extends Serializable { /** * Gets the title of this navigation target. diff --git a/flow-server/src/main/java/com/vaadin/flow/router/HasErrorParameter.java b/flow-server/src/main/java/com/vaadin/flow/router/HasErrorParameter.java index e635c55ba0c..7f683be844e 100644 --- a/flow-server/src/main/java/com/vaadin/flow/router/HasErrorParameter.java +++ b/flow-server/src/main/java/com/vaadin/flow/router/HasErrorParameter.java @@ -15,6 +15,8 @@ */ package com.vaadin.flow.router; +import java.io.Serializable; + /** * Interface for defining a view that handles the exceptions for the set * Exception type T. @@ -25,7 +27,7 @@ * @author Vaadin Ltd */ @FunctionalInterface -public interface HasErrorParameter { +public interface HasErrorParameter extends Serializable { /** * Method called before rendering the exception view. diff --git a/flow-server/src/main/java/com/vaadin/flow/router/HasUrlParameter.java b/flow-server/src/main/java/com/vaadin/flow/router/HasUrlParameter.java index 6524dbb152d..d065a1545a1 100644 --- a/flow-server/src/main/java/com/vaadin/flow/router/HasUrlParameter.java +++ b/flow-server/src/main/java/com/vaadin/flow/router/HasUrlParameter.java @@ -15,6 +15,8 @@ */ package com.vaadin.flow.router; +import java.io.Serializable; + /** * Interface for defining url parameters for navigation targets for use in * routing. @@ -25,7 +27,7 @@ * type parameter type */ @FunctionalInterface -public interface HasUrlParameter { +public interface HasUrlParameter extends Serializable { /** * Method that is called automatically when navigating to the target that diff --git a/flow-server/src/main/java/com/vaadin/flow/router/internal/AfterNavigationHandler.java b/flow-server/src/main/java/com/vaadin/flow/router/internal/AfterNavigationHandler.java index bb6830ba879..dfd5e22384b 100644 --- a/flow-server/src/main/java/com/vaadin/flow/router/internal/AfterNavigationHandler.java +++ b/flow-server/src/main/java/com/vaadin/flow/router/internal/AfterNavigationHandler.java @@ -15,13 +15,15 @@ */ package com.vaadin.flow.router.internal; +import java.io.Serializable; + import com.vaadin.flow.router.AfterNavigationEvent; /** * The base interface for every interface that handles {@link AfterNavigationEvent}. */ @FunctionalInterface -public interface AfterNavigationHandler { +public interface AfterNavigationHandler extends Serializable { /** * Method called after navigation has been executed. diff --git a/flow-server/src/main/java/com/vaadin/flow/router/internal/BeforeLeaveHandler.java b/flow-server/src/main/java/com/vaadin/flow/router/internal/BeforeLeaveHandler.java index 10be6a1a9e7..8140b5d5b19 100644 --- a/flow-server/src/main/java/com/vaadin/flow/router/internal/BeforeLeaveHandler.java +++ b/flow-server/src/main/java/com/vaadin/flow/router/internal/BeforeLeaveHandler.java @@ -15,13 +15,15 @@ */ package com.vaadin.flow.router.internal; +import java.io.Serializable; + import com.vaadin.flow.router.BeforeLeaveEvent; /** * The base interface for every interface that handles {@link BeforeLeaveEvent}. */ @FunctionalInterface -public interface BeforeLeaveHandler { +public interface BeforeLeaveHandler extends Serializable { /** * Method called before navigation to detaching Component chain is made. diff --git a/flow-server/src/main/java/com/vaadin/flow/router/internal/DefaultRouteResolver.java b/flow-server/src/main/java/com/vaadin/flow/router/internal/DefaultRouteResolver.java index fdca279eb09..4306081b4e6 100644 --- a/flow-server/src/main/java/com/vaadin/flow/router/internal/DefaultRouteResolver.java +++ b/flow-server/src/main/java/com/vaadin/flow/router/internal/DefaultRouteResolver.java @@ -15,6 +15,7 @@ */ package com.vaadin.flow.router.internal; +import java.io.Serializable; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Collections; @@ -81,7 +82,7 @@ public NavigationState resolve(ResolveRequest request) { return builder.build(); } - private static class PathDetails { + private static class PathDetails implements Serializable { private final String path; private final List segments; diff --git a/flow-server/src/main/java/com/vaadin/flow/server/InitialPageSettings.java b/flow-server/src/main/java/com/vaadin/flow/server/InitialPageSettings.java index 08b19dedc74..0cf1e2b6fd0 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/InitialPageSettings.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/InitialPageSettings.java @@ -15,6 +15,7 @@ */ package com.vaadin.flow.server; +import java.io.Serializable; import java.util.ArrayList; import java.util.EnumMap; import java.util.HashMap; @@ -35,7 +36,7 @@ /** * Initial page settings class for modifying the bootstrap page. */ -public class InitialPageSettings { +public class InitialPageSettings implements Serializable { /** * Append position enum. diff --git a/flow-server/src/main/java/com/vaadin/flow/server/PageConfigurator.java b/flow-server/src/main/java/com/vaadin/flow/server/PageConfigurator.java index 21793088a0c..53502b282a6 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/PageConfigurator.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/PageConfigurator.java @@ -15,11 +15,13 @@ */ package com.vaadin.flow.server; +import java.io.Serializable; + /** * Interface for configuring the initial page contents. */ @FunctionalInterface -public interface PageConfigurator { +public interface PageConfigurator extends Serializable { /** * Configure the initial page settings when called. diff --git a/flow-server/src/main/java/com/vaadin/flow/server/communication/rpc/RpcDecoder.java b/flow-server/src/main/java/com/vaadin/flow/server/communication/rpc/RpcDecoder.java index 970702066e7..0d76bf8b6d1 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/communication/rpc/RpcDecoder.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/communication/rpc/RpcDecoder.java @@ -15,6 +15,8 @@ */ package com.vaadin.flow.server.communication.rpc; +import java.io.Serializable; + import elemental.json.JsonValue; /** @@ -33,7 +35,7 @@ * @author Vaadin Ltd * */ -public interface RpcDecoder { +public interface RpcDecoder extends Serializable { /** * Returns {@code true} if the decoder is applicable for the given diff --git a/flow-server/src/main/java/com/vaadin/flow/server/communication/rpc/RpcInvocationHandler.java b/flow-server/src/main/java/com/vaadin/flow/server/communication/rpc/RpcInvocationHandler.java index 66a6743da95..8b4de9fa4d9 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/communication/rpc/RpcInvocationHandler.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/communication/rpc/RpcInvocationHandler.java @@ -15,6 +15,7 @@ */ package com.vaadin.flow.server.communication.rpc; +import java.io.Serializable; import java.util.Optional; import com.vaadin.flow.component.UI; @@ -31,7 +32,7 @@ * @author Vaadin Ltd * */ -public interface RpcInvocationHandler { +public interface RpcInvocationHandler extends Serializable { /** * Gets unique RPC type which this handler is applicable for. diff --git a/flow-server/src/main/java/com/vaadin/flow/server/startup/AbstractAnnotationValidator.java b/flow-server/src/main/java/com/vaadin/flow/server/startup/AbstractAnnotationValidator.java index 8798a040e93..613dc6033a1 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/startup/AbstractAnnotationValidator.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/startup/AbstractAnnotationValidator.java @@ -15,6 +15,7 @@ */ package com.vaadin.flow.server.startup; +import java.io.Serializable; import java.lang.annotation.Annotation; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -34,7 +35,7 @@ * Validation class that contains common logic to checks that specific * annotations are not configured wrong. */ -public abstract class AbstractAnnotationValidator { +public abstract class AbstractAnnotationValidator implements Serializable { public static final String ERROR_MESSAGE_BEGINNING = "Found configuration annotations that will not be used in the application. \n" + "Move the following annotations to a single route or the top RouterLayout of the application: \n"; diff --git a/flow-server/src/main/java/com/vaadin/flow/server/startup/AbstractRouteRegistryInitializer.java b/flow-server/src/main/java/com/vaadin/flow/server/startup/AbstractRouteRegistryInitializer.java index 2492ccf9680..7c89f614f58 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/startup/AbstractRouteRegistryInitializer.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/startup/AbstractRouteRegistryInitializer.java @@ -15,6 +15,7 @@ */ package com.vaadin.flow.server.startup; +import java.io.Serializable; import java.lang.annotation.Annotation; import java.util.List; import java.util.Set; @@ -42,7 +43,7 @@ * @author Vaadin Ltd * */ -public abstract class AbstractRouteRegistryInitializer { +public abstract class AbstractRouteRegistryInitializer implements Serializable { /** * Validate the potential route classes stream and return them as a set. diff --git a/flow-server/src/main/java/com/vaadin/flow/server/startup/CustomElements.java b/flow-server/src/main/java/com/vaadin/flow/server/startup/CustomElements.java index 2f66e4a1fcd..2a9f8a16027 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/startup/CustomElements.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/startup/CustomElements.java @@ -16,6 +16,7 @@ package com.vaadin.flow.server.startup; +import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -35,7 +36,7 @@ * * @author Vaadin Ltd. */ -class CustomElements { +class CustomElements implements Serializable { private final Map>> elements = new HashMap<>(); private static Optional validateComponentClasses(String tagName, diff --git a/flow-server/src/main/java/com/vaadin/flow/server/startup/RouteRegistry.java b/flow-server/src/main/java/com/vaadin/flow/server/startup/RouteRegistry.java index c7309ca4331..2e66f2c14d5 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/startup/RouteRegistry.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/startup/RouteRegistry.java @@ -67,7 +67,7 @@ public class RouteRegistry implements Serializable { * A pair of a navigation target for handling exceptions and the exception * type handled by the navigation target. */ - public static class ErrorTargetEntry { + public static class ErrorTargetEntry implements Serializable { private final Class navigationTarget; private final Class handledExceptionType; diff --git a/flow-server/src/main/java/com/vaadin/flow/shared/VaadinUriResolver.java b/flow-server/src/main/java/com/vaadin/flow/shared/VaadinUriResolver.java index 55ab74a5865..8531c12a00a 100644 --- a/flow-server/src/main/java/com/vaadin/flow/shared/VaadinUriResolver.java +++ b/flow-server/src/main/java/com/vaadin/flow/shared/VaadinUriResolver.java @@ -28,7 +28,7 @@ * @since 7.4 * @author Vaadin Ltd */ -public abstract class VaadinUriResolver { +public abstract class VaadinUriResolver implements Serializable{ /** * Translates a Vaadin URI to a URL that can be loaded by the browser. The diff --git a/flow-server/src/main/java/com/vaadin/flow/templatemodel/PropertyFilter.java b/flow-server/src/main/java/com/vaadin/flow/templatemodel/PropertyFilter.java index 7e313396f22..e440a10956e 100644 --- a/flow-server/src/main/java/com/vaadin/flow/templatemodel/PropertyFilter.java +++ b/flow-server/src/main/java/com/vaadin/flow/templatemodel/PropertyFilter.java @@ -56,7 +56,7 @@ public PropertyFilter(Predicate predicate) { * a predicate matching property names in the inner scope */ public PropertyFilter(PropertyFilter outerFilter, String scopeName, - Predicate predicate) { + Predicate predicate) { this(composePrefix(outerFilter, scopeName), predicate.and(composeFilter(outerFilter, scopeName))); } diff --git a/flow-server/src/main/java/com/vaadin/flow/templatemodel/TemplateModel.java b/flow-server/src/main/java/com/vaadin/flow/templatemodel/TemplateModel.java index 3f359442afa..e5c161ef195 100644 --- a/flow-server/src/main/java/com/vaadin/flow/templatemodel/TemplateModel.java +++ b/flow-server/src/main/java/com/vaadin/flow/templatemodel/TemplateModel.java @@ -156,11 +156,10 @@ default List getListProxy(String modelPath, Class beanType) { * the to import * @param propertyNameFilter * the filter to apply to the bean's properties - * @see #importBean(String, Object, Predicate) * @see TemplateModel supported property types */ default void importBean(String modelPath, Object bean, - Predicate propertyNameFilter) { + Predicate propertyNameFilter) { TemplateModelUtil.resolveBeanAndRun(this, modelPath, (type, map) -> { type.importProperties(map, bean, new PropertyFilter(propertyNameFilter)); diff --git a/flow-server/src/main/java/com/vaadin/flow/templatemodel/TemplateModelListProxy.java b/flow-server/src/main/java/com/vaadin/flow/templatemodel/TemplateModelListProxy.java index ce7276ebd24..1d807b9544c 100644 --- a/flow-server/src/main/java/com/vaadin/flow/templatemodel/TemplateModelListProxy.java +++ b/flow-server/src/main/java/com/vaadin/flow/templatemodel/TemplateModelListProxy.java @@ -15,6 +15,7 @@ */ package com.vaadin.flow.templatemodel; +import java.io.Serializable; import java.util.AbstractList; import com.vaadin.flow.internal.StateNode; @@ -29,7 +30,7 @@ * @param * the type of items in the list */ -public class TemplateModelListProxy extends AbstractList { +public class TemplateModelListProxy extends AbstractList implements Serializable { private final StateNode stateNode; private final ComplexModelType itemType; diff --git a/flow-server/src/main/java/com/vaadin/flow/templatemodel/TemplateModelProxyHandler.java b/flow-server/src/main/java/com/vaadin/flow/templatemodel/TemplateModelProxyHandler.java index 872aa4a7e48..081b04062b7 100644 --- a/flow-server/src/main/java/com/vaadin/flow/templatemodel/TemplateModelProxyHandler.java +++ b/flow-server/src/main/java/com/vaadin/flow/templatemodel/TemplateModelProxyHandler.java @@ -50,7 +50,7 @@ public class TemplateModelProxyHandler implements Serializable { /** * Gives access to the state node of a proxy instance. */ - protected interface ModelProxy { + protected interface ModelProxy extends Serializable { /** * Gets the state node that this instance is backed by. * diff --git a/flow-server/src/test/java/com/vaadin/packaging/SplitPackagesTest.java b/flow-server/src/test/java/com/vaadin/packaging/SplitPackagesTest.java index 1dd75045b62..278d1d62112 100644 --- a/flow-server/src/test/java/com/vaadin/packaging/SplitPackagesTest.java +++ b/flow-server/src/test/java/com/vaadin/packaging/SplitPackagesTest.java @@ -35,7 +35,7 @@ public class SplitPackagesTest { * Modules that contain known split packages that we don't care about */ private static final Set ignoredModules = new HashSet<>( - Arrays.asList("demo-flow-components", "flow-tests", + Arrays.asList("demo-flow-components", "flow-tests", "flow-test-generic", "flow-code-generator", "flow-generated-components")); /* diff --git a/flow-server/src/test/java/com/vaadin/tests/server/component/FlowClassesSerializableTest.java b/flow-server/src/test/java/com/vaadin/tests/server/component/FlowClassesSerializableTest.java new file mode 100644 index 00000000000..284bfec9ac4 --- /dev/null +++ b/flow-server/src/test/java/com/vaadin/tests/server/component/FlowClassesSerializableTest.java @@ -0,0 +1,7 @@ +package com.vaadin.tests.server.component; + +import com.vaadin.flow.testutil.ClassesSerializableTest; + +public class FlowClassesSerializableTest extends ClassesSerializableTest +{ +} diff --git a/flow-test-generic/pom.xml b/flow-test-generic/pom.xml new file mode 100644 index 00000000000..e44b852a0a2 --- /dev/null +++ b/flow-test-generic/pom.xml @@ -0,0 +1,26 @@ + + + + flow-project + com.vaadin + 1.0-SNAPSHOT + + 4.0.0 + + flow-test-generic + Flow Generic Test Utilities + + + junit + junit + + + org.slf4j + slf4j-api + + + + + diff --git a/flow-test-generic/src/main/java/com/vaadin/flow/testutil/ClassesSerializableTest.java b/flow-test-generic/src/main/java/com/vaadin/flow/testutil/ClassesSerializableTest.java new file mode 100644 index 00000000000..945e0ddcea7 --- /dev/null +++ b/flow-test-generic/src/main/java/com/vaadin/flow/testutil/ClassesSerializableTest.java @@ -0,0 +1,453 @@ +/* + * Copyright 2000-2017 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.testutil; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Optional; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static java.lang.reflect.Modifier.isStatic; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +/** + * A superclass for serialization testing. The test scans all the classpath and + * tries to serialize every single class (except ones from whitelist) in the + * classpath. Subclasses may adjust the whitelist by overriding + * {@link #getExcludedPatterns()}, {@link #getBasePackages()}, + * {@link #getJarPattern()} + */ + +public abstract class ClassesSerializableTest { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @SuppressWarnings("WeakerAccess") + protected Stream getExcludedPatterns() { + return Stream.of( + "com\\.vaadin\\.flow\\.data\\.validator\\.BeanValidator\\$LazyFactoryInitializer", + "com\\.vaadin\\.flow\\.internal\\.BeanUtil\\$LazyValidationAvailability", + ".*\\.slf4j\\..*", + ".*\\.testbench\\..*", + ".*\\.testutil\\..*", + ".*\\.demo\\..*", + "com\\.vaadin\\..*Util(s)?(\\$\\w+)?$", //Various utils with inner classes + + "com\\.vaadin\\.flow\\.data\\.provider\\.InMemoryDataProviderHelpers", + "com\\.vaadin\\.flow\\.dom\\.ElementConstants", + "com\\.vaadin\\.flow\\.component\\.board\\.internal\\.FunctionCaller", + "com\\.vaadin\\.flow\\.component\\.grid\\.ColumnGroupHelpers", + "com\\.vaadin\\.flow\\.component\\.textfield\\.SlotHelpers", + "com\\.vaadin\\.flow\\.component\\.orderedlayout\\.FlexConstants", + "com\\.vaadin\\.flow\\.component\\.polymertemplate\\.DefaultTemplateParser", + "com\\.vaadin\\.flow\\.component\\.PropertyDescriptors(\\$.*)?", + "com\\.vaadin\\.flow\\.internal\\.JsonSerializer", + "com\\.vaadin\\.flow\\.internal\\.JsonCodec", + "com\\.vaadin\\.flow\\.internal\\.UsageStatistics(\\$.*)?", + "com\\.vaadin\\.flow\\.internal\\.nodefeature\\.NodeFeatureRegistry", + "com\\.vaadin\\.flow\\.internal\\.nodefeature\\.NodeFeatures", + "com\\.vaadin\\.flow\\.internal\\.CustomElementNameValidator", + "com\\.vaadin\\.flow\\.router\\.HighlightActions", + "com\\.vaadin\\.flow\\.router\\.HighlightConditions", + "com\\.vaadin\\.flow\\.router\\.ParameterDeserializer", + "com\\.vaadin\\.flow\\.router\\.NavigationStateBuilder", + "com\\.vaadin\\.flow\\.router\\.RouteNotFoundError$LazyInit", + "com\\.vaadin\\.flow\\.internal\\.JavaScriptSemantics", + "com\\.vaadin\\.flow\\.internal\\.nodefeature\\.NodeProperties", + "com\\.vaadin\\.flow\\.internal\\.AnnotationReader", + "com\\.vaadin\\.flow\\.server\\.communication\\.ServerRpcHandler\\$LazyInvocationHandlers", + "com\\.vaadin\\.flow\\.server\\.VaadinServletRequest", + "com\\.vaadin\\.flow\\.server\\.VaadinServletResponse", + "com\\.vaadin\\.flow\\.server\\.startup\\.AnnotationValidator", + "com\\.vaadin\\.flow\\.server\\.startup\\.ServletDeployer", + "com\\.vaadin\\.flow\\.server\\.communication.JSR356WebsocketInitializer(\\$.*)?", + "com\\.vaadin\\.flow\\.server\\.BootstrapHandler(\\$.*)?", + "com\\.vaadin\\.flow\\.server\\.BootstrapPageResponse", + "com\\.vaadin\\.flow\\.server\\.InlineTargets", + "com\\.vaadin\\.flow\\.server\\.communication\\.PushHandler(\\$.*)?", + "com\\.vaadin\\.flow\\.server\\.communication\\.PushRequestHandler(\\$.*)?", + "com\\.vaadin\\.flow\\.templatemodel\\.PathLookup", + "com\\.vaadin\\.flow\\.server\\.startup\\.ErrorNavigationTargetInitializer", + "com\\.vaadin\\.flow\\.server\\.startup\\.ServletVerifier", + "com\\.vaadin\\.flow\\.server\\.startup\\.RouteRegistryInitializer", + "com\\.vaadin\\.flow\\.server\\.VaadinResponse", + "com\\.vaadin\\.flow\\.component\\.Key", + "com\\.vaadin\\.flow\\.server\\.VaadinRequest", + "com\\.vaadin\\.flow\\.router\\.RouteNotFoundError\\$LazyInit", + "com\\.vaadin\\.flow\\.component\\.polymertemplate\\.TemplateDataAnalyzer\\$.*", + "com\\.vaadin\\.flow\\.component\\.HtmlComponent",// De-facto abstract class + "com\\.vaadin\\.flow\\.component\\.HtmlContainer",// De-facto abstract class + "com\\.vaadin\\.flow\\.component\\.polymertemplate\\.TemplateInitializer(\\$.*)?", + "com\\.vaadin\\.flow\\.component\\.polymertemplate\\.TemplateParser(\\$.*)?", + "com\\.vaadin\\.flow\\.dom\\.impl\\.ThemeListImpl\\$ThemeListIterator", + "com\\.vaadin\\.flow\\.templatemodel\\.PropertyMapBuilder(\\$.*)?", + "com\\.vaadin\\.flow\\.internal\\.ReflectionCache", + "com\\.vaadin\\.flow\\.component\\.internal\\.ComponentMetaData(\\$.*)?", + "com\\.vaadin\\.flow\\.component\\.polymertemplate\\.TemplateDataAnalyzer", + "com\\.vaadin\\.flow\\.dom\\.ElementFactory", + "com\\.vaadin\\.flow\\.dom\\.NodeVisitor", + "com\\.vaadin\\.flow\\.internal\\.nodefeature\\.NodeList(\\$.*)?", + "com\\.vaadin\\.flow\\.templatemodel\\.PropertyFilter", + "com\\.vaadin\\.flow\\.internal\\.ReflectTools(\\$.*)?", + "com\\.vaadin\\.flow\\.server\\.FutureAccess", + "com\\.vaadin\\.flow\\.internal\\.nodefeature\\.ElementPropertyMap\\$PutResult", + + //Various test classes + ".*\\.test(s)?\\..*", + ".*Test.*", + "com\\.vaadin\\.flow\\.server\\.MockVaadinServletService", + "com\\.vaadin\\.flow\\.server\\.MockServletServiceSessionSetup", + "com\\.vaadin\\.flow\\.server\\.MockServletConfig", + "com\\.vaadin\\.flow\\.server\\.MockServletContext", + "com\\.vaadin\\.flow\\.templatemodel\\.Bean", + "com\\.vaadin\\.flow\\.internal\\.HasCurrentService", + "com\\.vaadin\\.flow\\.component\\.ValueChangeMonitor", + "com\\.vaadin\\.flow\\.templatemodel\\.BeanContainingBeans(\\$.*)?"); + } + + /** + * Performs actual serialization/deserialization + * + * @param instance the instance + * @return the copy of the source object + * @throws Throwable if something goes wrong. + */ + @SuppressWarnings({"UnusedReturnValue", "WeakerAccess"}) + public T serializeAndDeserialize(T instance) + throws Throwable { + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(bs); + out.writeObject(instance); + byte[] data = bs.toByteArray(); + ObjectInputStream in = new ObjectInputStream( + new ByteArrayInputStream(data)); + + @SuppressWarnings("unchecked") + T readObject = (T) in.readObject(); + + return readObject; + } + + /** + * The method is called right after a class instantiation and might be + * overriden by subclasses to reset thread local values (ex. current UI). + * @see #setupThreadLocals + */ + @SuppressWarnings("WeakerAccess") + protected void resetThreadLocals() { + } + + /** + * The method is called right a class instantiation + * and might be overriden by subclasses to install some necessary thread + * local values (ex. current UI). + * @see #resetThreadLocals + */ + @SuppressWarnings("WeakerAccess") + protected void setupThreadLocals() { + } + + private static boolean isFunctionalType(Type type) { + return type.getTypeName().contains("java.util.function"); + } + + /** + * Lists all class path entries by splitting the class path string. + *

+ * Adapted from ClassPathExplorer.getRawClasspathEntries(), but without + * filtering. + * + * @return List of class path segment strings + */ + private static List getRawClasspathEntries() { + // try to keep the order of the classpath + + String pathSep = System.getProperty("path.separator"); + String classpath = System.getProperty("java.class.path"); + + if (classpath.startsWith("\"")) { + classpath = classpath.substring(1); + } + if (classpath.endsWith("\"")) { + classpath = classpath.substring(0, classpath.length() - 1); + } + + String[] split = classpath.split(pathSep); + return Arrays.asList(split); + } + + /** + * Lists class names (based on .class files) in a directory (a package path + * root). + * + * @param parentPackage parent package name or null at root of hierarchy, used by + * recursion + * @param parent File representing the directory to scan + * @return collection of fully qualified class names in the directory + */ + private static Collection findClassesInDirectory( + String parentPackage, File parent) { + if (parent.isHidden() + || parent.getPath().contains(File.separator + ".")) { + return Collections.emptyList(); + } + + if (parentPackage == null) { + parentPackage = ""; + } else { + parentPackage += "."; + } + + Collection classNames = new ArrayList<>(); + + // add all directories recursively + File[] files = parent.listFiles(); + assertNotNull(files); + for (File child : files) { + if (child.isDirectory()) { + classNames.addAll(findClassesInDirectory( + parentPackage + child.getName(), child)); + } else if (child.getName().endsWith(".class")) { + classNames.add(parentPackage.replace(File.separatorChar, '.') + + child.getName().replaceAll("\\.class", "")); + } + } + + return classNames; + } + + /** + * JARs that will be scanned for classes to test, in addition to classpath + * directories. + */ + @SuppressWarnings("WeakerAccess") + protected Pattern getJarPattern() { + return Pattern.compile("(.*vaadin.*)|(.*flow.*)\\.jar"); + } + + @SuppressWarnings("WeakerAccess") + protected Stream getBasePackages() { + return Stream.of("com.vaadin"); + } + + /** + * Tests that all the relevant classes and interfaces under + * {@link #getBasePackages} implement Serializable. + * + * @throws Throwable serialization goes wrong + */ + @Test + public void classesSerializable() throws Throwable { + List rawClasspathEntries = getRawClasspathEntries(); + + List classes = new ArrayList<>(); + List excludes = getExcludedPatterns().map(Pattern::compile).collect(Collectors.toList()); + for (String location : rawClasspathEntries) { + classes.addAll(findServerClasses(location, excludes)); + } + + ArrayList nonSerializableFunctionFields = new ArrayList<>(); + + List> nonSerializableClasses = new ArrayList<>(); + for (String className : classes) { + Class cls = Class.forName(className); + // Don't add classes that have a @Ignore annotation on the class + if (isTestClass(cls)) { + continue; + } + + // report fields that use lambda types that won't be serializable + // (also in synthetic classes) + Stream.of(cls.getDeclaredFields()) + .filter(field -> isFunctionalType(field.getGenericType())) + .filter(field -> !isStatic(field.getModifiers())) + .forEach(nonSerializableFunctionFields::add); + + // skip annotations and synthetic classes + if (cls.isAnnotation() || cls.isSynthetic()) { + continue; + } + + if (!cls.isInterface() + && !Modifier.isAbstract(cls.getModifiers())) { + serializeAndDeserialize(cls); + } + + // report non-serializable classes and interfaces + if (!Serializable.class.isAssignableFrom(cls)) { + nonSerializableClasses.add(cls); + // TODO easier to read when testing + // System.err.println(cls); + } + } + + // useful failure message including all non-serializable classes and + // interfaces + if (!nonSerializableClasses.isEmpty()) { + failSerializableClasses(nonSerializableClasses); + } + + if (!nonSerializableFunctionFields.isEmpty()) { + failSerializableFields(nonSerializableFunctionFields); + } + } + + private void serializeAndDeserialize(Class clazz) { + try { + Optional> defaultCtor = Stream + .of(clazz.getDeclaredConstructors()) + .filter(ctor -> ctor.getParameterCount() == 0).findFirst(); + if (!defaultCtor.isPresent()) { + return; + } + defaultCtor.get().setAccessible(true); + setupThreadLocals(); + Object instance; + try { + instance = defaultCtor.get().newInstance(); + } finally { + resetThreadLocals(); + } + serializeAndDeserialize(instance); + } catch (Throwable e) { + throw new AssertionError(clazz.getName(), e); + } + } + + private void failSerializableFields( + List nonSerializableFunctionFields) { + String nonSerializableString = nonSerializableFunctionFields.stream() + .map(field -> String.format("%s.%s", + field.getDeclaringClass().getName(), field.getName())) + .collect(Collectors.joining(", ")); + + fail("Fields with functional types that are not serializable: " + + nonSerializableString); + } + + private void failSerializableClasses( + List> nonSerializableClasses) { + StringBuilder nonSerializableString = new StringBuilder(); + for (Class c : nonSerializableClasses) { + nonSerializableString.append(",\n").append(c.getName()); + if (c.isAnonymousClass()) { + nonSerializableString.append("(super: ") + .append(c.getSuperclass().getName()) + .append(", interfaces: "); + for (Class i : c.getInterfaces()) { + nonSerializableString.append(i.getName()).append(","); + } + nonSerializableString.append(")"); + } + } + fail("Serializable not implemented by the following classes and interfaces: " + + nonSerializableString); + + } + + private boolean isTestClass(Class cls) { + if (cls.getEnclosingClass() != null + && isTestClass(cls.getEnclosingClass())) { + return true; + } + + // Test classes with a @Test annotation on some method + for (Method method : cls.getMethods()) { + if (method.isAnnotationPresent(Test.class)) { + return true; + } + } + + return false; + } + + /** + * Finds the server side classes/interfaces under a class path entry - + * either a directory or a JAR that matches {@link #getJarPattern()}. + *

+ * Only classes under {@link #getBasePackages} are considered, and those + * matching {@link #getExcludedPatterns()} are filtered out. + */ + private List findServerClasses(String classpathEntry, Collection excludes) + throws IOException { + Collection classes; + + File file = new File(classpathEntry); + if (file.isDirectory()) { + classes = findClassesInDirectory(null, file); + } else if (getJarPattern().matcher(file.getName()).matches()) { + classes = findClassesInJar(file); + } else { + logger.debug("Ignoring " + classpathEntry); + return Collections.emptyList(); + } + return classes.stream() + .filter(className -> getBasePackages().anyMatch(basePackage -> className.startsWith(basePackage + "."))) + .filter(className -> excludes.stream().noneMatch(p -> p.matcher(className).matches())) + .collect(Collectors.toList()); + } + + /** + * Lists class names (based on .class files) in a JAR file. + * + * @param file a valid JAR file + * @return collection of fully qualified class names in the JAR + */ + private Collection findClassesInJar(File file) throws IOException { + Collection classes = new ArrayList<>(); + + try (JarFile jar = new JarFile(file)) { + Enumeration e = jar.entries(); + while (e.hasMoreElements()) { + JarEntry entry = e.nextElement(); + if (entry.getName().endsWith(".class")) { + String nameWithoutExtension = entry.getName() + .replaceAll("\\.class", ""); + String className = nameWithoutExtension.replace('/', '.'); + classes.add(className); + } + } + } + return classes; + } + +} diff --git a/pom.xml b/pom.xml index d7ed1e598a1..5530fe3a10f 100644 --- a/pom.xml +++ b/pom.xml @@ -23,6 +23,7 @@ flow-server-production-mode flow-components-parent flow-maven-plugin + flow-test-generic build-tools