/******************************************************************************* * Copyright (c) 2008, 2014 Stuart McCulloch * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Stuart McCulloch - initial API and implementation *******************************************************************************/ package org.eclipse.sisu.peaberry.util.decorators; import java.lang.ref.WeakReference; import java.util.concurrent.Callable; import org.eclipse.sisu.peaberry.Import; import org.eclipse.sisu.peaberry.ServiceException; import org.eclipse.sisu.peaberry.builders.ImportDecorator; import org.eclipse.sisu.peaberry.util.DelegatingImport; /** * An {@link ImportDecorator} that caches the first valid service instance and * uses that until it becomes invalid. The decorator then calls the reset task * to see if it should reset the cache and get a new service instance. * <p> * If no reset task is provided, the service instance cache is never reset. * <p> * Note: sticky decorators only really make sense for <i>single</i> services. * * @author mcculls@gmail.com (Stuart McCulloch) */ public final class StickyDecorator<S> implements ImportDecorator<S> { final Callable<Boolean> resetTask; public StickyDecorator(final Callable<Boolean> resetTask) { this.resetTask = resetTask; } private final class StickyImport<T> extends DelegatingImport<T> { private WeakReference<T> instanceRef; private boolean reset = true; StickyImport(final Import<T> service) { super(service); } @Override public synchronized T get() { // when service becomes unavailable we ask if the client wants to reset if (null != resetTask && null != instanceRef && !super.available()) { // always clear the current service once it's invalid instanceRef.clear(); instanceRef = null; try { // should we reset and take the next valid service? reset = resetTask.call(); } catch (final Exception e) { throw new ServiceException("Exception in resetTask", e); } if (reset) { super.unget(); // balance previous successful get } } if (reset) { try { final T instance = super.get(); instanceRef = new WeakReference<T>(instance); reset = null == instance; } finally { if (reset) { instanceRef = null; super.unget(); // balance previous unsuccessful get } } } return null == instanceRef ? null : instanceRef.get(); } @Override public void unget() {/* nothing to do */} } public <T extends S> Import<T> decorate(final Import<T> service) { return new StickyImport<T>(service); } }