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;
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;
/**
* 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
* 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;
}
}
+ /**
+ * 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
--- /dev/null
+/*
+ * 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();
+
+}
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;
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;
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();
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++) {
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);
+ }
+
+ }
+
}