KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > dspace > core > PluginManager


1 /*
2  * PluginManager.java
3  *
4  * Version: $Revision: 1.3 $
5  *
6  * Date: $Date: 2006/03/27 22:10:28 $
7  *
8  * Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
9  * Institute of Technology. All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions are
13  * met:
14  *
15  * - Redistributions of source code must retain the above copyright
16  * notice, this list of conditions and the following disclaimer.
17  *
18  * - Redistributions in binary form must reproduce the above copyright
19  * notice, this list of conditions and the following disclaimer in the
20  * documentation and/or other materials provided with the distribution.
21  *
22  * - Neither the name of the Hewlett-Packard Company nor the name of the
23  * Massachusetts Institute of Technology nor the names of their
24  * contributors may be used to endorse or promote products derived from
25  * this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
32  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
33  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
34  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
35  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
36  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
37  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
38  * DAMAGE.
39  */

40
41 package org.dspace.core;
42
43 import java.util.Map JavaDoc;
44 import java.util.HashMap JavaDoc;
45 import java.util.HashSet JavaDoc;
46 import java.util.Enumeration JavaDoc;
47 import java.util.List JavaDoc;
48 import java.util.ArrayList JavaDoc;
49 import java.util.Collection JavaDoc;
50 import java.util.Iterator JavaDoc;
51 import java.util.regex.Pattern JavaDoc;
52 import java.util.regex.Matcher JavaDoc;
53 import java.lang.reflect.InvocationTargetException JavaDoc;
54 import java.lang.reflect.Array JavaDoc;
55 import java.io.BufferedReader JavaDoc;
56 import java.io.FileReader JavaDoc;
57 import java.io.File JavaDoc;
58 import java.io.IOException JavaDoc;
59  
60 import org.apache.log4j.Logger;
61
62 /**
63  * The Plugin Manager is a very simple component container. It creates and
64  * organizes components (plugins), and helps select a plugin in the cases
65  * where there are many possible choices. It also gives some limited
66  * control over the lifecycle of a plugin. It manages three different types
67  * (usage patterns) of plugins:
68  * <p>
69  * <ol><li> Singleton Plugin
70  * <br> There is only one implementation class for the plugin. It is indicated
71  * in the configuration. This type of plugin chooses an implementations of
72  * a service, for the entire system, at configuration time. Your
73  * application just fetches the plugin for that interface and gets the
74  * configured-in choice.
75  *
76  * <p><li> Sequence Plugins
77  * <br> You need a sequence or series of plugins, to implement a mechanism like
78  * StackableAuthenticationMethods or a pipeline, where each plugin is
79  * called in order to contribute its implementation of a process to the
80  * whole.
81  * <p><li> Named Plugins
82  * <br> Use a named plugin when the application has to choose one plugin
83  * implementation out of many available ones. Each implementation is bound
84  * to one or more names (symbolic identifiers) in the configuration.
85  * </ol><p>
86  * The name is just a <code>String</code> to be associated with the
87  * combination of implementation class and interface. It may contain
88  * any characters except for comma (,) and equals (=). It may contain
89  * embedded spaces. Comma is a special character used to separate
90  * names in the configuration entry.
91  *
92  * @author Larry Stone
93  * @version $Revision: 1.3 $
94  * @see SelfNamedPlugin
95  */

96 public class PluginManager
97 {
98     /** log4j category */
99     private static Logger log = Logger.getLogger(PluginManager.class);
100
101     /**
102      * Prefixes of names of properties to look for in DSpace Configuration
103      */

104     private static final String JavaDoc SINGLE_PREFIX = "plugin.single.";
105     private static final String JavaDoc SEQUENCE_PREFIX = "plugin.sequence.";
106     private static final String JavaDoc NAMED_PREFIX = "plugin.named.";
107     private static final String JavaDoc SELFNAMED_PREFIX = "plugin.selfnamed.";
108     private static final String JavaDoc REUSABLE_PREFIX = "plugin.reusable.";
109
110     // Separator character (from perl $;) to make "two dimensional"
111
// hashtable key out of interface classname and plugin name;
112
// this character separates the words.
113
private static final String JavaDoc SEP = "\034";
114
115     // Map of plugin class to "reusable" metric (as Boolean, must be Object)
116
// Key is Class, value is Boolean (true by default).
117
private static HashMap JavaDoc cacheMeCache = new HashMap JavaDoc();
118
119     // Predicate -- whether or not to cache this class. Ironically,
120
// the cacheability information is itself cached.
121
private static boolean cacheMe(Class JavaDoc implClass)
122     {
123         if (cacheMeCache.containsKey(implClass))
124         {
125             return ((Boolean JavaDoc)cacheMeCache.get(implClass)).booleanValue();
126         }
127         else
128         {
129             String JavaDoc key = REUSABLE_PREFIX+implClass.getName();
130             boolean reusable = ConfigurationManager.getBooleanProperty(key, true);
131             cacheMeCache.put(implClass, new Boolean JavaDoc(reusable));
132             return reusable;
133         }
134     }
135
136     /**
137      * Returns an instance of the singleton (single) plugin implementing
138      * the given interface. There must be exactly one single plugin
139      * configured for this interface, otherwise the
140      * <code>PluginConfigurationError</code> is thrown.
141      * <p>
142      * Note that this is the only "get plugin" method which throws an
143      * exception. It is typically used at initialization time to set up
144      * a permanent part of the system so any failure is fatal.
145      *
146      * @param interfaceClass interface Class object
147      * @return instance of plugin
148      * @throws PluginConfigurationError
149      */

150     public static Object JavaDoc getSinglePlugin(Class JavaDoc interfaceClass)
151         throws PluginConfigurationError, PluginInstantiationException
152     {
153         String JavaDoc iname = interfaceClass.getName();
154
155         // configuration format is prefix.<interface> = <classname>
156
String JavaDoc classname = ConfigurationManager.getProperty(SINGLE_PREFIX+iname);
157         if (classname != null)
158             return getAnonymousPlugin(classname.trim());
159         else
160             throw new PluginConfigurationError("No Single Plugin configured for interface \""+iname+"\"");
161     }
162
163
164     // cache of config data for Sequence Plugins; format its
165
// <interface-name> -> [ <classname>.. ] (value is Array)
166
private static HashMap JavaDoc sequenceConfig = new HashMap JavaDoc();
167
168     /**
169      * Returns instances of all plugins that implement the interface
170      * intface, in an Array. Returns an empty array if no there are no
171      * matching plugins.
172      * <p>
173      * The order of the plugins in the array is the same as their class
174      * names in the configuration's value field.
175      *
176      * @param intfc interface for which to find plugins.
177      * @return an array of plugin instances; if none are
178      * available an empty array is returned.
179      */

180     public static Object JavaDoc[] getPluginSequence(Class JavaDoc intfc)
181         throws PluginInstantiationException
182     {
183         // cache the configuration for this interface after grovelling it once:
184
// format is prefix.<interface> = <classname>
185
String JavaDoc iname = intfc.getName();
186         String JavaDoc classname[] = null;
187         if (!sequenceConfig.containsKey(iname))
188         {
189             String JavaDoc val = ConfigurationManager.getProperty(SEQUENCE_PREFIX+iname);
190             if (val == null)
191             {
192                 log.warn("No Configuration entry found for Sequence Plugin interface="+iname);
193                 return new Object JavaDoc[0];
194             }
195             classname = val.trim().split("\\s*,\\s*");
196             sequenceConfig.put(iname, classname);
197         }
198         else
199             classname = (String JavaDoc[])sequenceConfig.get(iname);
200
201         Object JavaDoc result[] = (Object JavaDoc[])Array.newInstance(intfc, classname.length);
202         for (int i = 0; i < classname.length; ++i)
203         {
204             log.debug("Adding Sequence plugin for interface= "+iname+", class="+classname[i]);
205             result[i] = getAnonymousPlugin(classname[i]);
206         }
207         return result;
208     }
209
210     // Map of cached (reusable) single plugin instances - class -> instance.
211
private static HashMap JavaDoc anonymousInstanceCache = new HashMap JavaDoc();
212
213     // Get possibly-cached plugin instance for un-named plugin,
214
// this is shared by Single and Sequence plugins.
215
private static Object JavaDoc getAnonymousPlugin(String JavaDoc classname)
216         throws PluginInstantiationException
217     {
218         try
219         {
220             Class JavaDoc pluginClass = Class.forName(classname);
221             if (cacheMe(pluginClass))
222             {
223                 Object JavaDoc cached = anonymousInstanceCache.get(pluginClass);
224                 if (cached == null)
225                 {
226                     cached = pluginClass.newInstance();
227                     anonymousInstanceCache.put(pluginClass, cached);
228                 }
229                 return cached;
230             }
231             else
232                 return pluginClass.newInstance();
233         }
234         catch (ClassNotFoundException JavaDoc e)
235         {
236             throw new PluginInstantiationException("Cannot load plugin class: " +
237                                                    e.toString(), e);
238         }
239         catch (InstantiationException JavaDoc e)
240         {
241             throw new PluginInstantiationException(e);
242         }
243         catch (IllegalAccessException JavaDoc e)
244         {
245             throw new PluginInstantiationException(e);
246         }
247     }
248
249     // Map of named plugin classes, [intfc,name] -> class
250
// Also contains intfc -> "marker" to mark when interface has been loaded.
251
private static HashMap JavaDoc namedPluginClasses = new HashMap JavaDoc();
252
253     // Map of cached (reusable) named plugin instances, [class,name] -> instance
254
private static HashMap JavaDoc namedInstanceCache = new HashMap JavaDoc();
255
256     // load and cache configuration data for the given interface.
257
private static void configureNamedPlugin(String JavaDoc iname)
258         throws ClassNotFoundException JavaDoc
259     {
260         int found = 0;
261
262         /**
263          * First load the class map for this interface (if not done yet):
264          * key is [intfc,name], value is class.
265          * There is ALSO a "marker key" of "intfc" by itself to show we
266          * loaded this intfc's configuration.
267          */

268         if (!namedPluginClasses.containsKey(iname))
269         {
270             // 1. Get classes named by the configuration. format is:
271
// plugin.named.<INTF> = <CLASS> = <name>, <name> [,] \
272
// <CLASS> = <name>, <name> [ ... ]
273
String JavaDoc namedVal = ConfigurationManager.getProperty(NAMED_PREFIX+iname);
274             if (namedVal != null)
275             {
276                 namedVal = namedVal.trim();
277                 log.debug("Got Named configuration for interface="+iname+", config="+namedVal);
278
279                 // match "<classname> ="
280
Pattern JavaDoc classnameEqual = Pattern.compile("([\\w\\p{Sc}\\.]+)\\s*\\=");
281
282                 int prevEnd = -1;
283                 String JavaDoc prevClassName = null;
284                 Matcher JavaDoc classMatcher = classnameEqual.matcher(namedVal);
285                 while (classMatcher.find())
286                 {
287                     if (prevClassName != null)
288                         found += installNamedConfigs(iname, prevClassName,
289                                    namedVal.substring(prevEnd, classMatcher.start()).trim().split("\\s*,\\s*"));
290                     prevClassName = classMatcher.group(1);
291                     prevEnd = classMatcher.end();
292                 }
293                 if (prevClassName != null)
294                     found += installNamedConfigs(iname, prevClassName,
295                                namedVal.substring(prevEnd).trim().split("\\s*,\\s*"));
296             }
297
298             // 2. Get Self-named config entries:
299
// format is plugin.selfnamed.<INTF> = <CLASS> , <CLASS> ..
300
String JavaDoc selfNamedVal = ConfigurationManager.getProperty(SELFNAMED_PREFIX+iname);
301             if (selfNamedVal != null)
302             {
303                 String JavaDoc classnames[] = selfNamedVal.trim().split("\\s*,\\s*");
304                 for (int i = 0; i < classnames.length; ++i)
305                 {
306                     try
307                     {
308                         Class JavaDoc pluginClass = Class.forName(classnames[i]);
309                         String JavaDoc names[] = (String JavaDoc[])pluginClass.getMethod("getPluginNames", null).
310                                                    invoke(null, null);
311                         if (names == null || names.length == 0)
312                             log.error("Self-named plugin class \""+classnames[i]+"\" returned null or empty name list!");
313                         else
314                             found += installNamedConfigs(iname, classnames[i], names);
315                     }
316                     catch (NoSuchMethodException JavaDoc e)
317                     {
318                         log.error("Implementation Class \""+classnames[i]+"\" is not a subclass of SelfNamedPlugin, it has no getPluginNames() method.");
319                     }
320                     catch (Exception JavaDoc e)
321                     {
322                         log.error("While configuring self-named plugin: " + e.toString());
323                     }
324                 }
325             }
326             namedPluginClasses.put(iname, "org.dspace.core.marker");
327             if (found == 0)
328                 log.error("No named plugins found for interface="+iname);
329         }
330     }
331
332     // add info for a named plugin to cache, under all its names.
333
private static int installNamedConfigs(String JavaDoc iname, String JavaDoc classname, String JavaDoc names[])
334         throws ClassNotFoundException JavaDoc
335     {
336         int found = 0;
337         for (int i = 0; i < names.length; ++i)
338         {
339             String JavaDoc key = iname+SEP+names[i];
340             if (namedPluginClasses.containsKey(key))
341                 log.error("Name collision in named plugin, implementation class=\""+classname+
342                             "\", name=\""+names[i]+"\"");
343             else
344                 namedPluginClasses.put(key, classname);
345             log.debug("Got Named Plugin, intfc="+iname+", name="+names[i]+", class="+classname);
346             ++found;
347         }
348         return found;
349     }
350
351     /**
352      * Returns an instance of a plugin that implements the interface
353      * intface and is bound to a name matching name. If there is no
354      * matching plugin, it returns null. The names are matched by
355      * String.equals().
356      *
357      * @param intfc the interface class of the plugin
358      * @param name under which the plugin implementation is configured.
359      * @return instance of plugin implementation, or null if there is no match or an error.
360      */

361     public static Object JavaDoc getNamedPlugin(Class JavaDoc intfc, String JavaDoc name)
362          throws PluginInstantiationException
363     {
364         try
365         {
366             String JavaDoc iname = intfc.getName();
367             configureNamedPlugin(iname);
368             String JavaDoc key = iname + SEP + name;
369             String JavaDoc cname = (String JavaDoc)namedPluginClasses.get(key);
370             if (cname == null)
371                 log.warn("Cannot find named plugin for interface="+iname+", name=\""+name+"\"");
372             else
373             {
374                 Class JavaDoc pluginClass = Class.forName(cname);
375                 if (cacheMe(pluginClass))
376                 {
377                     String JavaDoc nkey = pluginClass.getName() + SEP + name;
378                     Object JavaDoc cached = namedInstanceCache.get(nkey);
379                     if (cached == null)
380                     {
381                         log.debug("Creating cached instance of: " + cname +
382                                   " for interface=" + iname +
383                                   " pluginName=" + name );
384                         cached = pluginClass.newInstance();
385                         if (cached instanceof SelfNamedPlugin)
386                             ((SelfNamedPlugin)cached).setPluginInstanceName(name);
387                         namedInstanceCache.put(nkey, cached);
388                     }
389                     return cached;
390                 }
391                 else
392                 {
393                     log.debug("Creating UNcached instance of: " + cname +
394                           " for interface=" + iname +
395                           " pluginName=" + name );
396                     Object JavaDoc result = pluginClass.newInstance();
397                     if (result instanceof SelfNamedPlugin)
398                         ((SelfNamedPlugin)result).setPluginInstanceName(name);
399                     return result;
400                 }
401             }
402         }
403         catch (ClassNotFoundException JavaDoc e)
404         {
405             throw new PluginInstantiationException("Cannot load plugin class: " +
406                                                    e.toString(), e);
407         }
408         catch (InstantiationException JavaDoc e)
409         {
410             throw new PluginInstantiationException(e);
411         }
412         catch (IllegalAccessException JavaDoc e)
413         {
414             throw new PluginInstantiationException(e);
415         }
416
417         return null;
418     }
419
420
421     /**
422      * Returns all of the names under which a named plugin implementing
423      * the interface intface can be requested (with getNamedPlugin()).
424      * The array is empty if there are no matches. Use this to populate
425      * a menu of plugins for interactive selection, or to document what
426      * the possible choices are.
427      * <p>
428      * NOTE: The names are NOT returned in any deterministic order.
429      *
430      * @param intfc plugin interface for which to return names.
431      * @return an array of strings with every name; if none are
432      * available an empty array is returned.
433      */

434     public static String JavaDoc[] getAllPluginNames(Class JavaDoc intfc)
435     {
436         try
437         {
438             String JavaDoc iname = intfc.getName();
439             configureNamedPlugin(iname);
440             String JavaDoc prefix = iname + SEP;
441             ArrayList JavaDoc result = new ArrayList JavaDoc();
442
443             Iterator JavaDoc ki = namedPluginClasses.keySet().iterator();
444             while (ki.hasNext())
445             {
446                 String JavaDoc key = (String JavaDoc)ki.next();
447                 if (key.startsWith(prefix))
448                     result.add(key.substring(prefix.length()));
449             }
450             if (result.size() == 0)
451                 log.error("Cannot find any names for named plugin, interface="+iname);
452
453             return (String JavaDoc[])result.toArray(new String JavaDoc[result.size()]);
454         }
455         catch (ClassNotFoundException JavaDoc e)
456         {
457             return new String JavaDoc[0];
458         }
459     }
460
461     /**
462      * Tells the Plugin Manager to let go of any references to a
463      * reusable plugin, to prevent it from being given out again and to
464      * allow the object to be garbage-collected. Call this when a
465      * plugin instance must be taken out of circulation.
466      *
467      * @param plugin the object to release, must have been created by
468      * <code>getNamedPlugin</code> etc.
469      */

470     public static void releasePlugin(Object JavaDoc plugin)
471     {
472         forgetInstance(plugin, namedInstanceCache);
473         forgetInstance(plugin, anonymousInstanceCache);
474     }
475
476     private static void forgetInstance(Object JavaDoc plugin, Map JavaDoc cacheMap)
477     {
478         Collection JavaDoc values = cacheMap.values();
479         Iterator JavaDoc ci = values.iterator();
480         while (ci.hasNext())
481         {
482             Object JavaDoc val = ci.next();
483             if (val == plugin)
484                 values.remove(val);
485         }
486     }
487
488     /* -----------------------------------------------------------------
489      * Code to check configuration is all below this line
490      * -----------------------------------------------------------------
491      */

492
493     // true if classname is valid and loadable.
494
private static boolean checkClassname(String JavaDoc iname, String JavaDoc msg)
495     {
496         try
497         {
498             Class JavaDoc intf = Class.forName(iname);
499             return true;
500         }
501         catch (ClassNotFoundException JavaDoc ce)
502         {
503             log.error("No class definition found for "+msg+": \""+iname+"\"");
504         }
505         return false;
506     }
507
508     // true if classname is loadable AND is subclass of SelfNamedPlugin
509
private static boolean checkSelfNamed(String JavaDoc iname)
510     {
511         try
512         {
513             if (!checkSelfNamed(Class.forName(iname)))
514                 log.error("The class \""+iname+"\" is NOT a subclass of SelfNamedPlugin but it should be!");
515         }
516         catch (ClassNotFoundException JavaDoc ce)
517         {
518             log.error("No class definition found for self-named class interface: \""+iname+"\"");
519         }
520         return false;
521     }
522
523     // recursively climb superclass stack until we find SelfNamedPlugin
524
private static boolean checkSelfNamed(Class JavaDoc cls)
525     {
526         Class JavaDoc sup = cls.getSuperclass();
527         if (sup == null)
528             return false;
529         else if (sup.equals(SelfNamedPlugin.class))
530             return true;
531         else
532             return checkSelfNamed(sup);
533     }
534
535     // check named-plugin names by interface -- call the usual
536
// configuration and let it find missing or duplicate names.
537
private static void checkNames(String JavaDoc iname)
538     {
539         try
540         {
541             configureNamedPlugin(iname);
542         }
543         catch (ClassNotFoundException JavaDoc ce)
544         {
545             // bogus classname should be old news by now.
546
}
547     }
548
549     /**
550      * Validate the entries in the DSpace Configuration relevant to
551      * PluginManager. Look for inconsistencies, illegal syntax, etc.
552      * Announce violations with "log.error" so they appear in the log
553      * or in the standard error stream if this is run interactively.
554      * <ul>
555      * <li>Look for duplicate keys (by parsing the config file)
556      * <li>Interface in plugin.single, plugin.sequence, plugin.named, plugin.selfnamed is valid.
557      * <li>Classname in plugin.reusable exists and matches a plugin config.
558      * <li>Classnames in config values exist.
559      * <li>Classnames in plugin.selfnamed loads and is subclass of <code>SelfNamedPlugin</code>
560      * <li>Implementations of named plugin have no name collisions.
561      * <li>Named plugin entries lacking names.
562      * </ul>
563      */

564     public static void checkConfiguration()
565         throws IOException JavaDoc
566     {
567         /* XXX TODO: (maybe) test that implementation class is really a
568          * subclass or impl of the plugin "interface"
569          */

570          
571         // tables of config keys for each type of config line:
572
Map JavaDoc singleKey = new HashMap JavaDoc();
573         Map JavaDoc sequenceKey = new HashMap JavaDoc();
574         Map JavaDoc namedKey = new HashMap JavaDoc();
575         Map JavaDoc selfnamedKey = new HashMap JavaDoc();
576         Map JavaDoc reusableKey = new HashMap JavaDoc();
577
578         // 1. First pass -- grovel the actual config file to check for
579
// duplicate keys, since Properties class hides them from us.
580
// Also build lists of each type of key, check for misspellings.
581
File JavaDoc config = ConfigurationManager.getConfigurationFile();
582         BufferedReader JavaDoc cr = new BufferedReader JavaDoc(new FileReader JavaDoc(config));
583         String JavaDoc line = null;
584         boolean continued = false;
585         HashMap JavaDoc keyMap = new HashMap JavaDoc();
586         Pattern JavaDoc keyPattern = Pattern.compile("([^\\s\\=\\:]+)");
587         while ((line = cr.readLine()) != null)
588         {
589             line = line.trim();
590             if (line.startsWith("!") || line.startsWith("#"))
591                 continued = false;
592             else
593             {
594                 if (!continued && line.startsWith("plugin."))
595                 {
596                     Matcher JavaDoc km = keyPattern.matcher(line);
597                     if (km.find())
598                     {
599                         String JavaDoc key = line.substring(0, km.end(1));
600                         if (keyMap.containsKey(key))
601                             log.error("Duplicate key \""+key+"\" in DSpace configuration file="+config.toString());
602                         else
603                             keyMap.put(key, key);
604
605                         if (key.startsWith(SINGLE_PREFIX))
606                             singleKey.put(key.substring(SINGLE_PREFIX.length()), key);
607                         else if (key.startsWith(SEQUENCE_PREFIX))
608                             sequenceKey.put(key.substring(SEQUENCE_PREFIX.length()), key);
609                         else if (key.startsWith(NAMED_PREFIX))
610                             namedKey.put(key.substring(NAMED_PREFIX.length()), key);
611                         else if (key.startsWith(SELFNAMED_PREFIX))
612                             selfnamedKey.put(key.substring(SELFNAMED_PREFIX.length()), key);
613                         else if (key.startsWith(REUSABLE_PREFIX))
614                             reusableKey.put(key.substring(REUSABLE_PREFIX.length()), key);
615                         else
616                             log.error("Key with unknown prefix \""+key+"\" in DSpace configuration file="+config.toString());
617                     }
618                 }
619                 continued = line.length() > 0 && line.charAt(line.length()-1) == '\\';
620             }
621         }
622
623         // 1.1 Sanity check, make sure keyMap == set of keys from Configuration
624
Enumeration JavaDoc pne = ConfigurationManager.propertyNames();
625         HashSet JavaDoc pn = new HashSet JavaDoc();
626         while (pne.hasMoreElements())
627         {
628             String JavaDoc nk = (String JavaDoc)pne.nextElement();
629             if (nk.startsWith("plugin."))
630             {
631                 pn.add(nk);
632                 if (!keyMap.containsKey(nk))
633                     log.error("Key is in ConfigurationManager.propertyNames() but NOT text crawl: \""+nk+"\"");
634             }
635         }
636         Iterator JavaDoc pi = keyMap.keySet().iterator();
637         while (pi.hasNext())
638         {
639             String JavaDoc key = (String JavaDoc)pi.next();
640             if (!pn.contains(key))
641                 log.error("Key is in text crawl but NOT ConfigurationManager.propertyNames(): \""+key+"\"");
642         }
643
644         // 2. Build up list of all interfaces and test that they are loadable.
645
// don't bother testing that they are "interface" rather than "class"
646
// since either one will work for the Plugin Manager.
647
ArrayList JavaDoc allInterfaces = new ArrayList JavaDoc();
648         allInterfaces.addAll(singleKey.keySet());
649         allInterfaces.addAll(sequenceKey .keySet());
650         allInterfaces.addAll(namedKey.keySet());
651         allInterfaces.addAll(selfnamedKey.keySet());
652         allInterfaces.addAll(reusableKey.keySet());
653         Iterator JavaDoc ii = allInterfaces.iterator();
654         while (ii.hasNext())
655             checkClassname((String JavaDoc)ii.next(), "key interface or class");
656
657         // Check implementation classes:
658
// - each class is loadable.
659
// - plugin.selfnamed values are each subclass of SelfNamedPlugin
660
// - save classname in allImpls
661
Map JavaDoc allImpls = new HashMap JavaDoc();
662          
663         // single plugins - just check that it has a valid impl. class
664
ii = singleKey.keySet().iterator();
665         while (ii.hasNext())
666         {
667             String JavaDoc key = (String JavaDoc)ii.next();
668             String JavaDoc val = ConfigurationManager.getProperty(SINGLE_PREFIX+key);
669             if (val == null)
670                 log.error("Single plugin config not found for: "+SINGLE_PREFIX+key);
671             else
672             {
673                 val = val.trim();
674                 if (checkClassname(val, "implementation class"))
675                     allImpls.put(val, val);
676             }
677         }
678          
679         // sequence plugins - all values must be classes
680
ii = sequenceKey.keySet().iterator();
681         while (ii.hasNext())
682         {
683             String JavaDoc key = (String JavaDoc)ii.next();
684             String JavaDoc val = ConfigurationManager.getProperty(SEQUENCE_PREFIX+key);
685             if (val == null)
686                 log.error("Sequence plugin config not found for: "+SEQUENCE_PREFIX+key);
687             else
688             {
689                 val = val.trim();
690                 String JavaDoc classname[] = val.split("\\s*,\\s*");
691                 for (int i = 0; i < classname.length; ++i)
692                     if (checkClassname(classname[i], "implementation class"))
693                         allImpls.put(classname[i], classname[i]);
694             }
695         }
696
697         // 3. self-named plugins - grab and check all values
698
// then make sure it is a subclass of SelfNamedPlugin
699
ii = selfnamedKey.keySet().iterator();
700         while (ii.hasNext())
701         {
702             String JavaDoc key = (String JavaDoc)ii.next();
703             String JavaDoc val = ConfigurationManager.getProperty(SELFNAMED_PREFIX+key);
704             if (val == null)
705                 log.error("Selfnamed plugin config not found for: "+SELFNAMED_PREFIX+key);
706             else
707             {
708                 val = val.trim();
709                 String JavaDoc classname[] = val.split("\\s*,\\s*");
710                 for (int i = 0; i < classname.length; ++i)
711                     if (checkClassname(classname[i], "selfnamed implementation class"))
712                     {
713                         allImpls.put(classname[i], classname[i]);
714                         checkSelfNamed(classname[i]);
715                     }
716                 checkNames(key);
717             }
718         }
719
720         // 4. named plugins - extract the classnames and treat same as sequence.
721
// use named plugin config mechanism to test for duplicates, unnamed.
722
ii = namedKey.keySet().iterator();
723         Pattern JavaDoc classnameEqual = Pattern.compile("([\\w\\p{Sc}\\.]+)\\s*\\=");
724         while (ii.hasNext())
725         {
726             String JavaDoc key = (String JavaDoc)ii.next();
727             String JavaDoc val = ConfigurationManager.getProperty(NAMED_PREFIX+key);
728             if (val == null)
729                 log.error("Named plugin config not found for: "+NAMED_PREFIX+key);
730             else
731             {
732                 checkNames(key);
733                 val = val.trim();
734                 Matcher JavaDoc classMatcher = classnameEqual.matcher(val);
735                 while (classMatcher.find())
736                 {
737                     String JavaDoc classname = classMatcher.group(1);
738
739                     if (checkClassname(classname, "implementation class"))
740                         allImpls.put(classname, classname);
741                 }
742             }
743         }
744
745         // 5. all classes named in Reusable config lines must be other classes.
746
Iterator JavaDoc ri = reusableKey.keySet().iterator();
747         while (ri.hasNext())
748         {
749             String JavaDoc rk = (String JavaDoc)ri.next();
750             if (!(allImpls.containsKey(rk)))
751                 log.error("In plugin.reusable configuration, class \""+rk+"\" is NOT a plugin implementation class.");
752         }
753     }
754
755     /**
756      * Invoking this class from the command line just runs
757      * <code>checkConfiguration</code> and shows the results.
758      * There are no command-line options.
759      */

760     public static void main(String JavaDoc[] argv) throws Exception JavaDoc
761     {
762         checkConfiguration();
763     }
764 }
765
Popular Tags