summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorGilberto Torrezan <gilberto-torrezan@users.noreply.github.com>2018-06-13 10:08:04 +0300
committerGilberto Torrezan <gilberto-torrezan@users.noreply.github.com>2018-09-03 15:21:22 +0300
commit8d61288b6fd19a7f5284c05b1917d33eeb816d2a (patch)
treefe3072366d6bd768e9f3025e057a6b499ebae290 /server
parent328b057f4087f546a0a26302e23014aae09920d7 (diff)
downloadvaadin-framework-8d61288b6fd19a7f5284c05b1917d33eeb816d2a.tar.gz
vaadin-framework-8d61288b6fd19a7f5284c05b1917d33eeb816d2a.zip
Add fallback resolvers for CurrentInstance (#10974)
This allow applications to inject custom default instances when the current instances cannot be found by regular means. For example, when VaadinServlet.getCurrent() would return null, a fallback resolver could be invoked to properly create the servlet and return it.
Diffstat (limited to 'server')
-rw-r--r--server/src/main/java/com/vaadin/util/CurrentInstance.java49
-rw-r--r--server/src/main/java/com/vaadin/util/CurrentInstanceFallbackResolver.java49
-rw-r--r--server/src/test/java/com/vaadin/util/CurrentInstanceTest.java120
3 files changed, 218 insertions, 0 deletions
diff --git a/server/src/main/java/com/vaadin/util/CurrentInstance.java b/server/src/main/java/com/vaadin/util/CurrentInstance.java
index efffd49108..b8567f0f0c 100644
--- a/server/src/main/java/com/vaadin/util/CurrentInstance.java
+++ b/server/src/main/java/com/vaadin/util/CurrentInstance.java
@@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -55,6 +56,7 @@ public class CurrentInstance implements Serializable {
private static final Object NULL_OBJECT = new Object();
private static final CurrentInstance CURRENT_INSTANCE_NULL = new CurrentInstance(
NULL_OBJECT);
+ private static final ConcurrentHashMap<Class<?>, CurrentInstanceFallbackResolver<?>> fallbackResolvers = new ConcurrentHashMap<Class<?>, CurrentInstanceFallbackResolver<?>>();
private final WeakReference<Object> instance;
@@ -66,6 +68,11 @@ public class CurrentInstance implements Serializable {
/**
* Gets the current instance of a specific type if available.
+ * <p>
+ * When a current instance of the specific type is not found, the
+ * {@link CurrentInstanceFallbackResolver} registered via
+ * {@link #defineFallbackResolver(Class, CurrentInstanceFallbackResolver)}
+ * (if any) is invoked.
*
* @param type
* the class to get an instance of
@@ -73,6 +80,19 @@ public class CurrentInstance implements Serializable {
* if there is no current instance.
*/
public static <T> T get(Class<T> type) {
+ T result = doGet(type);
+ if (result != null) {
+ return result;
+ }
+ CurrentInstanceFallbackResolver<?> fallbackResolver = fallbackResolvers
+ .get(type);
+ if (fallbackResolver != null) {
+ return (T) fallbackResolver.resolve();
+ }
+ return null;
+ }
+
+ private static <T> T doGet(Class<T> type) {
Map<Class<?>, CurrentInstance> map = INSTANCES.get();
if (map == null) {
return null;
@@ -108,6 +128,35 @@ public class CurrentInstance implements Serializable {
}
}
+ /**
+ * Adds a CurrentInstanceFallbackResolver, that is triggered when
+ * {@link #get(Class)} can't find a suitable instance for the given type
+ * parameter.
+ *
+ * @param type
+ * the class used on {@link #get(Class)} invocations to retrieve
+ * the current instance
+ * @param fallbackResolver
+ * the resolver, not <code>null</code>
+ *
+ * @throws IllegalArgumentException
+ * if there's already a defined fallback resolver for the given
+ * type
+ * @since
+ */
+ public static <T> void defineFallbackResolver(Class<T> type,
+ CurrentInstanceFallbackResolver<T> fallbackResolver) {
+ if (fallbackResolver == null) {
+ throw new IllegalArgumentException(
+ "The fallback resolver can not be null.");
+ }
+ if (fallbackResolvers.putIfAbsent(type, fallbackResolver) != null) {
+ throw new IllegalArgumentException(
+ "A fallback resolver for the type " + type
+ + " is already defined.");
+ }
+ }
+
private static void removeStaleInstances(
Map<Class<?>, CurrentInstance> map) {
for (Iterator<Entry<Class<?>, CurrentInstance>> iterator = map
diff --git a/server/src/main/java/com/vaadin/util/CurrentInstanceFallbackResolver.java b/server/src/main/java/com/vaadin/util/CurrentInstanceFallbackResolver.java
new file mode 100644
index 0000000000..2c8cefe953
--- /dev/null
+++ b/server/src/main/java/com/vaadin/util/CurrentInstanceFallbackResolver.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2000-2018 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.util;
+
+import java.io.Serializable;
+
+/**
+ * Fallback that is used to revolve current instances when they are not
+ * available by regular means.
+ * <p>
+ * This interface is used internally by the framework and it's not meant for
+ * public usage.
+ *
+ * @author Vaadin Ltd.
+ *
+ * @param <T>
+ * the type of the instances returned by this resolver
+ *
+ * @see CurrentInstance#get(Class)
+ * @see CurrentInstance#defineFallbackResolver(Class,
+ * CurrentInstanceFallbackResolver)
+ *
+ * @since
+ *
+ */
+public interface CurrentInstanceFallbackResolver<T> extends Serializable {
+
+ /**
+ * Resolves a current instance for the type {@code T}.
+ *
+ * @return the current instance, or <code>null</code> if none can be found
+ */
+ T resolve();
+
+}
diff --git a/server/src/test/java/com/vaadin/util/CurrentInstanceTest.java b/server/src/test/java/com/vaadin/util/CurrentInstanceTest.java
index 87d6245dcb..1beae2d3be 100644
--- a/server/src/test/java/com/vaadin/util/CurrentInstanceTest.java
+++ b/server/src/test/java/com/vaadin/util/CurrentInstanceTest.java
@@ -3,11 +3,13 @@ package com.vaadin.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.Map;
+import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -15,11 +17,17 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.easymock.EasyMock;
+import org.hamcrest.CoreMatchers;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import com.vaadin.server.DefaultDeploymentConfiguration;
+import com.vaadin.server.ServiceException;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinService;
+import com.vaadin.server.VaadinServlet;
+import com.vaadin.server.VaadinServletService;
import com.vaadin.server.VaadinSession;
import com.vaadin.ui.UI;
@@ -31,6 +39,17 @@ public class CurrentInstanceTest {
CurrentInstance.clearAll();
}
+ @Before
+ @After
+ public void clearExistingFallbackResolvers() throws Exception {
+ // Removes all static fallback resolvers
+ Field field = CurrentInstance.class
+ .getDeclaredField("fallbackResolvers");
+ field.setAccessible(true);
+ Map<?, ?> map = (Map<?, ?>) field.get(null);
+ map.clear();
+ }
+
@Test
public void testInitiallyCleared() throws Exception {
assertCleared();
@@ -144,6 +163,62 @@ public class CurrentInstanceTest {
assertNull(VaadinSession.getCurrent());
}
+ @Test
+ public void testFallbackResolvers() throws Exception {
+ TestFallbackResolver<UI> uiResolver = new TestFallbackResolver<UI>(
+ new FakeUI());
+ CurrentInstance.defineFallbackResolver(UI.class, uiResolver);
+
+ TestFallbackResolver<VaadinSession> sessionResolver = new TestFallbackResolver<VaadinSession>(
+ new FakeSession());
+ CurrentInstance.defineFallbackResolver(VaadinSession.class,
+ sessionResolver);
+
+ TestFallbackResolver<VaadinService> serviceResolver = new TestFallbackResolver<VaadinService>(
+ new FakeService(new FakeServlet()));
+ CurrentInstance.defineFallbackResolver(VaadinService.class,
+ serviceResolver);
+
+ assertThat(UI.getCurrent(), CoreMatchers.instanceOf(FakeUI.class));
+ assertThat(VaadinSession.getCurrent(),
+ CoreMatchers.instanceOf(FakeSession.class));
+ assertThat(VaadinService.getCurrent(),
+ CoreMatchers.instanceOf(FakeService.class));
+
+ assertEquals(
+ "The UI fallback resolver should have been called exactly once",
+ 1, uiResolver.getCalled());
+
+ assertEquals(
+ "The VaadinSession fallback resolver should have been called exactly once",
+ 1, sessionResolver.getCalled());
+
+ assertEquals(
+ "The VaadinService fallback resolver should have been called exactly once",
+ 1, serviceResolver.getCalled());
+
+ // the VaadinServlet.getCurrent() resolution uses the VaadinService type
+ assertThat(VaadinServlet.getCurrent(),
+ CoreMatchers.instanceOf(FakeServlet.class));
+ assertEquals(
+ "The VaadinService fallback resolver should have been called exactly twice",
+ 2, serviceResolver.getCalled());
+
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testFallbackResolversWithAlreadyDefinedResolver() {
+ TestFallbackResolver<UI> uiResolver = new TestFallbackResolver<UI>(
+ new FakeUI());
+ CurrentInstance.defineFallbackResolver(UI.class, uiResolver);
+ CurrentInstance.defineFallbackResolver(UI.class, uiResolver);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testFallbackResolversWithNullResolver() {
+ CurrentInstance.defineFallbackResolver(UI.class, null);
+ }
+
public static void waitUntilGarbageCollected(WeakReference<?> ref)
throws InterruptedException {
for (int i = 0; i < 50; i++) {
@@ -172,4 +247,49 @@ public class CurrentInstanceTest {
Future<Void> future = service.submit(runnable);
future.get();
}
+
+ private static class TestFallbackResolver<T>
+ implements CurrentInstanceFallbackResolver<T> {
+
+ private int called;
+ private final T instance;
+
+ public TestFallbackResolver(T instance) {
+ this.instance = instance;
+ }
+
+ @Override
+ public T resolve() {
+ called++;
+ return instance;
+ }
+
+ public int getCalled() {
+ return called;
+ }
+ }
+
+ private static class FakeUI extends UI {
+ @Override
+ protected void init(VaadinRequest request) {
+ }
+ }
+
+ private static class FakeServlet extends VaadinServlet {
+ }
+
+ private static class FakeService extends VaadinServletService {
+ public FakeService(VaadinServlet servlet) throws ServiceException {
+ super(servlet, new DefaultDeploymentConfiguration(FakeService.class,
+ new Properties()));
+ }
+ }
+
+ private static class FakeSession extends VaadinSession {
+ public FakeSession() {
+ super(null);
+ }
+
+ }
+
}