001: /*
002: * PluginManager.java
003: *
004: * Version: $Revision: 1865 $
005: *
006: * Date: $Date: 2007-04-23 05:28:41 -0500 (Mon, 23 Apr 2007) $
007: *
008: * Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
009: * Institute of Technology. All rights reserved.
010: *
011: * Redistribution and use in source and binary forms, with or without
012: * modification, are permitted provided that the following conditions are
013: * met:
014: *
015: * - Redistributions of source code must retain the above copyright
016: * notice, this list of conditions and the following disclaimer.
017: *
018: * - Redistributions in binary form must reproduce the above copyright
019: * notice, this list of conditions and the following disclaimer in the
020: * documentation and/or other materials provided with the distribution.
021: *
022: * - Neither the name of the Hewlett-Packard Company nor the name of the
023: * Massachusetts Institute of Technology nor the names of their
024: * contributors may be used to endorse or promote products derived from
025: * this software without specific prior written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
028: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
030: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
031: * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
032: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
033: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
034: * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
035: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
036: * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
037: * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
038: * DAMAGE.
039: */
040:
041: package org.dspace.core;
042:
043: import java.util.Map;
044: import java.util.HashMap;
045: import java.util.HashSet;
046: import java.util.Enumeration;
047: import java.util.List;
048: import java.util.ArrayList;
049: import java.util.Collection;
050: import java.util.Iterator;
051: import java.util.regex.Pattern;
052: import java.util.regex.Matcher;
053: import java.lang.reflect.InvocationTargetException;
054: import java.lang.reflect.Array;
055: import java.io.BufferedReader;
056: import java.io.FileReader;
057: import java.io.File;
058: import java.io.IOException;
059:
060: import org.apache.log4j.Logger;
061:
062: /**
063: * The Plugin Manager is a very simple component container. It creates and
064: * organizes components (plugins), and helps select a plugin in the cases
065: * where there are many possible choices. It also gives some limited
066: * control over the lifecycle of a plugin. It manages three different types
067: * (usage patterns) of plugins:
068: * <p>
069: * <ol><li> Singleton Plugin
070: * <br> There is only one implementation class for the plugin. It is indicated
071: * in the configuration. This type of plugin chooses an implementations of
072: * a service, for the entire system, at configuration time. Your
073: * application just fetches the plugin for that interface and gets the
074: * configured-in choice.
075: *
076: * <p><li> Sequence Plugins
077: * <br> You need a sequence or series of plugins, to implement a mechanism like
078: * StackableAuthenticationMethods or a pipeline, where each plugin is
079: * called in order to contribute its implementation of a process to the
080: * whole.
081: * <p><li> Named Plugins
082: * <br> Use a named plugin when the application has to choose one plugin
083: * implementation out of many available ones. Each implementation is bound
084: * to one or more names (symbolic identifiers) in the configuration.
085: * </ol><p>
086: * The name is just a <code>String</code> to be associated with the
087: * combination of implementation class and interface. It may contain
088: * any characters except for comma (,) and equals (=). It may contain
089: * embedded spaces. Comma is a special character used to separate
090: * names in the configuration entry.
091: *
092: * @author Larry Stone
093: * @version $Revision: 1865 $
094: * @see SelfNamedPlugin
095: */
096: public class PluginManager {
097: /** log4j category */
098: private static Logger log = Logger.getLogger(PluginManager.class);
099:
100: /**
101: * Prefixes of names of properties to look for in DSpace Configuration
102: */
103: private static final String SINGLE_PREFIX = "plugin.single.";
104: private static final String SEQUENCE_PREFIX = "plugin.sequence.";
105: private static final String NAMED_PREFIX = "plugin.named.";
106: private static final String SELFNAMED_PREFIX = "plugin.selfnamed.";
107: private static final String REUSABLE_PREFIX = "plugin.reusable.";
108:
109: // Separator character (from perl $;) to make "two dimensional"
110: // hashtable key out of interface classname and plugin name;
111: // this character separates the words.
112: private static final String SEP = "\034";
113:
114: // Map of plugin class to "reusable" metric (as Boolean, must be Object)
115: // Key is Class, value is Boolean (true by default).
116: private static HashMap cacheMeCache = new HashMap();
117:
118: // Predicate -- whether or not to cache this class. Ironically,
119: // the cacheability information is itself cached.
120: private static boolean cacheMe(Class implClass) {
121: if (cacheMeCache.containsKey(implClass)) {
122: return ((Boolean) cacheMeCache.get(implClass))
123: .booleanValue();
124: } else {
125: String key = REUSABLE_PREFIX + implClass.getName();
126: boolean reusable = ConfigurationManager.getBooleanProperty(
127: key, true);
128: cacheMeCache.put(implClass, new Boolean(reusable));
129: return reusable;
130: }
131: }
132:
133: /**
134: * Returns an instance of the singleton (single) plugin implementing
135: * the given interface. There must be exactly one single plugin
136: * configured for this interface, otherwise the
137: * <code>PluginConfigurationError</code> is thrown.
138: * <p>
139: * Note that this is the only "get plugin" method which throws an
140: * exception. It is typically used at initialization time to set up
141: * a permanent part of the system so any failure is fatal.
142: *
143: * @param interfaceClass interface Class object
144: * @return instance of plugin
145: * @throws PluginConfigurationError
146: */
147: public static Object getSinglePlugin(Class interfaceClass)
148: throws PluginConfigurationError,
149: PluginInstantiationException {
150: String iname = interfaceClass.getName();
151:
152: // configuration format is prefix.<interface> = <classname>
153: String classname = ConfigurationManager
154: .getProperty(SINGLE_PREFIX + iname);
155: if (classname != null)
156: return getAnonymousPlugin(classname.trim());
157: else
158: throw new PluginConfigurationError(
159: "No Single Plugin configured for interface \""
160: + iname + "\"");
161: }
162:
163: // cache of config data for Sequence Plugins; format its
164: // <interface-name> -> [ <classname>.. ] (value is Array)
165: private static HashMap sequenceConfig = new HashMap();
166:
167: /**
168: * Returns instances of all plugins that implement the interface
169: * intface, in an Array. Returns an empty array if no there are no
170: * matching plugins.
171: * <p>
172: * The order of the plugins in the array is the same as their class
173: * names in the configuration's value field.
174: *
175: * @param intfc interface for which to find plugins.
176: * @return an array of plugin instances; if none are
177: * available an empty array is returned.
178: */
179: public static Object[] getPluginSequence(Class intfc)
180: throws PluginInstantiationException {
181: // cache the configuration for this interface after grovelling it once:
182: // format is prefix.<interface> = <classname>
183: String iname = intfc.getName();
184: String classname[] = null;
185: if (!sequenceConfig.containsKey(iname)) {
186: String val = ConfigurationManager
187: .getProperty(SEQUENCE_PREFIX + iname);
188: if (val == null) {
189: log
190: .warn("No Configuration entry found for Sequence Plugin interface="
191: + iname);
192: return new Object[0];
193: }
194: classname = val.trim().split("\\s*,\\s*");
195: sequenceConfig.put(iname, classname);
196: } else
197: classname = (String[]) sequenceConfig.get(iname);
198:
199: Object result[] = (Object[]) Array.newInstance(intfc,
200: classname.length);
201: for (int i = 0; i < classname.length; ++i) {
202: log.debug("Adding Sequence plugin for interface= " + iname
203: + ", class=" + classname[i]);
204: result[i] = getAnonymousPlugin(classname[i]);
205: }
206: return result;
207: }
208:
209: // Map of cached (reusable) single plugin instances - class -> instance.
210: private static HashMap anonymousInstanceCache = new HashMap();
211:
212: // Get possibly-cached plugin instance for un-named plugin,
213: // this is shared by Single and Sequence plugins.
214: private static Object getAnonymousPlugin(String classname)
215: throws PluginInstantiationException {
216: try {
217: Class pluginClass = Class.forName(classname);
218: if (cacheMe(pluginClass)) {
219: Object cached = anonymousInstanceCache.get(pluginClass);
220: if (cached == null) {
221: cached = pluginClass.newInstance();
222: anonymousInstanceCache.put(pluginClass, cached);
223: }
224: return cached;
225: } else
226: return pluginClass.newInstance();
227: } catch (ClassNotFoundException e) {
228: throw new PluginInstantiationException(
229: "Cannot load plugin class: " + e.toString(), e);
230: } catch (InstantiationException e) {
231: throw new PluginInstantiationException(e);
232: } catch (IllegalAccessException e) {
233: throw new PluginInstantiationException(e);
234: }
235: }
236:
237: // Map of named plugin classes, [intfc,name] -> class
238: // Also contains intfc -> "marker" to mark when interface has been loaded.
239: private static HashMap namedPluginClasses = new HashMap();
240:
241: // Map of cached (reusable) named plugin instances, [class,name] -> instance
242: private static HashMap namedInstanceCache = new HashMap();
243:
244: // load and cache configuration data for the given interface.
245: private static void configureNamedPlugin(String iname)
246: throws ClassNotFoundException {
247: int found = 0;
248:
249: /**
250: * First load the class map for this interface (if not done yet):
251: * key is [intfc,name], value is class.
252: * There is ALSO a "marker key" of "intfc" by itself to show we
253: * loaded this intfc's configuration.
254: */
255: if (!namedPluginClasses.containsKey(iname)) {
256: // 1. Get classes named by the configuration. format is:
257: // plugin.named.<INTF> = <CLASS> = <name>, <name> [,] \
258: // <CLASS> = <name>, <name> [ ... ]
259: String namedVal = ConfigurationManager
260: .getProperty(NAMED_PREFIX + iname);
261: if (namedVal != null) {
262: namedVal = namedVal.trim();
263: log.debug("Got Named configuration for interface="
264: + iname + ", config=" + namedVal);
265:
266: // match "<classname> ="
267: Pattern classnameEqual = Pattern
268: .compile("([\\w\\p{Sc}\\.]+)\\s*\\=");
269:
270: int prevEnd = -1;
271: String prevClassName = null;
272: Matcher classMatcher = classnameEqual.matcher(namedVal);
273: while (classMatcher.find()) {
274: if (prevClassName != null)
275: found += installNamedConfigs(iname,
276: prevClassName, namedVal.substring(
277: prevEnd, classMatcher.start())
278: .trim().split("\\s*,\\s*"));
279: prevClassName = classMatcher.group(1);
280: prevEnd = classMatcher.end();
281: }
282: if (prevClassName != null)
283: found += installNamedConfigs(iname, prevClassName,
284: namedVal.substring(prevEnd).trim().split(
285: "\\s*,\\s*"));
286: }
287:
288: // 2. Get Self-named config entries:
289: // format is plugin.selfnamed.<INTF> = <CLASS> , <CLASS> ..
290: String selfNamedVal = ConfigurationManager
291: .getProperty(SELFNAMED_PREFIX + iname);
292: if (selfNamedVal != null) {
293: String classnames[] = selfNamedVal.trim().split(
294: "\\s*,\\s*");
295: for (int i = 0; i < classnames.length; ++i) {
296: try {
297: Class pluginClass = Class
298: .forName(classnames[i]);
299: String names[] = (String[]) pluginClass
300: .getMethod("getPluginNames").invoke(
301: null);
302: if (names == null || names.length == 0)
303: log
304: .error("Self-named plugin class \""
305: + classnames[i]
306: + "\" returned null or empty name list!");
307: else
308: found += installNamedConfigs(iname,
309: classnames[i], names);
310: } catch (NoSuchMethodException e) {
311: log
312: .error("Implementation Class \""
313: + classnames[i]
314: + "\" is not a subclass of SelfNamedPlugin, it has no getPluginNames() method.");
315: } catch (Exception e) {
316: log
317: .error("While configuring self-named plugin: "
318: + e.toString());
319: }
320: }
321: }
322: namedPluginClasses.put(iname, "org.dspace.core.marker");
323: if (found == 0)
324: log.error("No named plugins found for interface="
325: + iname);
326: }
327: }
328:
329: // add info for a named plugin to cache, under all its names.
330: private static int installNamedConfigs(String iname,
331: String classname, String names[])
332: throws ClassNotFoundException {
333: int found = 0;
334: for (int i = 0; i < names.length; ++i) {
335: String key = iname + SEP + names[i];
336: if (namedPluginClasses.containsKey(key))
337: log
338: .error("Name collision in named plugin, implementation class=\""
339: + classname
340: + "\", name=\""
341: + names[i]
342: + "\"");
343: else
344: namedPluginClasses.put(key, classname);
345: log.debug("Got Named Plugin, intfc=" + iname + ", name="
346: + names[i] + ", class=" + classname);
347: ++found;
348: }
349: return found;
350: }
351:
352: /**
353: * Returns an instance of a plugin that implements the interface
354: * intface and is bound to a name matching name. If there is no
355: * matching plugin, it returns null. The names are matched by
356: * String.equals().
357: *
358: * @param intfc the interface class of the plugin
359: * @param name under which the plugin implementation is configured.
360: * @return instance of plugin implementation, or null if there is no match or an error.
361: */
362: public static Object getNamedPlugin(Class intfc, String name)
363: throws PluginInstantiationException {
364: try {
365: String iname = intfc.getName();
366: configureNamedPlugin(iname);
367: String key = iname + SEP + name;
368: String cname = (String) namedPluginClasses.get(key);
369: if (cname == null)
370: log.warn("Cannot find named plugin for interface="
371: + iname + ", name=\"" + name + "\"");
372: else {
373: Class pluginClass = Class.forName(cname);
374: if (cacheMe(pluginClass)) {
375: String nkey = pluginClass.getName() + SEP + name;
376: Object cached = namedInstanceCache.get(nkey);
377: if (cached == null) {
378: log.debug("Creating cached instance of: "
379: + cname + " for interface=" + iname
380: + " pluginName=" + name);
381: cached = pluginClass.newInstance();
382: if (cached instanceof SelfNamedPlugin)
383: ((SelfNamedPlugin) cached)
384: .setPluginInstanceName(name);
385: namedInstanceCache.put(nkey, cached);
386: }
387: return cached;
388: } else {
389: log.debug("Creating UNcached instance of: " + cname
390: + " for interface=" + iname
391: + " pluginName=" + name);
392: Object result = pluginClass.newInstance();
393: if (result instanceof SelfNamedPlugin)
394: ((SelfNamedPlugin) result)
395: .setPluginInstanceName(name);
396: return result;
397: }
398: }
399: } catch (ClassNotFoundException e) {
400: throw new PluginInstantiationException(
401: "Cannot load plugin class: " + e.toString(), e);
402: } catch (InstantiationException e) {
403: throw new PluginInstantiationException(e);
404: } catch (IllegalAccessException e) {
405: throw new PluginInstantiationException(e);
406: }
407:
408: return null;
409: }
410:
411: /**
412: * Returns all of the names under which a named plugin implementing
413: * the interface intface can be requested (with getNamedPlugin()).
414: * The array is empty if there are no matches. Use this to populate
415: * a menu of plugins for interactive selection, or to document what
416: * the possible choices are.
417: * <p>
418: * NOTE: The names are NOT returned in any deterministic order.
419: *
420: * @param intfc plugin interface for which to return names.
421: * @return an array of strings with every name; if none are
422: * available an empty array is returned.
423: */
424: public static String[] getAllPluginNames(Class intfc) {
425: try {
426: String iname = intfc.getName();
427: configureNamedPlugin(iname);
428: String prefix = iname + SEP;
429: ArrayList result = new ArrayList();
430:
431: Iterator ki = namedPluginClasses.keySet().iterator();
432: while (ki.hasNext()) {
433: String key = (String) ki.next();
434: if (key.startsWith(prefix))
435: result.add(key.substring(prefix.length()));
436: }
437: if (result.size() == 0)
438: log
439: .error("Cannot find any names for named plugin, interface="
440: + iname);
441:
442: return (String[]) result.toArray(new String[result.size()]);
443: } catch (ClassNotFoundException e) {
444: return new String[0];
445: }
446: }
447:
448: /**
449: * Tells the Plugin Manager to let go of any references to a
450: * reusable plugin, to prevent it from being given out again and to
451: * allow the object to be garbage-collected. Call this when a
452: * plugin instance must be taken out of circulation.
453: *
454: * @param plugin the object to release, must have been created by
455: * <code>getNamedPlugin</code> etc.
456: */
457: public static void releasePlugin(Object plugin) {
458: forgetInstance(plugin, namedInstanceCache);
459: forgetInstance(plugin, anonymousInstanceCache);
460: }
461:
462: private static void forgetInstance(Object plugin, Map cacheMap) {
463: Collection values = cacheMap.values();
464: Iterator ci = values.iterator();
465: while (ci.hasNext()) {
466: Object val = ci.next();
467: if (val == plugin)
468: values.remove(val);
469: }
470: }
471:
472: /* -----------------------------------------------------------------
473: * Code to check configuration is all below this line
474: * -----------------------------------------------------------------
475: */
476:
477: // true if classname is valid and loadable.
478: private static boolean checkClassname(String iname, String msg) {
479: try {
480: Class intf = Class.forName(iname);
481: return true;
482: } catch (ClassNotFoundException ce) {
483: log.error("No class definition found for " + msg + ": \""
484: + iname + "\"");
485: }
486: return false;
487: }
488:
489: // true if classname is loadable AND is subclass of SelfNamedPlugin
490: private static boolean checkSelfNamed(String iname) {
491: try {
492: if (!checkSelfNamed(Class.forName(iname)))
493: log
494: .error("The class \""
495: + iname
496: + "\" is NOT a subclass of SelfNamedPlugin but it should be!");
497: } catch (ClassNotFoundException ce) {
498: log
499: .error("No class definition found for self-named class interface: \""
500: + iname + "\"");
501: }
502: return false;
503: }
504:
505: // recursively climb superclass stack until we find SelfNamedPlugin
506: private static boolean checkSelfNamed(Class cls) {
507: Class sup = cls.getSuperclass();
508: if (sup == null)
509: return false;
510: else if (sup.equals(SelfNamedPlugin.class))
511: return true;
512: else
513: return checkSelfNamed(sup);
514: }
515:
516: // check named-plugin names by interface -- call the usual
517: // configuration and let it find missing or duplicate names.
518: private static void checkNames(String iname) {
519: try {
520: configureNamedPlugin(iname);
521: } catch (ClassNotFoundException ce) {
522: // bogus classname should be old news by now.
523: }
524: }
525:
526: /**
527: * Validate the entries in the DSpace Configuration relevant to
528: * PluginManager. Look for inconsistencies, illegal syntax, etc.
529: * Announce violations with "log.error" so they appear in the log
530: * or in the standard error stream if this is run interactively.
531: * <ul>
532: * <li>Look for duplicate keys (by parsing the config file)
533: * <li>Interface in plugin.single, plugin.sequence, plugin.named, plugin.selfnamed is valid.
534: * <li>Classname in plugin.reusable exists and matches a plugin config.
535: * <li>Classnames in config values exist.
536: * <li>Classnames in plugin.selfnamed loads and is subclass of <code>SelfNamedPlugin</code>
537: * <li>Implementations of named plugin have no name collisions.
538: * <li>Named plugin entries lacking names.
539: * </ul>
540: */
541: public static void checkConfiguration() throws IOException {
542: /* XXX TODO: (maybe) test that implementation class is really a
543: * subclass or impl of the plugin "interface"
544: */
545:
546: // tables of config keys for each type of config line:
547: Map singleKey = new HashMap();
548: Map sequenceKey = new HashMap();
549: Map namedKey = new HashMap();
550: Map selfnamedKey = new HashMap();
551: Map reusableKey = new HashMap();
552:
553: // 1. First pass -- grovel the actual config file to check for
554: // duplicate keys, since Properties class hides them from us.
555: // Also build lists of each type of key, check for misspellings.
556: File config = ConfigurationManager.getConfigurationFile();
557: BufferedReader cr = new BufferedReader(new FileReader(config));
558: String line = null;
559: boolean continued = false;
560: HashMap keyMap = new HashMap();
561: Pattern keyPattern = Pattern.compile("([^\\s\\=\\:]+)");
562: while ((line = cr.readLine()) != null) {
563: line = line.trim();
564: if (line.startsWith("!") || line.startsWith("#"))
565: continued = false;
566: else {
567: if (!continued && line.startsWith("plugin.")) {
568: Matcher km = keyPattern.matcher(line);
569: if (km.find()) {
570: String key = line.substring(0, km.end(1));
571: if (keyMap.containsKey(key))
572: log
573: .error("Duplicate key \""
574: + key
575: + "\" in DSpace configuration file="
576: + config.toString());
577: else
578: keyMap.put(key, key);
579:
580: if (key.startsWith(SINGLE_PREFIX))
581: singleKey.put(key.substring(SINGLE_PREFIX
582: .length()), key);
583: else if (key.startsWith(SEQUENCE_PREFIX))
584: sequenceKey.put(
585: key.substring(SEQUENCE_PREFIX
586: .length()), key);
587: else if (key.startsWith(NAMED_PREFIX))
588: namedKey.put(key.substring(NAMED_PREFIX
589: .length()), key);
590: else if (key.startsWith(SELFNAMED_PREFIX))
591: selfnamedKey.put(key
592: .substring(SELFNAMED_PREFIX
593: .length()), key);
594: else if (key.startsWith(REUSABLE_PREFIX))
595: reusableKey.put(
596: key.substring(REUSABLE_PREFIX
597: .length()), key);
598: else
599: log
600: .error("Key with unknown prefix \""
601: + key
602: + "\" in DSpace configuration file="
603: + config.toString());
604: }
605: }
606: continued = line.length() > 0
607: && line.charAt(line.length() - 1) == '\\';
608: }
609: }
610:
611: // 1.1 Sanity check, make sure keyMap == set of keys from Configuration
612: Enumeration pne = ConfigurationManager.propertyNames();
613: HashSet pn = new HashSet();
614: while (pne.hasMoreElements()) {
615: String nk = (String) pne.nextElement();
616: if (nk.startsWith("plugin.")) {
617: pn.add(nk);
618: if (!keyMap.containsKey(nk))
619: log
620: .error("Key is in ConfigurationManager.propertyNames() but NOT text crawl: \""
621: + nk + "\"");
622: }
623: }
624: Iterator pi = keyMap.keySet().iterator();
625: while (pi.hasNext()) {
626: String key = (String) pi.next();
627: if (!pn.contains(key))
628: log
629: .error("Key is in text crawl but NOT ConfigurationManager.propertyNames(): \""
630: + key + "\"");
631: }
632:
633: // 2. Build up list of all interfaces and test that they are loadable.
634: // don't bother testing that they are "interface" rather than "class"
635: // since either one will work for the Plugin Manager.
636: ArrayList allInterfaces = new ArrayList();
637: allInterfaces.addAll(singleKey.keySet());
638: allInterfaces.addAll(sequenceKey.keySet());
639: allInterfaces.addAll(namedKey.keySet());
640: allInterfaces.addAll(selfnamedKey.keySet());
641: allInterfaces.addAll(reusableKey.keySet());
642: Iterator ii = allInterfaces.iterator();
643: while (ii.hasNext())
644: checkClassname((String) ii.next(), "key interface or class");
645:
646: // Check implementation classes:
647: // - each class is loadable.
648: // - plugin.selfnamed values are each subclass of SelfNamedPlugin
649: // - save classname in allImpls
650: Map allImpls = new HashMap();
651:
652: // single plugins - just check that it has a valid impl. class
653: ii = singleKey.keySet().iterator();
654: while (ii.hasNext()) {
655: String key = (String) ii.next();
656: String val = ConfigurationManager.getProperty(SINGLE_PREFIX
657: + key);
658: if (val == null)
659: log.error("Single plugin config not found for: "
660: + SINGLE_PREFIX + key);
661: else {
662: val = val.trim();
663: if (checkClassname(val, "implementation class"))
664: allImpls.put(val, val);
665: }
666: }
667:
668: // sequence plugins - all values must be classes
669: ii = sequenceKey.keySet().iterator();
670: while (ii.hasNext()) {
671: String key = (String) ii.next();
672: String val = ConfigurationManager
673: .getProperty(SEQUENCE_PREFIX + key);
674: if (val == null)
675: log.error("Sequence plugin config not found for: "
676: + SEQUENCE_PREFIX + key);
677: else {
678: val = val.trim();
679: String classname[] = val.split("\\s*,\\s*");
680: for (int i = 0; i < classname.length; ++i)
681: if (checkClassname(classname[i],
682: "implementation class"))
683: allImpls.put(classname[i], classname[i]);
684: }
685: }
686:
687: // 3. self-named plugins - grab and check all values
688: // then make sure it is a subclass of SelfNamedPlugin
689: ii = selfnamedKey.keySet().iterator();
690: while (ii.hasNext()) {
691: String key = (String) ii.next();
692: String val = ConfigurationManager
693: .getProperty(SELFNAMED_PREFIX + key);
694: if (val == null)
695: log.error("Selfnamed plugin config not found for: "
696: + SELFNAMED_PREFIX + key);
697: else {
698: val = val.trim();
699: String classname[] = val.split("\\s*,\\s*");
700: for (int i = 0; i < classname.length; ++i)
701: if (checkClassname(classname[i],
702: "selfnamed implementation class")) {
703: allImpls.put(classname[i], classname[i]);
704: checkSelfNamed(classname[i]);
705: }
706: checkNames(key);
707: }
708: }
709:
710: // 4. named plugins - extract the classnames and treat same as sequence.
711: // use named plugin config mechanism to test for duplicates, unnamed.
712: ii = namedKey.keySet().iterator();
713: Pattern classnameEqual = Pattern
714: .compile("([\\w\\p{Sc}\\.]+)\\s*\\=");
715: while (ii.hasNext()) {
716: String key = (String) ii.next();
717: String val = ConfigurationManager.getProperty(NAMED_PREFIX
718: + key);
719: if (val == null)
720: log.error("Named plugin config not found for: "
721: + NAMED_PREFIX + key);
722: else {
723: checkNames(key);
724: val = val.trim();
725: Matcher classMatcher = classnameEqual.matcher(val);
726: while (classMatcher.find()) {
727: String classname = classMatcher.group(1);
728:
729: if (checkClassname(classname,
730: "implementation class"))
731: allImpls.put(classname, classname);
732: }
733: }
734: }
735:
736: // 5. all classes named in Reusable config lines must be other classes.
737: Iterator ri = reusableKey.keySet().iterator();
738: while (ri.hasNext()) {
739: String rk = (String) ri.next();
740: if (!(allImpls.containsKey(rk)))
741: log.error("In plugin.reusable configuration, class \""
742: + rk
743: + "\" is NOT a plugin implementation class.");
744: }
745: }
746:
747: /**
748: * Invoking this class from the command line just runs
749: * <code>checkConfiguration</code> and shows the results.
750: * There are no command-line options.
751: */
752: public static void main(String[] argv) throws Exception {
753: checkConfiguration();
754: }
755: }
|