/* * Copyright 2012, We The Internet Ltd. * * All rights reserved. * * Distributed under a modified BSD License as follow: * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution, unless otherwise * agreed to in a written document signed by a director of We The Internet Ltd. * * Neither the name of We The Internet nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ package xapi.inject.impl; import xapi.util.api.ProvidesValue; import xapi.util.impl.ImmutableProvider; import javax.inject.Provider; /** * A proxy class used to create a lazy-loading provider, with an efficient proxy (self-overwriting) proxy. * .get() calls {@link #initialValue()} on first load and replaces the initializer with a pojo getter. * This allows for easy wrapping of lazily-loaded singletons without null-checking on every access. * @param <X> - The type of data this var returns. * * @author James X Nelson (james@wetheinter.net, @ajax) */ public abstract class SingletonProvider<X> implements Provider<X>, ProvidesValue<X>{ public class NullCheckOnGet implements Provider<X>{ /** * Synchronized so multi-threaded platforms don't get into race conditions. * Has no effect in javascript {yet!} */ @Override public final X get() { // perform a volatile read. Much cheaper than a full synchronization Provider<X> provider = proxy; if (provider == this) {// double checked lock part 1 //start lock; could be multiple threads here at once synchronized (this) {//compiles out of gwt provider = proxy; // another volatile read, in case we just blocked on the synchro if (provider == this) { // double checked lock part 2 // we won the race condition X init = initialValue();//try to init, if (null == init) { return null;//pay for null-checks + synchro until inited } proxy = createImmutableProvider(init);//replace proxy with an immutable provider onCreate(init);//call on create inside synchro block. //it would be nice if we could do this outside the synchro block, //but then any threads who lose the race condition will get back //a partially initialized instance return init; } else { //another thread has already inited while we waited. return provider.get();//just return whatever the new proxy supplies } } } else { return proxy.get(); } } } /** * The proxy object will override itself on first load with a bare pojo-based provider. * * In subclasses like @LazyPojo, this proxy object is toggled to allow for efficient get() when we can. * */ protected volatile Provider<X> proxy; public SingletonProvider() { proxy = new NullCheckOnGet(); } /** * Return a new proxy provider which just returns the set field. * * NEVER return an instance of {@link NullCheckOnGet}, * or you will contract a case of recursion sickness. * * @param init - A non-null value to provide immutably * @return - An immutable provider. * * IF SUBCLASSES RETURN A PROVIDER THAT IS NOT A SUBCLASS OF * {@link ImmutableProvider}, THEN YOU MUST OVERRIDE {@link #isSet()} AS WELL. */ protected Provider<X> createImmutableProvider(X init) { assert init != null : "Do not send null values to the immutable provider; " + "the singleton will then return null forever."; return new ImmutableProvider<X>(init); } /** * Tells whether or not this singleton is already set by instanceof checking * the pojo class return from {@link #createImmutableProvider(Object)}. * * IF YOU OVERRIDE {@link #createImmutableProvider(Object)}, YOU MUST ALSO OVERRIDE isSet(). * @return - Whether or not our proxy class matches the expected immutable class. */ public boolean isSet(){ return proxy instanceof ImmutableProvider; } /** * Called whenever the object is initialized. * Will be called on the first successful {@link #get()}, * and every successful get() after a call to {@link #reset()} * @param init */ protected void onCreate(X init) { } public void reset() { proxy = this; } /** * Called once, on first .get(). Override to provide new value, or use @LazyPojo to set(X) externally. * @return A singleton object that will be saved and returned on every subsequent .get() */ protected abstract X initialValue(); /** * This method is final so the compiler can optimize the call. * * This method is synchronized and null checks while the singleton is uninitialized, * but once inited, it is unsynchronized with direct access, null-check free access to singleton. */ public final X get() { return proxy.get(); } }