001: /**
002: *
003: */package clime.messadmin.providers;
004:
005: import java.util.ArrayList;
006: import java.util.Collections;
007: import java.util.Comparator;
008: import java.util.Iterator;
009: import java.util.List;
010: import java.util.Map;
011:
012: import clime.messadmin.providers.spi.BaseProvider;
013:
014: /**
015: * Fetches and caches Services (== Providers)
016: * @author Cédrik LIME
017: */
018: public class ProviderUtils {
019: /**
020: * This is a cache of Providers, keyed by its Interface, and by a ClassLoader.
021: * This enables different WebApps (different ClassLoaders) to have their own set of plugins (same Interface).
022: */
023: private static final Map/*<ClassLoader, Map<Class, List<? extends BaseProvider>>>*/PROVIDERS_CACHE = new SoftHashMap();//FIXME do we need soft values too?
024: private static final Comparator priorityComparator = new Comparator() {
025: /** {@inheritDoc} */
026: public int compare(Object o1, Object o2) {
027: return ((BaseProvider) o1).getPriority()
028: - ((BaseProvider) o2).getPriority();
029: }
030: };
031:
032: /**
033: *
034: */
035: private ProviderUtils() {
036: super ();
037: }
038:
039: /**
040: * Implementation note: keep this method unsynchronized, as it would be a contention point otherwise.
041: * We don't care if the cache is filled multiple times.
042: * @param clazz
043: * @return list of providers for clazz, sorted by priority
044: */
045: public static/*<T extends BaseProvider>*/List/*<T>*/getProviders(
046: final Class/*<T>*/clazz) {
047: return getProviders(clazz, Thread.currentThread()
048: .getContextClassLoader());
049: }
050:
051: public static/*<T extends BaseProvider>*/List/*<T>*/getProviders(
052: final Class/*<T>*/clazz, ClassLoader classLoader) {
053: if (classLoader == null) {
054: classLoader = Thread.currentThread()
055: .getContextClassLoader();
056: }
057: Map providersByInterface = (Map) PROVIDERS_CACHE
058: .get(classLoader);
059: if (providersByInterface == null) {
060: fillProvidersCache(clazz, classLoader);
061: providersByInterface = (Map) PROVIDERS_CACHE
062: .get(classLoader);
063: }
064:
065: List providers = (List) providersByInterface.get(clazz);
066: if (providers == null) {
067: fillProvidersCache(clazz, classLoader);
068: providers = (List) providersByInterface.get(clazz);
069: }
070: return providers;
071: }
072:
073: /**
074: * @param clazz
075: */
076: private static synchronized void fillProvidersCache(
077: final Class/*<? extends BaseProvider>*/clazz,
078: final ClassLoader classLoader) {
079: Map/*<Class, List<? extends BaseProvider>>*/providersByInterface = (Map) PROVIDERS_CACHE
080: .get(classLoader);
081: if (providersByInterface == null) {
082: providersByInterface = new SoftHashMap/*<Class, List<? extends BaseProvider>>*/();
083: // put resulting Map in cache as *last operation* (after filling said Map)
084: PROVIDERS_CACHE.put(classLoader, providersByInterface);
085: }
086:
087: List/*<? extends BaseProvider>*/providers = (List) providersByInterface
088: .get(clazz);
089: if (providers == null) {
090: providers = new ArrayList/*<? extends BaseProvider>*/();
091: Iterator ps = Service.providers(clazz, classLoader);
092: if (!ps.hasNext() && classLoader != clazz.getClassLoader()) {
093: ps = Service.providers(clazz, clazz.getClassLoader());
094: }
095: while (ps.hasNext()) {
096: try {
097: BaseProvider provider = (BaseProvider) ps.next();
098: providers.add(provider);
099: } catch (RuntimeException rte) {
100: // error while fetching provider; skipping
101: System.err
102: .println("ERROR while fetching MessAdmin Provider (skipped): "
103: + rte.getLocalizedMessage());
104: } catch (LinkageError le) {
105: // error while fetching provider; skipping
106: System.err
107: .println("ERROR while fetching MessAdmin Provider (skipped): "
108: + le.getLocalizedMessage());
109: }
110: }
111: Collections.sort(providers, priorityComparator);
112: // put resulting List in cache as *last operation* (after filling said List)
113: providersByInterface.put(clazz, providers);
114: }
115: }
116:
117: /**
118: * Clear this service's provider cache.
119: *
120: * <p> After invoking this method, subsequent invocations of the
121: * <code>iterator</code> method will lazily look up and instantiate
122: * providers from scratch, just as is done by a newly-created instance of
123: * this class.
124: *
125: * <p> This method is intended for use in situations in which new providers
126: * can be installed into a running Java virtual machine.
127: */
128: public static void reload() {
129: PROVIDERS_CACHE.clear();
130: }
131:
132: /**
133: * Clear this service's provider cache for the current ClassLoader.
134: *
135: * <p> After invoking this method, subsequent invocations of the
136: * <code>iterator</code> method will lazily look up and instantiate
137: * providers from scratch, just as is done by a newly-created instance of
138: * this class.
139: *
140: * <p> This method is intended for use in situations in which new providers
141: * can be installed into a running Java virtual machine.
142: */
143: public static void deregisterCurrent() {
144: PROVIDERS_CACHE.remove(Thread.currentThread()
145: .getContextClassLoader());
146: }
147:
148: /**
149: * Finalizes this object prior to garbage collection. The
150: * <code>deregisterAll</code> method is called to deregister all
151: * currently registered service providers. This method should not
152: * be called from application code.
153: *
154: * @exception Throwable if an error occurs during superclass
155: * finalization.
156: * @see java.lang.Object#finalize()
157: */
158: protected void finalize() throws Throwable {
159: reload();
160: super.finalize();
161: }
162: }
|