All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.vaadin.util.CurrentInstance Maven / Gradle / Ivy
/*
* Copyright (C) 2000-2024 Vaadin Ltd
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See for the full
* license.
*/
package com.vaadin.util;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.Collections;
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;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinResponse;
import com.vaadin.server.VaadinService;
import com.vaadin.server.VaadinSession;
import com.vaadin.ui.UI;
/**
* Keeps track of various current instances for the current thread. All the
* instances are automatically cleared after handling a request from the client
* to avoid leaking memory.
*
* Please note that the instances are stored using {@link WeakReference}. This
* means that the a current instance value may suddenly disappear if there a no
* other references to the object.
*
* Currently the framework uses the following instances:
*
*
* {@link UI}, {@link VaadinService}, {@link VaadinSession},
* {@link VaadinRequest}, {@link VaadinResponse}.
*
*
* @author Vaadin Ltd
* @since 7.0.0
*/
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, CurrentInstanceFallbackResolver>> fallbackResolvers = new ConcurrentHashMap, CurrentInstanceFallbackResolver>>();
private final WeakReference instance;
private static final ThreadLocal, CurrentInstance>> INSTANCES = new ThreadLocal<>();
private CurrentInstance(Object instance) {
this.instance = new WeakReference<>(instance);
}
/**
* Gets the current instance of a specific type if available.
*
* 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
* @return the current instance or the provided type, or null
* if there is no current instance.
*/
public static T get(Class 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 doGet(Class type) {
Map, CurrentInstance> map = INSTANCES.get();
if (map == null) {
return null;
}
CurrentInstance currentInstance = map.get(type);
if (currentInstance != null) {
Object value = currentInstance.instance.get();
if (value == null) {
/*
* This is believed to never actually happen since the
* ThreadLocal should only outlive the referenced object on
* threads that are not doing anything related to Vaadin, which
* should thus never invoke CurrentInstance.get().
*
* At this point, there might also be other values that have
* been collected, so we'll scan the entire map and remove stale
* CurrentInstance objects. Using a ReferenceQueue could make
* this assumingly rare case slightly more efficient, but would
* significantly increase the complexity of the code for
* maintaining a separate ReferenceQueue for each Thread.
*/
removeStaleInstances(map);
if (map.isEmpty()) {
INSTANCES.remove();
}
return null;
}
return type.cast(value);
} else {
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 null
*
* @throws IllegalArgumentException
* if there's already a defined fallback resolver for the given
* type
* @since 8.5.2
*/
public static void defineFallbackResolver(Class type,
CurrentInstanceFallbackResolver 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, CurrentInstance> map) {
for (Iterator, CurrentInstance>> iterator = map
.entrySet().iterator(); iterator.hasNext();) {
Entry, CurrentInstance> entry = iterator.next();
Object instance = entry.getValue().instance.get();
if (instance == null) {
iterator.remove();
getLogger().log(Level.FINE,
"CurrentInstance for {0} has been garbage collected.",
entry.getKey());
}
}
}
/**
* Sets the current instance of the given type.
*
* @see ThreadLocal
*
* @param type
* the class that should be used when getting the current
* instance back
* @param instance
* the actual instance
*/
public static CurrentInstance set(Class type, T instance) {
Map, CurrentInstance> map = INSTANCES.get();
CurrentInstance previousInstance = null;
if (instance == null) {
// remove the instance
if (map != null) {
previousInstance = map.remove(type);
if (map.isEmpty()) {
INSTANCES.remove();
map = null;
}
}
} else {
assert type.isInstance(instance) : "Invald instance type";
if (map == null) {
map = new HashMap<>();
INSTANCES.set(map);
}
previousInstance = map.put(type, new CurrentInstance(instance));
}
if (previousInstance == null) {
previousInstance = CURRENT_INSTANCE_NULL;
}
return previousInstance;
}
/**
* Clears all current instances.
*/
public static void clearAll() {
INSTANCES.remove();
}
/**
* Restores the given instances to the given values. Note that this should
* only be used internally to restore Vaadin classes.
*
* @since 7.1
*
* @param old
* A Class -> CurrentInstance map to set as current instances
*/
public static void restoreInstances(Map, CurrentInstance> old) {
boolean removeStale = false;
for (Class c : old.keySet()) {
CurrentInstance ci = old.get(c);
Object v = ci.instance.get();
if (v == null) {
removeStale = true;
} else if (v == NULL_OBJECT) {
/*
* NULL_OBJECT is used to identify objects that are null when
* #setCurrent(UI) or #setCurrent(VaadinSession) are called on a
* CurrentInstance. Without this a reference to an already
* collected instance may be left in the CurrentInstance when it
* really should be restored to null.
*
* One example case that this fixes:
* VaadinService.runPendingAccessTasks() clears all current
* instances and then sets everything but the UI. This makes
* UI.accessSynchronously() save these values before calling
* setCurrent(UI), which stores UI=null in the map it returns.
* This map will be restored after UI.accessSync(), which,
* unless it respects null values, will just leave the wrong UI
* instance registered.
*/
v = null;
}
set(c, v);
}
if (removeStale) {
removeStaleInstances(old);
}
}
/**
* Gets the currently set instances so that they can later be restored using
* {@link #restoreInstances(Map)}.
*
* @since 8.0
*
* @return a map containing the current instances
*/
public static Map, CurrentInstance> getInstances() {
Map, CurrentInstance> map = INSTANCES.get();
if (map == null) {
return Collections.emptyMap();
} else {
Map, CurrentInstance> copy = new HashMap<>();
boolean removeStale = false;
for (Class> c : map.keySet()) {
CurrentInstance ci = map.get(c);
if (ci.instance.get() == null) {
removeStale = true;
} else {
copy.put(c, ci);
}
}
if (removeStale) {
removeStaleInstances(map);
if (map.isEmpty()) {
INSTANCES.remove();
}
}
return copy;
}
}
/**
* Sets current instances for the UI and all related classes. The previously
* defined values can be restored by passing the returned map to
* {@link #restoreInstances(Map)}.
*
* @since 7.1
*
* @param ui
* The UI
* @return A map containing the old values of the instances that this method
* updated.
*/
public static Map, CurrentInstance> setCurrent(UI ui) {
Map, CurrentInstance> old = setCurrent(ui.getSession());
old.put(UI.class, set(UI.class, ui));
return old;
}
/**
* Sets current instances for the {@link VaadinSession} and all related
* classes. The previously defined values can be restored by passing the
* returned map to {@link #restoreInstances(Map)}.
*
* @since 7.1
*
* @param session
* The VaadinSession
* @return A map containing the old values of the instances this method
* updated.
*/
public static Map, CurrentInstance> setCurrent(
VaadinSession session) {
Map, CurrentInstance> old = new HashMap<>();
old.put(VaadinSession.class, set(VaadinSession.class, session));
VaadinService service = null;
if (session != null) {
service = session.getService();
}
old.put(VaadinService.class, set(VaadinService.class, service));
return old;
}
private static Logger getLogger() {
return Logger.getLogger(CurrentInstance.class.getName());
}
}