io.atlassian.util.concurrent.ResettableLazyReference Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of atlassian-util-concurrent Show documentation
Show all versions of atlassian-util-concurrent Show documentation
This project contains utility classes that are used by
various products and projects inside Atlassian and may have some
utility to the world at large.
/**
* Copyright 2008 Atlassian Pty 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 io.atlassian.util.concurrent;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.Supplier;
import io.atlassian.util.concurrent.LazyReference.InitializationException;
import net.jcip.annotations.ThreadSafe;
/**
* Lazily loaded reference that is not constructed until required. This class is
* used to maintain a reference to an object that is expensive to create, but
* may need to be reset and recomputed at a later time. Object creation is
* guaranteed to be thread-safe and the first thread that calls {@link #get()}
* will be the one that creates it.
*
* Usage: clients need to implement the {@link #create()} method to return the
* object this reference will hold.
*
* For instance:
*
*
* final ResettableLazyReference<MyObject> ref = new ResettableLazyReference() {
* protected MyObject create() throws Exception {
* // Do expensive object construction here
* return new MyObject();
* }
* };
*
*
* Then call {@link #get()} to get a reference to the referenced object:
*
*
* MyObject myLazyLoadedObject = ref.get()
*
*
* NOTE: Interruption policy is that if you want to be cancellable while waiting
* for another thread to create the value, instead of calling {@link #get()}
* call {@link #getInterruptibly()}. However, If your {@link #create()} method
* is interrupted and throws an {@link java.lang.InterruptedException}, it is
* treated as an application exception and will be the causal exception inside
* the runtime
* {@link io.atlassian.util.concurrent.LazyReference.InitializationException}
* that {@link #get()} or {@link #getInterruptibly()} throws and your
* {@link #create()} will not be called again.
*
* @param the type of the contained element.
*/
@ThreadSafe public abstract class ResettableLazyReference implements Supplier {
@SuppressWarnings("rawtypes") private static final AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater
.newUpdater(ResettableLazyReference.class, InternalReference.class, "referrent");
private volatile InternalReference referrent = new InternalReference(this);
/**
* The object factory method, guaranteed to be called once and only once.
*
* @return the object that {@link #get()} and {@link #getInterruptibly()} will
* return.
* @throws java.lang.Exception if anything goes wrong, rethrown as an
* Initializationjava.lang.Exception from {@link #get()} and
* {@link #getInterruptibly()}
*/
protected abstract T create() throws Exception;
/**
* Get the lazily loaded reference in a non-cancellable manner. If your
* create()
method throws an Exception calls to
* get()
will throw an InitializationException which wraps the
* previously thrown exception.
*
* @return the object that {@link #create()} created.
* @throws InitializationException if the {@link #create()} method throws an
* exception. The
* {@link io.atlassian.util.concurrent.LazyReference.InitializationException#getCause()}
* will contain the exception thrown by the {@link #create()} method
*/
public final T get() {
return referrent.get();
}
/**
* Get the lazily loaded reference in a cancellable manner. If your
* create()
method throws an Exception, calls to
* get()
will throw a RuntimeException which wraps the previously
* thrown exception.
*
* @return the object that {@link #create()} created.
* @throws InitializationException if the {@link #create()} method throws an
* exception. The
* {@link io.atlassian.util.concurrent.LazyReference.InitializationException#getCause()}
* will contain the exception thrown by the {@link #create()} method
* @throws java.lang.InterruptedException If the calling thread is Interrupted
* while waiting for another thread to create the value (if the creating
* thread is interrupted while blocking on something, the
* {@link java.lang.InterruptedException} will be thrown as the causal
* exception of the
* {@link io.atlassian.util.concurrent.LazyReference.InitializationException}
* to everybody calling this method).
*/
public final T getInterruptibly() throws InterruptedException {
return referrent.getInterruptibly();
}
/**
* Reset the internal reference. Anyone currently in the process of calling
* {@link #get()} will still force that and receive the old reference.
*
* Note: this method was made final in 3.0. Override
* {@link #onReset(LazyReference)} to implement custom reset behavior.
*/
public final void reset() {
resets();
}
/**
* Reset the internal reference and returns a LazyReference of the old value.
* Anyone currently in the process of calling {@link #get()} will still force
* that and receive the old reference however.
*
* @return A lazy reference of the old value that may or may not already be
* initialized. Calling the supplier may block and cause the initialization to
* occur.
*/
public final LazyReference resets() {
@SuppressWarnings("unchecked")
LazyReference result = updater.getAndSet(this, new InternalReference(this));
onReset(result);
return result;
}
/**
* Template extension method for providing custom reset behavior.
*
* @param oldValue the old LazyReference, guaranteed that nobody else has
* access to it anymore.
* @since 2.6
*/
protected void onReset(LazyReference oldValue) {}
/**
* Has the {@link #create()} reference been initialized.
*
* @return true if the task is complete and has not been reset.
*/
public final boolean isInitialized() {
return referrent.isInitialized();
}
/**
* Cancel the initializing operation if it has not already run. Will try and
* interrupt if it is currently running.
*/
public final void cancel() {
referrent.cancel();
}
/**
* The internal LazyReference that may get thrown away
*/
static class InternalReference extends LazyReference {
private final ResettableLazyReference ref;
InternalReference(ResettableLazyReference ref) {
this.ref = ref;
}
@Override protected T create() throws Exception {
return ref.create();
}
}
}