From f0a6e84eda0b2e838ef05bb0978ecba6707921c3 Mon Sep 17 00:00:00 2001 From: Denis Date: Wed, 9 Dec 2020 16:04:44 +0300 Subject: [PATCH] chore: add internal classes to access classloader and init context (#9618) * chore: add internal classes to access classloader and init context related to #9601 : needed to restore OSGi resource provider --- flow-server/bnd.bnd | 3 +- .../ApplicationClassLoaderAccess.java | 41 ++++++++++++++++ .../internal/VaadinContextInitializer.java | 47 +++++++++++++++++++ .../com/vaadin/flow/server/VaadinServlet.java | 35 ++++++++++++-- .../vaadin/flow/server/VaadinServletTest.java | 45 ++++++++++++++++++ .../testutil/ClassesSerializableTest.java | 2 + 6 files changed, 166 insertions(+), 7 deletions(-) create mode 100644 flow-server/src/main/java/com/vaadin/flow/internal/ApplicationClassLoaderAccess.java create mode 100644 flow-server/src/main/java/com/vaadin/flow/internal/VaadinContextInitializer.java diff --git a/flow-server/bnd.bnd b/flow-server/bnd.bnd index e19449404d9..9f2f564bbc2 100644 --- a/flow-server/bnd.bnd +++ b/flow-server/bnd.bnd @@ -5,5 +5,4 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-License: http://www.apache.org/licenses/LICENSE-2.0 Import-Package: org.atmosphere*;resolution:=optional;version='${atmosphere.runtime.version}',\ org.apache.http*;resolution:=optional;,* -Export-Package: com.vaadin.flow.client,\ - !com.vaadin.flow.push*, com.vaadin.flow*;-noimport:=true +Export-Package: !com.vaadin.flow.push*, com.vaadin.flow*;-noimport:=true diff --git a/flow-server/src/main/java/com/vaadin/flow/internal/ApplicationClassLoaderAccess.java b/flow-server/src/main/java/com/vaadin/flow/internal/ApplicationClassLoaderAccess.java new file mode 100644 index 00000000000..4e39daa99d5 --- /dev/null +++ b/flow-server/src/main/java/com/vaadin/flow/internal/ApplicationClassLoaderAccess.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2020 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.internal; + +import com.vaadin.flow.server.VaadinContext; + +/** + * Allows to access the web application classloader. + *

+ * The functionality is intended to internal usage only. The implementation of + * this interface may be set as an attribute in {@link VaadinContext} so that + * the classloader may be used in other place where {@link VaadinContext} is + * available. + * + * @author Vaadin Ltd + * @since + * + */ +@FunctionalInterface +public interface ApplicationClassLoaderAccess { + + /** + * Gets the web application classloader. + * + * @return the web application classloader. + */ + ClassLoader getClassloader(); +} diff --git a/flow-server/src/main/java/com/vaadin/flow/internal/VaadinContextInitializer.java b/flow-server/src/main/java/com/vaadin/flow/internal/VaadinContextInitializer.java new file mode 100644 index 00000000000..34ea8d6c0b2 --- /dev/null +++ b/flow-server/src/main/java/com/vaadin/flow/internal/VaadinContextInitializer.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2020 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.flow.internal; + +import javax.servlet.ServletContextListener; + +import com.vaadin.flow.server.VaadinContext; +import com.vaadin.flow.server.VaadinServlet; + +/** + * Allows to run initialization of {@link VaadinContext} which for some reasons + * may not be done via {@link ServletContextListener}. + *

+ * The functionality is intended to internal usage only. The implementation of + * this interface may be available as an attribute in a {@link VaadinContext}. + * In the latter case {@link VaadinServlet#init()} method will run + * {@link #initialize(VaadinContext)} method. + * + * @author Vaadin Ltd + * @since + * + */ +@FunctionalInterface +public interface VaadinContextInitializer { + + /** + * Initializes the Vaadin {@code context}. + * + * @param context + * the Vaadin context instance + */ + void initialize(VaadinContext context); + +} diff --git a/flow-server/src/main/java/com/vaadin/flow/server/VaadinServlet.java b/flow-server/src/main/java/com/vaadin/flow/server/VaadinServlet.java index ffea886f189..eb03ee27b7d 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/VaadinServlet.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/VaadinServlet.java @@ -30,7 +30,9 @@ import com.vaadin.flow.component.UI; import com.vaadin.flow.di.Lookup; import com.vaadin.flow.function.DeploymentConfiguration; +import com.vaadin.flow.internal.ApplicationClassLoaderAccess; import com.vaadin.flow.internal.CurrentInstance; +import com.vaadin.flow.internal.VaadinContextInitializer; import com.vaadin.flow.server.HandlerHelper.RequestType; import com.vaadin.flow.shared.JsonConstants; @@ -84,8 +86,11 @@ public void init(ServletConfig servletConfig) throws ServletException { * its "init" method is called from the {@code * ServletContextListener} with the same ServletConfig instance. */ + VaadinServletContext vaadinServletContext = null; if (getServletConfig() == null) { super.init(servletConfig); + + vaadinServletContext = initializeContext(); } if (getServletConfig() != servletConfig) { @@ -94,11 +99,13 @@ public void init(ServletConfig servletConfig) throws ServletException { + "instance which has been used for the initial method call"); } - ServletContext servletContext = getServletConfig() - .getServletContext(); - if (servletService != null - || new VaadinServletContext(servletContext) - .getAttribute(Lookup.class) == null) { + if (vaadinServletContext == null) { + vaadinServletContext = new VaadinServletContext( + getServletConfig().getServletContext()); + } + + if (servletService != null || vaadinServletContext + .getAttribute(Lookup.class) == null) { return; } @@ -513,4 +520,22 @@ public void destroy() { getService().destroy(); } + private VaadinServletContext initializeContext() { + ServletContext servletContext = getServletConfig().getServletContext(); + VaadinServletContext vaadinServletContext = new VaadinServletContext( + servletContext); + // ensure the web application classloader is available via context + ApplicationClassLoaderAccess access = () -> servletContext + .getClassLoader(); + vaadinServletContext.getAttribute(ApplicationClassLoaderAccess.class, + () -> access); + + VaadinContextInitializer initializer = vaadinServletContext + .getAttribute(VaadinContextInitializer.class); + if (initializer != null) { + initializer.initialize(vaadinServletContext); + } + return vaadinServletContext; + } + } diff --git a/flow-server/src/test/java/com/vaadin/flow/server/VaadinServletTest.java b/flow-server/src/test/java/com/vaadin/flow/server/VaadinServletTest.java index 0608910e570..7372c553338 100644 --- a/flow-server/src/test/java/com/vaadin/flow/server/VaadinServletTest.java +++ b/flow-server/src/test/java/com/vaadin/flow/server/VaadinServletTest.java @@ -25,11 +25,14 @@ import net.jcip.annotations.NotThreadSafe; import org.junit.Assert; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import com.vaadin.flow.component.UI; import com.vaadin.flow.di.Lookup; +import com.vaadin.flow.internal.ApplicationClassLoaderAccess; import com.vaadin.flow.internal.CurrentInstance; +import com.vaadin.flow.internal.VaadinContextInitializer; @NotThreadSafe public class VaadinServletTest { @@ -222,6 +225,48 @@ public void init() throws ServletException { } } + @Test + public void init_appClassLoaderIsSet() throws ServletException { + VaadinServlet servlet = new VaadinServlet(); + + ServletConfig config = mockConfig(); + ServletContext servletContext = config.getServletContext(); + ClassLoader loader = Mockito.mock(ClassLoader.class); + Mockito.when(servletContext.getClassLoader()).thenReturn(loader); + servlet.init(config); + + ArgumentCaptor captor = ArgumentCaptor + .forClass(ApplicationClassLoaderAccess.class); + Mockito.verify(servletContext).setAttribute( + Mockito.eq(ApplicationClassLoaderAccess.class.getName()), + captor.capture()); + + ApplicationClassLoaderAccess access = captor.getValue(); + Assert.assertSame(loader, access.getClassloader()); + } + + @Test + public void init_contextInitializationIsExecuted() throws ServletException { + VaadinServlet servlet = new VaadinServlet(); + + ServletConfig config = mockConfig(); + ServletContext servletContext = config.getServletContext(); + ClassLoader loader = Mockito.mock(ClassLoader.class); + + VaadinContextInitializer initializer = Mockito + .mock(VaadinContextInitializer.class); + + Mockito.when(servletContext + .getAttribute(VaadinContextInitializer.class.getName())) + .thenReturn(initializer); + + Mockito.when(servletContext.getClassLoader()).thenReturn(loader); + servlet.init(config); + + Mockito.verify(initializer) + .initialize(Mockito.any(VaadinContext.class)); + } + private ServletConfig mockConfig() { ServletConfig config = Mockito.mock(ServletConfig.class); ServletContext context = Mockito.mock(ServletContext.class); 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 index 97cb50e31c1..3189afd84fc 100644 --- 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 @@ -69,6 +69,8 @@ protected Stream getExcludedPatterns() { "com\\.vaadin\\.flow\\.osgi\\.support\\..*", "com\\.vaadin\\.flow\\.server\\.osgi\\..*", + "com\\.vaadin\\.flow\\.internal\\.VaadinContextInitializer", + "com\\.vaadin\\.flow\\.internal\\.ApplicationClassLoaderAccess", "com\\.vaadin\\.flow\\.data\\.provider\\.InMemoryDataProviderHelpers", "com\\.vaadin\\.flow\\.di\\.InstantiatorFactory", "com\\.vaadin\\.flow\\.di\\.Lookup(\\$.*)?",