From 87289d81ced8c9746a74e7a0d204ba3f7bae2e4a Mon Sep 17 00:00:00 2001 From: Marco Collovati Date: Thu, 29 Jun 2023 10:02:04 +0200 Subject: [PATCH] fix: fix serialization issues (#442) Fixes #436 Fixes #437 --- pom.xml | 8 +- vaadin-cdi/pom.xml | 2 +- .../vaadin/cdi/CdiVaadinServletService.java | 28 ++++--- .../cdi/context/RouteScopedContext.java | 2 +- .../cdi/CdiVaadinServletServiceTest.java | 73 ++++++++++++++----- .../com/vaadin/cdi/SerializationUtils.java | 44 +++++++++++ .../cdi/context/RouteContextNormalTest.java | 27 ++++++- .../cdi/context/RouteContextPseudoTest.java | 27 ++++++- 8 files changed, 176 insertions(+), 35 deletions(-) create mode 100644 vaadin-cdi/src/test/java/com/vaadin/cdi/SerializationUtils.java diff --git a/pom.xml b/pom.xml index 2a9212ec..e2e2f17e 100644 --- a/pom.xml +++ b/pom.xml @@ -46,14 +46,14 @@ 2023 - 9.0-SNAPSHOT + 9.1-SNAPSHOT 1.7.25 1.8.1 1.7.5 2.4.6.Final 1.1.15.Final - + @@ -190,8 +190,8 @@ org.mockito - mockito-core - 1.10.19 + mockito-inline + 4.11.0 org.jboss.arquillian diff --git a/vaadin-cdi/pom.xml b/vaadin-cdi/pom.xml index 3efe918c..e82807dc 100644 --- a/vaadin-cdi/pom.xml +++ b/vaadin-cdi/pom.xml @@ -71,7 +71,7 @@ org.mockito - mockito-core + mockito-inline test diff --git a/vaadin-cdi/src/main/java/com/vaadin/cdi/CdiVaadinServletService.java b/vaadin-cdi/src/main/java/com/vaadin/cdi/CdiVaadinServletService.java index e1a63baa..3a9e8f6f 100644 --- a/vaadin-cdi/src/main/java/com/vaadin/cdi/CdiVaadinServletService.java +++ b/vaadin-cdi/src/main/java/com/vaadin/cdi/CdiVaadinServletService.java @@ -32,6 +32,7 @@ import com.vaadin.flow.server.VaadinService; import com.vaadin.flow.server.VaadinServletService; import com.vaadin.flow.server.VaadinSession; +import com.vaadin.flow.server.WrappedSession; import javax.enterprise.context.spi.Context; import javax.enterprise.context.spi.CreationalContext; @@ -74,12 +75,12 @@ public CdiVaadinServletService(CdiVaadinServlet servlet, DeploymentConfiguration configuration, BeanManager beanManager) { super(servlet, configuration); - this.delegate = new CdiVaadinServiceDelegate(this, beanManager); + this.delegate = new CdiVaadinServiceDelegate(beanManager); } @Override public void init() throws ServiceException { - delegate.init(); + delegate.init(this); super.init(); } @@ -89,6 +90,20 @@ public void fireUIInitListeners(UI ui) { super.fireUIInitListeners(ui); } + @Override + protected VaadinSession loadSession(WrappedSession wrappedSession) { + return super.loadSession(wrappedSession); + } + + @Override + protected void storeSession(VaadinSession session, WrappedSession wrappedSession) { + super.storeSession(session, wrappedSession); + } + + private void restoreDelegate(VaadinSession session) { + + } + public Optional loadInstantiators() throws ServiceException { BeanManager beanManager = delegate.getBeanManager(); @@ -139,21 +154,16 @@ public CdiVaadinServlet getServlet() { */ public static class CdiVaadinServiceDelegate implements Serializable { - private final VaadinService vaadinService; - private transient BeanManager beanManager; private final UIEventListener uiEventListener; - public CdiVaadinServiceDelegate(VaadinService vaadinService, - BeanManager beanManager) { + public CdiVaadinServiceDelegate(BeanManager beanManager) { this.beanManager = beanManager; - this.vaadinService = vaadinService; - uiEventListener = new UIEventListener(this); } - public void init() throws ServiceException { + public void init(VaadinService vaadinService) throws ServiceException { lookup(SystemMessagesProvider.class) .ifPresent(vaadinService::setSystemMessagesProvider); vaadinService.addUIInitListener(e -> getBeanManager().fireEvent(e)); diff --git a/vaadin-cdi/src/main/java/com/vaadin/cdi/context/RouteScopedContext.java b/vaadin-cdi/src/main/java/com/vaadin/cdi/context/RouteScopedContext.java index eea18e3d..ccf36e95 100644 --- a/vaadin-cdi/src/main/java/com/vaadin/cdi/context/RouteScopedContext.java +++ b/vaadin-cdi/src/main/java/com/vaadin/cdi/context/RouteScopedContext.java @@ -204,7 +204,7 @@ String getUIId() { } - static class NavigationData { + static class NavigationData implements Serializable { private final Class navigationTarget; private final List> layouts; diff --git a/vaadin-cdi/src/test/java/com/vaadin/cdi/CdiVaadinServletServiceTest.java b/vaadin-cdi/src/test/java/com/vaadin/cdi/CdiVaadinServletServiceTest.java index a0bc2c29..846935c0 100644 --- a/vaadin-cdi/src/test/java/com/vaadin/cdi/CdiVaadinServletServiceTest.java +++ b/vaadin-cdi/src/test/java/com/vaadin/cdi/CdiVaadinServletServiceTest.java @@ -8,39 +8,48 @@ */ package com.vaadin.cdi; -import com.vaadin.cdi.annotation.VaadinServiceEnabled; -import com.vaadin.cdi.annotation.VaadinServiceScoped; -import com.vaadin.cdi.context.ServiceUnderTestContext; -import com.vaadin.flow.di.Instantiator; -import com.vaadin.flow.server.CustomizedSystemMessages; -import com.vaadin.flow.server.DefaultSystemMessagesProvider; -import com.vaadin.flow.server.ServiceException; -import com.vaadin.flow.server.SystemMessages; -import com.vaadin.flow.server.SystemMessagesInfo; -import com.vaadin.flow.server.SystemMessagesProvider; - -import org.apache.deltaspike.testcontrol.api.junit.CdiTestRunner; -import org.junit.After; -import org.junit.Test; -import org.junit.runner.RunWith; - import javax.enterprise.context.spi.Context; import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.event.Observes; import javax.enterprise.inject.AmbiguousResolutionException; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.inject.Inject; +import javax.inject.Singleton; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Optional; import java.util.Set; +import org.apache.deltaspike.testcontrol.api.junit.CdiTestRunner; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; + +import com.vaadin.cdi.annotation.VaadinServiceEnabled; +import com.vaadin.cdi.annotation.VaadinServiceScoped; +import com.vaadin.cdi.context.ServiceUnderTestContext; +import com.vaadin.flow.component.ComponentUtil; +import com.vaadin.flow.component.PollEvent; +import com.vaadin.flow.component.UI; +import com.vaadin.flow.di.Instantiator; +import com.vaadin.flow.server.CustomizedSystemMessages; +import com.vaadin.flow.server.DefaultSystemMessagesProvider; +import com.vaadin.flow.server.ServiceException; +import com.vaadin.flow.server.SystemMessages; +import com.vaadin.flow.server.SystemMessagesInfo; +import com.vaadin.flow.server.SystemMessagesProvider; +import com.vaadin.flow.server.VaadinSession; + +import static com.vaadin.cdi.SerializationUtils.serializeAndDeserialize; import static org.hamcrest.CoreMatchers.instanceOf; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.same; @@ -64,6 +73,15 @@ public SystemMessages getSystemMessages( } + @Singleton + public static class UIListenerEventReceiver { + + private UI pollEventUI; + void onPollEvent(@Observes PollEvent pollEvent) { + pollEventUI = pollEvent.getSource(); + } + } + @Inject private BeanManager beanManager; @@ -174,6 +192,25 @@ public void loadInstantiators_serviceInitialized_instantiatorInstanceCreated() assertEquals(mockInstantiator, maybeInstantiator.get()); } + @Test + public void fireUIInitListeners_serialization_UIserializableAndListenersWork() throws Exception { + initService(beanManager); + + UIListenerEventReceiver uiListenerEventReceiver = service.getInstantiator().getOrCreate(UIListenerEventReceiver.class); + UI ui = new UI(); + ui.getInternals().setSession(Mockito.mock(VaadinSession.class, Mockito.withSettings().serializable())); + service.fireUIInitListeners(ui); + + ComponentUtil.fireEvent(ui, new PollEvent(ui, false)); + assertEquals(ui, uiListenerEventReceiver.pollEventUI); + + UI ui2 = serializeAndDeserialize(ui); + assertNotNull(ui2); + + ComponentUtil.fireEvent(ui2, new PollEvent(ui2, false)); + assertEquals(ui2, uiListenerEventReceiver.pollEventUI); + } + private void initService(BeanManager beanManager) throws ServiceException { ServiceUnderTestContext serviceUnderTestContext = new ServiceUnderTestContext(beanManager); serviceUnderTestContext.activate(); diff --git a/vaadin-cdi/src/test/java/com/vaadin/cdi/SerializationUtils.java b/vaadin-cdi/src/test/java/com/vaadin/cdi/SerializationUtils.java new file mode 100644 index 00000000..968ed5d1 --- /dev/null +++ b/vaadin-cdi/src/test/java/com/vaadin/cdi/SerializationUtils.java @@ -0,0 +1,44 @@ +/* + * 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.cdi; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +public final class SerializationUtils { + + private SerializationUtils() { + } + + @SuppressWarnings("unchecked") + public static S serializeAndDeserialize(S obj) + throws Exception { + // Serialize and deserialize + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + try (ObjectOutputStream out = new ObjectOutputStream(bs)) { + out.writeObject(obj); + } + byte[] data = bs.toByteArray(); + try (ObjectInputStream in = new ObjectInputStream( + new ByteArrayInputStream(data))) { + return (S) in.readObject(); + } + } + +} diff --git a/vaadin-cdi/src/test/java/com/vaadin/cdi/context/RouteContextNormalTest.java b/vaadin-cdi/src/test/java/com/vaadin/cdi/context/RouteContextNormalTest.java index 35ac1ce4..a49ebcbf 100644 --- a/vaadin-cdi/src/test/java/com/vaadin/cdi/context/RouteContextNormalTest.java +++ b/vaadin-cdi/src/test/java/com/vaadin/cdi/context/RouteContextNormalTest.java @@ -8,15 +8,26 @@ */ package com.vaadin.cdi.context; +import java.io.Serializable; import java.util.Collections; +import org.apache.deltaspike.core.api.provider.BeanProvider; import org.apache.deltaspike.testcontrol.api.junit.CdiTestRunner; +import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; +import org.mockito.MockedStatic; +import org.mockito.Mockito; import com.vaadin.cdi.annotation.NormalRouteScoped; import com.vaadin.cdi.context.RouteScopedContext.NavigationData; import com.vaadin.flow.component.ComponentUtil; +import com.vaadin.flow.component.UI; import com.vaadin.flow.router.Route; +import com.vaadin.flow.server.startup.ApplicationConfiguration; + +import static com.vaadin.cdi.SerializationUtils.serializeAndDeserialize; +import static org.junit.Assert.assertNotNull; @RunWith(CdiTestRunner.class) public class RouteContextNormalTest extends @@ -24,7 +35,7 @@ public class RouteContextNormalTest extends @NormalRouteScoped @Route("") - public static class RouteScopedTestBean extends TestBean { + public static class RouteScopedTestBean extends TestBean implements Serializable { } @Override @@ -55,4 +66,18 @@ protected boolean isNormalScoped() { return true; } + @Test + public void activeContext_UISerializable() throws Exception { + UIUnderTestContext context = (UIUnderTestContext) createContext(); + context.activate(); + BeanProvider.getContextualReference(getBeanType()); + UI ui = context.getUi(); + try (MockedStatic appCfg = Mockito.mockStatic(ApplicationConfiguration.class)) { + appCfg.when(() -> ApplicationConfiguration.get(ArgumentMatchers.any())) + .thenReturn(Mockito.mock(ApplicationConfiguration.class)); + UI ui2 = serializeAndDeserialize(ui); + assertNotNull(ui2); + } + } + } diff --git a/vaadin-cdi/src/test/java/com/vaadin/cdi/context/RouteContextPseudoTest.java b/vaadin-cdi/src/test/java/com/vaadin/cdi/context/RouteContextPseudoTest.java index 1960d8f9..bd5725b9 100644 --- a/vaadin-cdi/src/test/java/com/vaadin/cdi/context/RouteContextPseudoTest.java +++ b/vaadin-cdi/src/test/java/com/vaadin/cdi/context/RouteContextPseudoTest.java @@ -8,15 +8,26 @@ */ package com.vaadin.cdi.context; +import java.io.Serializable; import java.util.Collections; +import org.apache.deltaspike.core.api.provider.BeanProvider; import org.apache.deltaspike.testcontrol.api.junit.CdiTestRunner; +import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; +import org.mockito.MockedStatic; +import org.mockito.Mockito; import com.vaadin.cdi.annotation.RouteScoped; import com.vaadin.cdi.context.RouteScopedContext.NavigationData; import com.vaadin.flow.component.ComponentUtil; +import com.vaadin.flow.component.UI; import com.vaadin.flow.router.Route; +import com.vaadin.flow.server.startup.ApplicationConfiguration; + +import static com.vaadin.cdi.SerializationUtils.serializeAndDeserialize; +import static org.junit.Assert.assertNotNull; @RunWith(CdiTestRunner.class) public class RouteContextPseudoTest extends @@ -30,7 +41,7 @@ public void setUp() { @RouteScoped @Route("") - public static class RouteScopedTestBean extends TestBean { + public static class RouteScopedTestBean extends TestBean implements Serializable { } @Override @@ -61,4 +72,18 @@ protected boolean isNormalScoped() { return false; } + @Test + public void activeContext_UISerializable() throws Exception { + UIUnderTestContext context = (UIUnderTestContext) createContext(); + context.activate(); + BeanProvider.getContextualReference(getBeanType()); + UI ui = context.getUi(); + try (MockedStatic appCfg = Mockito.mockStatic(ApplicationConfiguration.class)) { + appCfg.when(() -> ApplicationConfiguration.get(ArgumentMatchers.any())) + .thenReturn(Mockito.mock(ApplicationConfiguration.class)); + UI ui2 = serializeAndDeserialize(ui); + assertNotNull(ui2); + } + } + }