/******************************************************************************* * 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.cache; import static java.util.logging.Level.WARNING; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; import org.eclipse.sisu.peaberry.Export; import org.eclipse.sisu.peaberry.Import; import org.eclipse.sisu.peaberry.ServiceUnavailableException; /** * Partial {@link Import} implementation with generation based caching. * * @author mcculls@gmail.com (Stuart McCulloch) */ public abstract class AbstractServiceImport<T> implements Import<T>, Comparable<Import<T>> { private static final Logger LOGGER = Logger.getLogger(AbstractServiceImport.class.getName()); static final int MODIFIED = 0; static final int UNREGISTERING = 1; private static final int INVALID = -1; private static final int DORMANT = 0; private static final int ACTIVE = 1; // generation-based service cache private volatile T instance; private final AtomicInteger count; private volatile int state; private int generation; private final List<Export<?>> watchers; // current cache generation (global member) private static volatile int cacheGeneration; protected AbstractServiceImport() { // need accurate usage count count = new AtomicInteger(); watchers = new ArrayList<Export<?>>(2); } public final T get() { count.getAndIncrement(); if (DORMANT == state) { synchronized (this) { if (DORMANT == state) { try { instance = acquireService(); } catch (final RuntimeException re) { throw new ServiceUnavailableException(re); } finally { state = ACTIVE; } } } } return instance; } public final void unget() { generation = cacheGeneration; count.decrementAndGet(); } public final boolean available() { return INVALID != state; } /** * Protected from concurrent access by {@link AbstractServiceListener}. */ void addWatcher(final Export<?> export) { watchers.add(export); } /** * Protected from concurrent access by {@link AbstractServiceListener}. */ void invalidate() { notifyWatchers(UNREGISTERING); watchers.clear(); synchronized (this) { instance = null; state = INVALID; } } public static final void setCacheGeneration(final int newGeneration) { cacheGeneration = newGeneration; } /** * Protected from concurrent access by {@link AbstractServiceListener}. */ void flush(final int targetGeneration) { // check no-one is using the active service and it belongs to the generation if (targetGeneration == generation && ACTIVE == state && 0 == count.get()) { synchronized (this) { // has it just gone? if (INVALID == state) { return; } // block other threads entering get() state = DORMANT; if (count.get() > 0) { state = ACTIVE; // another thread snuck in, so roll back... } else { try { releaseService(instance); } catch (final RuntimeException re) {/* already gone */} // NOPMD finally { instance = null; } } } } } void notifyWatchers(final int eventType) { for (final Export<?> export : watchers) { try { switch (eventType) { case MODIFIED: export.attributes(attributes()); break; case UNREGISTERING: export.unput(); break; default: break; } } catch (final RuntimeException re) { LOGGER.log(WARNING, "Exception in service watcher", re); } } } protected abstract T acquireService(); protected abstract boolean hasRankingChanged(); protected abstract void releaseService(T o); }