Statistics
| Revision:

svn-gvsig-desktop / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / core / gt2 / factory / FactoryRegistry.java @ 2943

History | View | Annotate | Download (25.8 KB)

1
/*
2
 * Geotools 2 - OpenSource mapping toolkit
3
 * (C) 2005, Geotools Project Managment Committee (PMC)
4
 *
5
 *    This library is free software; you can redistribute it and/or
6
 *    modify it under the terms of the GNU Lesser General Public
7
 *    License as published by the Free Software Foundation; either
8
 *    version 2.1 of the License, or (at your option) any later version.
9
 *
10
 *    This library is distributed in the hope that it will be useful,
11
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 *    Lesser General Public License for more details.
14
 *
15
 *    You should have received a copy of the GNU Lesser General Public
16
 *    License along with this library; if not, write to the Free Software
17
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 */
19
package com.iver.cit.gvsig.fmap.core.gt2.factory;
20

    
21
// J2SE dependencies
22
import java.util.ArrayList;
23
import java.util.Collection;
24
import java.util.Comparator;
25
import java.util.HashSet;
26
import java.util.Iterator;
27
import java.util.List;
28
import java.util.Map;
29
import java.util.Set;
30
import java.util.logging.Level;
31
import java.util.logging.LogRecord;
32
import java.util.logging.Logger;
33

    
34
import javax.imageio.spi.ServiceRegistry;
35

    
36
import org.geotools.resources.Utilities;
37

    
38

    
39
/**
40
 * A registry for factories, organized by categories (usualy by <strong>interface</strong>).
41
 * <p>
42
 * For example <code>{@link org.opengis.referencing.crs.CRSFactory}.class</code> is a category,
43
 * and <code>{@link org.opengis.referencing.operation.MathTransformFactory}.class</code>
44
 * is an other category.
45
 * </p>
46
 * <p>
47
 * For each category, implementations are registered in a file placed in the
48
 * {@code META-INF/services/} directory, as specified in the {@link ServiceRegistry}
49
 * javadoc. Those files are usually bundled into the JAR file distributed by the vendor.
50
 * If the same {@code META-INF/services/} file appears many time in different JARs,
51
 * they are processed as if their content were merged.
52
 * </p>
53
 * <p>
54
 * Example use: <pre><code>
55
 * Set categories = Collections.singleton(new Class[] {MathTransformProvider.class});
56
 * FactoryRegistry registry = new FactoryRegistry(categories);
57
 * 
58
 * // get the providers
59
 * Iterator providers = registry.getProviders(MathTransformProvider.class)
60
 * </code></pre>
61
 * </p>
62
 * <p>
63
 * <strong>NOTE: This class is not thread safe</strong>. Users are responsable
64
 * for synchronisation. This is usually done in an utility class wrapping this
65
 * service registry (e.g. {@link org.geotools.referencing.FactoryFinder}).
66
 * </p>
67
 * @version $Id: FactoryRegistry.java 2943 2005-09-22 11:27:52Z fjp $
68
 * @author Martin Desruisseaux
69
 * @author Richard Gould
70
 * @author Jody Garnett
71
 *
72
 * @see org.geotools.referencing.FactoryFinder
73
 */
74
public class FactoryRegistry extends ServiceRegistry {
75
    /**
76
     * Filters only the factories that are {@linkplain OptionalFactory#isReady ready}.
77
     */
78
    private static final Filter FILTER = new Filter() {
79
        public boolean filter(final Object provider) {
80
            return !(provider instanceof OptionalFactory) || ((OptionalFactory) provider).isReady();
81
        }
82
    };
83

    
84
    /**
85
     * Constructs a new registry for the specified categories.
86
     *
87
     * @param categories The categories.
88
     */
89
    public FactoryRegistry(final Collection categories) {
90
        // TODO: remove the cast when we will be allowed to compile for J2SE 1.5.
91
        super((Iterator) categories.iterator());
92
    }
93

    
94
    /**
95
     * Returns the providers in the registry for the specified category. Providers that are
96
     * not {@linkplain OptionalFactory#isReady ready} will be ignored. This method will
97
     * {@linkplain #scanForPlugins scan for plugins} the first time it is invoked for the
98
     * given category.
99
     *
100
     * @param category The category to look for. Must be an interface class
101
     *                 (not the actual implementation class).
102
     * @return Factories ready to use for the specified category.
103
     */
104
    public Iterator getServiceProviders(final Class category) {
105
        Iterator iterator = getServiceProviders(category, FILTER, true);
106
        if (!iterator.hasNext()) {
107
            /*
108
             * No plugin. This method is probably invoked the first time for the specified
109
             * category, otherwise we should have found at least the Geotools implementation.
110
             * Scans the plugin now, but for this category only.
111
             */
112
            for (final Iterator it=getClassLoaders().iterator(); it.hasNext();) {
113
                scanForPlugins((ClassLoader) it.next(), category);
114
            }
115
            iterator = getServiceProviders(category, FILTER, true);
116
        }
117
        return iterator;
118
    }
119

    
120
    /**
121
     * Returns the first provider in the registry for the specified category, using the specified
122
     * map of hints (if any). This method may {@linkplain #scanForPlugins scan for plugins} the
123
     * first time it is invoked. Except as a result of this scan, no new provider instance is
124
     * created by the default implementation of this method. The {@link FactoryCreator} class
125
     * change this behavior however.
126
     *
127
     * @param  category The category to look for. Must be an interface class
128
     *                  (not the actual implementation class).
129
     * @param  filter   An optional filter, or {@code null} if none. This is used for example in
130
     *                  order to select the first factory for some {@linkplain
131
     *                  org.opengis.referencing.AuthorityFactory#getAuthority authority}.
132
     * @param  hints    A {@linkplain Hints map of hints}, or {@code null} if none.
133
     * @return A factory {@linkplain OptionalFactory#isReady ready} to use for the specified
134
     *         category and hints. The returns type is {@code Object} instead of {@link Factory}
135
     *         because the factory implementation doesn't need to be a Geotools one.
136
     * @throws FactoryNotFoundException if no factory was found for the specified category, filter
137
     *         and hints.
138
     * @throws FactoryRegistryException if a factory can't be returned for some othe reason.
139
     *
140
     * @see #getServiceProviders
141
     * @see FactoryCreator#getServiceProvider
142
     */
143
    public Object getServiceProvider(final Class category, final Filter filter, final Hints hints)
144
            throws FactoryRegistryException
145
    {
146
        Class implementation = null;
147
        if (hints!=null && !hints.isEmpty()) {
148
            final Hints.Key key = Hints.Key.getKeyForCategory(category);
149
            if (key != null) {
150
                final Object hint = hints.get(key);
151
                if (category.isInstance(hint)) {
152
                    /*
153
                     * The factory implementation was given explicitly by the user.
154
                     * Nothing to do; we are done.
155
                     */
156
                    return hint;
157
                }
158
                if (hint instanceof Class[]) {
159
                    /*
160
                     * The user accepts many implementation classes. Tries all of them in the
161
                     * preference order given by the user. The last class will be tried using
162
                     * the "normal" path (oustide the loop) in order to get the error message
163
                     * in case of failure.
164
                     */
165
                    final Class[] types = (Class[]) hint;
166
                    for (int i=0; i<types.length-1; i++) {
167
                        Object candidate = getServiceProvider(category, types[i], filter, hints);
168
                        if (candidate != null) {
169
                            return candidate;
170
                        }
171
                    }
172
                    if (types.length != 0) {
173
                        implementation = types[types.length-1]; // Last try to be done below.
174
                    }
175
                } else {
176
                    implementation = (Class) hint;
177
                }
178
            }
179
        }
180
        final Object candidate = getServiceProvider(category, implementation, filter, hints);
181
        if (candidate != null) {
182
            return candidate;
183
        }
184
        // TODO: provides localized messages.
185
        final String message;
186
        if (implementation == null) {
187
            message = "No factory found for category \"" + Utilities.getShortName(category) + "\".";
188
        } else {
189
            message = "No factory found for implementation \"" +
190
                      Utilities.getShortName(implementation) +"\".";
191
        }
192
        throw new FactoryNotFoundException(message);
193
    }
194

    
195
    /**
196
     * Search the first implementation in the registery matching the specified conditions.
197
     * This method do not creates new instance if no matching factory is found.
198
     *
199
     * @param  category       The category to look for. Must be an interface class.
200
     * @param  implementation The desired class for the implementation, or {@code null} if none.
201
     * @param  filter         An optional filter, or {@code null} if none.
202
     * @param  hints          A {@linkplain Hints map of hints}, or {@code null} if none.
203
     * @return A factory for the specified category and hints, or {@code null} if none.
204
     */
205
    private Object getServiceProvider(final Class category, final Class implementation,
206
                                      final Filter filter,  final Hints hints)
207
    {
208
        for (final Iterator it=getServiceProviders(category); it.hasNext();) {
209
            final Object candidate = it.next();
210
            if (filter!=null && !filter.filter(candidate)) {
211
                continue;
212
            }
213
            if (implementation!=null && !implementation.isInstance(candidate)) {
214
                continue;
215
            }
216
            if (hints != null) {
217
                if (candidate instanceof Factory) {
218
                    if (!isAcceptable((Factory) candidate, hints, null)) {
219
                        continue;
220
                    }
221
                }
222
                if (!isAcceptable(candidate, hints)) {
223
                    continue;
224
                }
225
            }
226
            return candidate;
227
        }
228
        return null;
229
    }
230

    
231
    /**
232
     * Returns {@code true} is the specified {@code factory} meets the requirements specified by a
233
     * map of {@code hints}.
234
     *
235
     * @param factory     The factory to checks.
236
     * @param hints       The user requirements.
237
     * @param alreadyDone Should be {@code null} except on recursive calls (for internal use only).
238
     * @return {@code true} if the {@code factory} meets the user requirements.
239
     */
240
    private boolean isAcceptable(final Factory factory, final Hints hints, Set alreadyDone) {
241
        for (final Iterator it=factory.getImplementationHints().entrySet().iterator(); it.hasNext();) {
242
            final Map.Entry entry = (Map.Entry) it.next();
243
            final Object    value = entry.getValue();
244
            final Object expected = hints.get(entry.getKey());
245
            if (expected != null) {
246
                /*
247
                 * We have found a hint that matter. Check if the
248
                 * available factory meets the user's criterions.
249
                 */
250
                if (expected instanceof Class) {
251
                    if (!((Class) expected).isInstance(value)) {
252
                        return false;
253
                    }
254
                } else if (expected instanceof Class[]) {
255
                    final Class[] types = (Class[]) expected;
256
                    int i=0;
257
                    do if (i >= types.length) return false;
258
                    while (!types[i++].isInstance(value));
259
                } else if (!expected.equals(value)) {
260
                    return false;
261
                }
262
            }
263
            // User check (overridable).
264
            if (!isAcceptable(value, hints)) {
265
                return false;
266
            }
267
            /*
268
             * Check recursively in factory depencies, if any. The 'alreadyDone' set is a safety
269
             * against cyclic dependencies, in order to protect ourself against never-ending loops.
270
             */
271
            if (value instanceof Factory) {
272
                if (alreadyDone == null) {
273
                    alreadyDone = new HashSet();
274
                }
275
                if (!alreadyDone.contains(value)) {
276
                    alreadyDone.add(factory);
277
                    if (!isAcceptable((Factory) value, hints, alreadyDone)) {
278
                        return false;
279
                    }
280
                }
281
            }
282
        }
283
        return true;
284
    }
285

    
286
    /**
287
     * Returns {@code true} if the specified {@code provider} meets the requirements specified by a
288
     * map of {@code hints}. This method is invoked automatically when the {@code provider} is known
289
     * to meets standard requirements.
290
     * <br><br>
291
     * The default implementation always returns {@code true}. Override this method if
292
     * more checks are needed, typically for non-Geotools implementation. For example a
293
     * JTS geometry factory finder may overrides this method in order to check if a
294
     * {@link com.vividsolutions.jts.geom.GeometryFactory} uses the required
295
     * {@link com.vividsolutions.jts.geom.CoordinateSequenceFactory}.
296
     *
297
     * @param provider The provider to checks.
298
     * @param hints    The user requirements.
299
     * @return {@code true} if the {@code provider} meets the user requirements.
300
     */
301
    protected boolean isAcceptable(final Object provider, final Hints hints) {
302
        return true;
303
    }
304

    
305
    /**
306
     * Returns all class loaders to be used for scanning plugins. Current implementation
307
     * returns the following class loaders:
308
     *
309
     * <ul>
310
     *   <li>{@linkplain Class#getClassLoader This object class loader}</li>
311
     *   <li>{@linkplain Thread#getContextClassLoader The thread context class loader}</li>
312
     *   <li>{@linkplain ClassLoader#getSystemClassLoader The system class loader}</li>
313
     * </ul>
314
     *
315
     * The actual number of class loaders may be smaller if redundancies was found.
316
     * If some more classloaders should be scanned, they shall be added into the code
317
     * of this method.
318
     */
319
    public final Set getClassLoaders() {
320
        final Set loaders = new HashSet();
321
        for (int i=0; i<4; i++) {
322
            final ClassLoader loader;
323
            try {
324
                switch (i) {
325
                    case 0:  loader = getClass().getClassLoader();                    break;
326
                    case 1:  loader = FactoryRegistry.class.getClassLoader();         break;
327
                    case 2:  loader = Thread.currentThread().getContextClassLoader(); break;
328
                    case 3:  loader = ClassLoader.getSystemClassLoader();             break;
329
                    // Add any supplementary class loaders here, if needed.
330
                    default: throw new AssertionError(i); // Should never happen.
331
                }
332
            } catch (SecurityException exception) {
333
                // We are not allowed to get a class loader.
334
                // Continue; some other class loader may be available.
335
                continue;
336
            }
337
            loaders.add(loader);
338
        }
339
        /*
340
         * We now have a set of class loaders with duplicated object already removed
341
         * (e.g. system classloader == context classloader). However, we may still
342
         * have an other form of redundancie. A class loader may be the parent of an
343
         * other one. Try to remove those dependencies.
344
         */
345
        final ClassLoader[] asArray = (ClassLoader[]) loaders.toArray(new ClassLoader[loaders.size()]);
346
        for (int i=0; i<asArray.length; i++) {
347
            ClassLoader loader = asArray[i];
348
            try {
349
                while ((loader=loader.getParent()) != null) {
350
                    loaders.remove(loader);
351
                }
352
            } catch (SecurityException exception) {
353
                // We are not allowed to fetch the parent class loader.
354
                // Ignore (some redundancies may remains).
355
            }
356
        }
357
        if (loaders.isEmpty()) {
358
            Logger.getLogger("org.geotools.factory").warning("No class loaders available");
359
        }
360
        return loaders;
361
    }
362

    
363
    /**
364
     * Scans for factory plug-ins on the application class path. This method is
365
     * needed because the application class path can theoretically change, or
366
     * additional plug-ins may become available. Rather than re-scanning the
367
     * classpath on every invocation of the API, the class path is scanned
368
     * automatically only on the first invocation. Clients can call this
369
     * method to prompt a re-scan. Thus this method need only be invoked by
370
     * sophisticated applications which dynamically make new plug-ins
371
     * available at runtime.
372
     */
373
    public void scanForPlugins() {
374
        final Set loaders = getClassLoaders();
375
        for (final Iterator categories=getCategories(); categories.hasNext();) {
376
            final Class category = (Class) categories.next();
377
            for (final Iterator it=loaders.iterator(); it.hasNext();) {
378
                final ClassLoader loader = (ClassLoader) it.next();
379
                    scanForPlugins(loader, category);
380
            }
381
        }
382
    }
383

    
384
    /**
385
     * Scans for factory plug-ins of the given category.
386
     *
387
     * @param loader The class loader to use.
388
     * @param category The category to scan for plug-ins.
389
     *
390
     * @todo localize log and error messages.
391
     */
392
    private void scanForPlugins(final ClassLoader loader, final Class category) {
393
        final Iterator   factories = ServiceRegistry.lookupProviders(category, loader);
394
        final String lineSeparator = System.getProperty("line.separator", "\n");
395
        final StringBuffer message = new StringBuffer();
396
        message.append("Scan for '");
397
        message.append(Utilities.getShortName(category));
398
        message.append("' implementations:");
399
        boolean newServices = false;
400
        while (factories.hasNext()) {
401
            Object factory = factories.next();
402
            final Class factoryClass = factory.getClass();
403
            /*
404
             * If the factory implements more than one interface and an instance were
405
             * already registered, reuse the same instance instead of duplicating it.
406
             */
407
            final Object replacement = getServiceProviderByClass(factoryClass);
408
            if (replacement != null) {
409
                factory = replacement;
410
            }
411
            if (registerServiceProvider(factory, category)) {
412
                /*
413
                 * The factory is now registered. Add it to the message to be logged
414
                 * at the end of this method. We log all factories together in order
415
                 * to produces only one log entry, since some registration (e.g.
416
                 * MathTransformProviders) may be quite extensive.
417
                 */
418
                message.append(lineSeparator);
419
                message.append("  Register ");
420
                message.append(factoryClass.getName());
421
                newServices = true;
422
            }
423
        }
424
        /*
425
         * If a system property was setup, load the class (if not already registered)
426
         * and move it in front of any other factory. This is done for compatibility
427
         * with legacy FactoryFinder implementation.
428
         */
429
        try {
430
            final String classname = System.getProperty(category.getName());
431
            if (classname != null) try {
432
                final Class factoryClass = loader.loadClass(classname);
433
                Object factory = getServiceProviderByClass(factoryClass);
434
                if (factory == null) try {
435
                    factory = factoryClass.newInstance();
436
                    if (registerServiceProvider(factory, category)) {
437
                        message.append(lineSeparator);
438
                        message.append("  Register ");
439
                        message.append(factoryClass.getName());
440
                        message.append(" from system property");
441
                        newServices = true;
442
                    }
443
                } catch (IllegalAccessException exception) {
444
                    // TODO: localize
445
                    throw new FactoryRegistryException("Can't create factory for the \"" +
446
                                classname + "\" system property.", exception);
447
                } catch (InstantiationException exception) {
448
                    // TODO: localize
449
                    throw new FactoryRegistryException("Can't create factory for the \"" +
450
                                classname + "\" system property.", exception);
451
                }
452
                /*
453
                 * Put this factory in front of every other factories (including the ones loaded
454
                 * in previous class loaders, which is why we don't inline this ordering in the
455
                 * above loop). Note: if some factories were not yet registered, they will not
456
                 * be properly ordered. Since this code exists more for compatibility reasons
457
                 * than as a commited API, we ignore this short comming for now.
458
                 */
459
                for (final Iterator it=getServiceProviders(category, false); it.hasNext();) {
460
                    final Object other = it.next();
461
                    if (other != factory) {
462
                        setOrdering(category, factory, other);
463
                    }
464
                }
465
            } catch (ClassNotFoundException exception) {
466
                // The class has not been found, maybe because we are not using the appropriate
467
                // class loader. Ignore (do not thrown an exception), in order to give a chance
468
                // to the caller to invokes this method again with a different class loader.
469
            }
470
        } catch (SecurityException exception) {
471
            // We are not allowed to read property, probably
472
            // because we are running in an applet. Ignore...
473
        }
474
        /*
475
         * Log the list of registered factories.
476
         */
477
        if (newServices) {
478
            final LogRecord record = new LogRecord(Level.CONFIG, message.toString());
479
            record.setSourceClassName(FactoryRegistry.class.getName());
480
            record.setSourceMethodName("scanForPlugins");
481
            Logger.getLogger("org.opengis.factory").log(record);
482
        }
483
    }
484

    
485
    /**
486
     * Set pairwise ordering between all services according a comparator. Calls to
487
     * <code>{@linkplain Comparator#compare compare}(factory1, factory2)</code> should returns:
488
     * <ul>
489
     *   <li>{@code -1} if {@code factory1} is preferred to {@code factory2}</li>
490
     *   <li>{@code +1} if {@code factory2} is preferred to {@code factory1}</li>
491
     *   <li>{@code 0} if there is no preferred order between {@code factory1} and
492
     *       {@code factory2}</li>
493
     * </ul>
494
     *
495
     * @param  category   The category to set ordering.
496
     * @param  comparator The comparator to use for ordering.
497
     * @return {@code true} if at least one ordering setting has been modified as a consequence
498
     *         of this call.
499
     */
500
    public boolean setOrdering(final Class category, final Comparator comparator) {
501
        boolean set = false;
502
        final List previous = new ArrayList();
503
        for (final Iterator it=getServiceProviders(category, false); it.hasNext();) {
504
            final Object f1 = it.next();
505
            for (int i=previous.size(); --i>=0;) {
506
                final Object f2 = previous.get(i);
507
                final int c;
508
                try {
509
                    c = comparator.compare(f1, f2);
510
                } catch (ClassCastException exception) {
511
                    /*
512
                     * This exception is expected if the user-supplied comparator follows strictly
513
                     * the java.util.Comparator specification and has determined that it can't
514
                     * compare the supplied factories. From ServiceRegistry point of view, it just
515
                     * means that the ordering between those factories will stay undeterminated.
516
                     */
517
                    continue;
518
                }
519
                if (c > 0) {
520
                    set |= setOrdering(category, f1, f2);
521
                } else if (c < 0) {
522
                    set |= setOrdering(category, f2, f1);
523
                }
524
            }
525
            previous.add(f1);
526
        }
527
        return set;
528
    }
529

    
530
    /**
531
     * Sets or unsets a pairwise ordering between all services meeting a criterion. For example
532
     * in the CRS framework ({@link org.geotools.referencing.FactoryFinder}), this is used for
533
     * setting ordering between all services provided by two vendors, or for two authorities.
534
     * If one or both services are not currently registered, or if the desired ordering is
535
     * already set/unset, nothing happens and false is returned.
536
     *
537
     * @param base     The base category. Only categories {@linkplain Class#isAssignableFrom
538
     *                 assignable} to {@code base} will be processed.
539
     * @param set      {@code true} for setting the ordering, or {@code false} for unsetting.
540
     * @param service1 filter for the preferred service.
541
     * @param service2 filter for the service to which {@code service1} is preferred.
542
     */
543
    public boolean setOrdering(final Class  base,
544
                               final boolean set,
545
                               final Filter service1,
546
                               final Filter service2)
547
    {
548
        boolean done = false;
549
        for (final Iterator categories=getCategories(); categories.hasNext();) {
550
            final Class category = (Class) categories.next();
551
            if (base.isAssignableFrom(category)) {
552
                Object impl1 = null;
553
                Object impl2 = null;
554
                for (final Iterator it=getServiceProviders(category); it.hasNext();) {
555
                    final Object factory = it.next();
556
                    if (service1.filter(factory)) impl1 = factory;
557
                    if (service2.filter(factory)) impl2 = factory;
558
                    if (impl1!=null && impl2!=null && impl1!=impl2) {
559
                        if (set) done |=   setOrdering(category, impl1, impl2);
560
                        else     done |= unsetOrdering(category, impl1, impl2);
561
                    }
562
                }
563
            }
564
        }
565
        return done;
566
    }
567
}