001// Copyright 2010 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007// http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.tapestry5.ioc.services;
016
017import java.util.Objects;
018import java.util.function.Consumer;
019import java.util.function.Function;
020import java.util.function.Supplier;
021
022/**
023 * Provides access to per-thread (and, by extension, per-request) data, managed by the {@link PerthreadManager}.
024 * A PerThreadValue stores a particular type of information.
025 *
026 * @see org.apache.tapestry5.ioc.services.PerthreadManager#createValue()
027 * @since 5.2.0
028 */
029public interface PerThreadValue<T>
030{
031    /**
032     * Is a value stored (even null)?
033     */
034    boolean exists();
035
036    /**
037     * Reads the current per-thread value, or returns null if no value has been stored.
038     */
039    T get();
040
041    /**
042     * Gets the current per-thread value if it exists (even if null), or the defaultValue
043     * if no value has been stored.
044     */
045    T get(T defaultValue);
046
047    /**
048     * Sets the current per-thread value, then returns that value.
049     */
050    T set(T newValue);
051
052    /**
053     * If no value is currently stored (checked by {@link #exists()}), the value
054     * provided by the supplier function is set and return.
055     * Otherwise, the current value is returned.
056     *
057     * @param fn the value supplier function
058     * @return The current (existing or computed) value
059     * @throws NullPointerException if the supplier function is null
060     * @since 5.8.3
061     */
062    default T computeIfAbsent(Supplier<? extends T> fn) {
063        Objects.requireNonNull(fn);
064        if (exists()) {
065            return get();
066        }
067
068        T newValue = fn.get();
069        set(newValue);
070
071        return newValue;
072    }
073
074    /**
075     * If a value is currently stored (checked by {@link #exists()}), this value
076     * is used to compute a new one with the given mapping function.
077     * Otherwise, null is returned.
078     *
079     * @param fn the mapping function to compute the new value
080     * @return The new computed value, or null if none was present
081     * @throws NullPointerException if the mapping function is null
082     * @since 5.8.3
083     */
084    default T computeIfPresent(Function<? super T, ? extends T> fn) {
085        Objects.requireNonNull(fn);
086        if (!exists()) {
087            return null;
088        }
089        
090        T newValue = fn.apply(get());
091        set(newValue);
092
093        return newValue;
094    }
095
096    /**
097     * Computes a new value with the help of the current one, which is returned.
098     *
099     * @param fn the mapping function to compute the new value
100     * @return The new computed value
101     * @throws NullPointerException if the mapping function is null
102     * @since 5.8.3
103     */
104    default T compute(Function<? super T, ? extends T> fn) {
105        Objects.requireNonNull(fn);
106
107        T newValue = fn.apply(get());
108        set(newValue);
109
110        return newValue;
111    }
112
113    /**
114     * If a value is set, performs the given action with it, otherwise it
115     * does nothing.
116     *
117     * @param action performed action if a value is set
118     * @throws NullPointerException if the action is null
119     * @since 5.8.3
120     */
121    default void ifSet(Consumer<? super T> action) {
122        Objects.requireNonNull(action);
123
124        if (!exists()) {
125            return;
126        }
127
128        action.accept(get());
129    }
130}