/******************************************************************************* * Copyright (c) 2009 Spring IDE Developers * 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: * Spring IDE Developers - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.beans.core.internal.model.namespaces; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; import org.osgi.framework.Bundle; /** * Map-like class with dedicated support for lazy bundles. Allows execution of operations on active * bundles, promoting the lazy ones, if necessary, as fall back. * @author Christian Dupuis * @author Costin Leau * @param<T> * the entity type associated with active bundles */ class LazyBundleRegistry<T> { /** * A simple condition-like class. * * @author Costin Leau */ interface Condition { /** * Indicates if the given target bundle passes the condition or not. * * @param bundle target bundle * @return true if the bundle passes the condition, false otherwise */ boolean pass(Bundle bundle); } /** * Activator action performed on lazy bundles upon promotion. * * @author Costin Leau * @param <V> */ interface Activator<V> { /** * Activates the given target bundle. * * @param <V> inherited return type * @param bundle target bundle * @return the bundle associated value */ V activate(Bundle bundle); } /** * Operation performed all bundles - first active ones, followed by lazy ones (if nothing is * found (null is returned)) by 'activating' them. * * @author Costin Leau * @param <T> * @param <V> */ interface Operation<T, V> { /** * Acts upon the bundle associated object (given as argument). * * @param object associated object * @return the result of the operation */ V operate(T t) throws Exception; } /** active, valid bundles */ private final ConcurrentMap<Bundle, T> activeBundles = new ConcurrentHashMap<Bundle, T>(8); /** lazy bundles (potentially invalid) */ private final ConcurrentMap<Bundle, Boolean> lazyBundles = new ConcurrentHashMap<Bundle, Boolean>( 8); /** * Queue of bundles that have been activated and validated and should be removed from the lazy * map. This is needed so that promoted bundles do not go unseen by threads using the method at * that point. */ private final List<Bundle> promotionQueue = new ArrayList<Bundle>(4); /** counter used for determining the promotion thread */ private volatile AtomicInteger threadCounter = new AtomicInteger(); private final Condition condition; private final Activator<T> activator; LazyBundleRegistry(Condition promotionCondition, Activator<T> activator) { this.condition = promotionCondition; this.activator = activator; } void add(Bundle bundle, boolean isLazy, boolean applyCondition) { if (isLazy) { lazyBundles.put(bundle, Boolean.valueOf(applyCondition)); } else { activeBundles.put(bundle, activator.activate(bundle)); } } boolean remove(Bundle bundle) { boolean value = false; value = (activeBundles.remove(bundle) != null); value |= (lazyBundles.remove(bundle) != null); synchronized (promotionQueue) { value |= promotionQueue.remove(bundle); } return value; } /** * Applies an operation on all the bundles. To cope with concurrent environment, the class uses * several queues so that lazy bundles that get activated, do not get ignored by threads hitting * the method right at that point. * * @param <V> * @param action * @return */ <V> V apply(Operation<T, V> action) throws Exception { // count running thread threadCounter.incrementAndGet(); try { // check active bundles for (Iterator<T> i = activeBundles.values().iterator(); i.hasNext();) { T result = i.next(); V value = action.operate(result); if (value != null) { return value; } } // nothing found, look into lazy bundles for (Iterator<Map.Entry<Bundle, Boolean>> i = lazyBundles.entrySet().iterator(); i .hasNext();) { Entry<Bundle, Boolean> entry = i.next(); Bundle bundle = entry.getKey(); Boolean applyCondition = entry.getValue(); if (Boolean.FALSE.equals(applyCondition) || (Boolean.TRUE.equals(applyCondition) && condition.pass(bundle))) { // promote bundle T result = activeBundles.putIfAbsent(bundle, activator.activate(bundle)); if (result == null) { result = activeBundles.get(bundle); synchronized (promotionQueue) { promotionQueue.add(bundle); } } if (result != null) { V value = action.operate(result); if (value != null) { return value; } } } else { // the bundle is not compatible, remove it asap lazyBundles.remove(bundle); } } // nothing found return null; } // cleanup the promotion queue finally { synchronized (promotionQueue) { // the last thread accessing the method, does the cleanup // this way, we know other threads will pick the most up to date state // of the concurrent map if (threadCounter.decrementAndGet() == 0) { for (Bundle bundle : promotionQueue) { // remove bundle from the lazy map // but check that it wasn't removed in the meantime // by #remove(Bundle) if (lazyBundles.remove(bundle) == null) { activeBundles.remove(bundle); } } } promotionQueue.clear(); } } } public void clear() { promotionQueue.clear(); lazyBundles.clear(); activeBundles.clear(); } }