KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > cruisecontrol > PluginRegistry


1 /********************************************************************************
2  * CruiseControl, a Continuous Integration Toolkit
3  * Copyright (c) 2001, ThoughtWorks, Inc.
4  * 651 W Washington Ave. Suite 600
5  * Chicago, IL 60661 USA
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * + Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * + Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following
17  * disclaimer in the documentation and/or other materials provided
18  * with the distribution.
19  *
20  * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
21  * names of its contributors may be used to endorse or promote
22  * products derived from this software without specific prior
23  * written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
29  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  ********************************************************************************/

37 package net.sourceforge.cruisecontrol;
38
39 import java.io.IOException JavaDoc;
40 import java.io.Serializable JavaDoc;
41 import java.util.Arrays JavaDoc;
42 import java.util.Collections JavaDoc;
43 import java.util.HashMap JavaDoc;
44 import java.util.Iterator JavaDoc;
45 import java.util.LinkedList JavaDoc;
46 import java.util.List JavaDoc;
47 import java.util.Map JavaDoc;
48 import java.util.Properties JavaDoc;
49
50 import org.apache.log4j.Logger;
51 import org.jdom.Attribute;
52 import org.jdom.Element;
53
54
55 /**
56  * Handles "registering" plugins that will be used by the CruiseControl
57  * configuration file.
58
59  * A PluginRegistry can have a parent registry, which it will query for
60  * a plugin if it's not defined in the registry itself. This is used to
61  * enable projects to have their own plugins and override the classname
62  * for a specific plugin, like the labelincrementer.
63  *
64  * The root-registry contains the default list of plugins, i.e. those
65  * that are already registered like AntBuilder that don't have to be registered
66  * seperately in the configuration file.
67  *
68  * The registry keeps track of the {@link #getPluginConfig(String) plugin configurations}
69  * in order to allow full plugin preconfigurations (default properties + nested elements).
70  *
71  * @see PluginXMLHelper
72  */

73 public final class PluginRegistry implements Serializable JavaDoc {
74
75     private static final Logger LOG = Logger.getLogger(PluginRegistry.class);
76
77     /**
78      * The only instance of the root plugin registry.
79      * This contains the default plugins and the plugins that are defined
80      * external to projects.
81      */

82     private static final PluginRegistry ROOTREGISTRY = loadDefaultPluginRegistry();
83
84     /**
85      * @return PluginRegistry with the ROOTREGISTRY as its parent.
86      */

87     public static PluginRegistry createRegistry() {
88         return createRegistry(ROOTREGISTRY);
89     }
90
91     /**
92      * @return PluginRegistry with the specified registry as its parent.
93      */

94     public static PluginRegistry createRegistry(PluginRegistry parent) {
95         return new PluginRegistry(parent);
96     }
97
98     /**
99      * The parent registry that will be searched for plugin definitions
100      * if they're not defined in the registry itself. May be null.
101      */

102     private final PluginRegistry parentRegistry;
103
104     /**
105      * Map of plugins where the key is the plugin name (e.g. ant) and the value is
106      * the fully qualified classname
107      * (e.g. net.sourceforge.cruisecontrol.builders.AntBuilder).
108      */

109     private final Map JavaDoc plugins = new HashMap JavaDoc();
110
111     /**
112      * Map that holds the DOM element representing the plugin declaration.
113      * Key is the plugin name (as taken from the DOM element),
114      * value is the Element representing the plugin configuration.
115      */

116     private final Map JavaDoc pluginConfigs = new HashMap JavaDoc();
117
118     /**
119      * Creates a new PluginRegistry with no plugins registered, with the given parent registry.
120      * Only used internally for now, Projects should call createRegistry instead.
121      */

122     private PluginRegistry(PluginRegistry parentRegistry) {
123         this.parentRegistry = parentRegistry;
124     }
125
126     /**
127      * @param pluginName The name for the plugin, e.g. ant. Note that plugin
128      * names are always treated as case insensitive, so Ant, ant, and AnT are
129      * all treated as the same plugin.
130      *
131      * @param pluginClassname The fully qualified classname for the
132      * plugin class, e.g. net.sourceforge.cruisecontrol.builders.AntBuilder.
133      */

134     public void register(String JavaDoc pluginName, String JavaDoc pluginClassname) {
135         plugins.put(pluginName.toLowerCase(), pluginClassname);
136     }
137     
138     /**
139      * Registers the given plugin, including plugin configuration.
140      *
141      * @param pluginElement the JDom element that contains the plugin definition.
142      */

143     public void register(Element pluginElement) throws CruiseControlException {
144         String JavaDoc pluginName = pluginElement.getAttributeValue("name").toLowerCase();
145         String JavaDoc pluginClassName = pluginElement.getAttributeValue("classname");
146         if (pluginClassName != null) {
147             register(pluginName, pluginClassName);
148         } else {
149             // should be known plugin, then
150
if (!isPluginRegistered(pluginName)) {
151                 throw new CruiseControlException("Unknown plugin '"
152                         + pluginName + "'; maybe you forgot to specify a classname?");
153             }
154         }
155         
156         Element clonedPluginElement = (Element) pluginElement.clone();
157         clonedPluginElement.removeAttribute("name");
158         clonedPluginElement.removeAttribute("classname");
159         clonedPluginElement.setName(pluginName);
160         if (LOG.isDebugEnabled()) {
161             LOG.debug("storing plugin configuration " + pluginName);
162         }
163         pluginConfigs.put(pluginName, clonedPluginElement);
164     }
165
166     /**
167      * Registers the given plugin in the root registry, so it will be
168      * available to all projects.
169      *
170      */

171     static void registerToRoot(Element pluginElement) throws CruiseControlException {
172         ROOTREGISTRY.register(pluginElement);
173     }
174     
175     /**
176      * Clears all plugin registrations and defaults in the root registry, so they can be re-registered
177      * when reloading the config file. The default-properties are re-read.
178      */

179     static void resetRootRegistry() {
180         ROOTREGISTRY.pluginConfigs.clear();
181         ROOTREGISTRY.plugins.clear();
182         ROOTREGISTRY.plugins.putAll(loadDefaultPluginRegistry().plugins);
183     }
184
185     /**
186      * @return Returns null if no plugin has been registered with the specified
187      * name, otherwise a String representing the fully qualified classname
188      * for the plugin class. Note that plugin
189      * names are always treated as case insensitive, so Ant, ant, and AnT are
190      * all treated as the same plugin.
191      * Note: a parent name->class mapping can be overriden by children registries.
192      */

193     public String JavaDoc getPluginClassname(String JavaDoc pluginName) {
194         pluginName = pluginName.toLowerCase();
195         String JavaDoc className = internalGetPluginClassname(pluginName);
196         if (className == null && parentRegistry != null) {
197             className = parentRegistry.getPluginClassname(pluginName);
198         }
199         return className;
200     }
201
202     /**
203      * @return the class name for this plugin on this registry. May be <code>null</code>
204      * Assumes the pluginName is lower case
205      */

206     private String JavaDoc internalGetPluginClassname(String JavaDoc pluginName) {
207         return (String JavaDoc) plugins.get(pluginName);
208     }
209
210     /**
211      * @return Returns null if no plugin has been registered with the specified
212      * name, otherwise the Class representing the the plugin class. Note that
213      * plugin names are always treated as case insensitive, so Ant, ant,
214      * and AnT are all treated as the same plugin.
215      *
216      * @throws CruiseControlException If the class provided cannot be loaded.
217      */

218     public Class JavaDoc getPluginClass(String JavaDoc pluginName) throws CruiseControlException {
219         if (!isPluginRegistered(pluginName)) {
220             return null;
221         }
222         String JavaDoc pluginClassname = getPluginClassname(pluginName);
223
224         try {
225             return Class.forName(pluginClassname);
226         } catch (ClassNotFoundException JavaDoc e) {
227             String JavaDoc msg = "Attemping to load plugin named [" + pluginName
228                     + "], but couldn't load corresponding class ["
229                     + pluginClassname + "].";
230 // LOG.error(msg, e);
231
throw new CruiseControlException(msg);
232         }
233     }
234     
235     public String JavaDoc getPluginName(Class JavaDoc pluginClass) {
236         String JavaDoc pluginName = null;
237
238         if (parentRegistry != null) {
239             pluginName = parentRegistry.getPluginName(pluginClass);
240         }
241
242         if (pluginName == null) {
243             for (Iterator JavaDoc i = plugins.entrySet().iterator(); i.hasNext();) {
244                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) i.next();
245                 String JavaDoc value = (String JavaDoc) entry.getValue();
246                 if (value.equals(pluginClass.getName())) {
247                     pluginName = ((String JavaDoc) entry.getKey());
248                     break;
249                 }
250             }
251         }
252
253         return pluginName;
254     }
255
256     public PluginDetail[] getPluginDetails() throws CruiseControlException {
257         List JavaDoc availablePlugins = new LinkedList JavaDoc();
258         
259         if (parentRegistry != null) {
260             availablePlugins.addAll(Arrays.asList(parentRegistry.getPluginDetails()));
261         }
262         
263         for (Iterator JavaDoc i = plugins.keySet().iterator(); i.hasNext();) {
264             String JavaDoc pluginName = (String JavaDoc) i.next();
265             try {
266                 Class JavaDoc pluginClass = getPluginClass(pluginName);
267                 availablePlugins.add(new GenericPluginDetail(pluginName, pluginClass));
268             } catch (CruiseControlException e) {
269                 String JavaDoc message = e.getMessage();
270                 if (message.indexOf("starteam") < 0) {
271                     throw e;
272                 }
273             }
274         }
275         
276         return (PluginDetail[]) availablePlugins.toArray(new PluginDetail[availablePlugins.size()]);
277     }
278     
279     public PluginType[] getPluginTypes() {
280         return PluginType.getTypes();
281     }
282
283     /**
284      * @return True if this registry or its parent contains
285      * an entry for the plugin specified by the name.
286      * The name is the short name for the plugin, not
287      * the classname, e.g. ant. Note that plugin
288      * names are always treated as case insensitive, so Ant, ant, and AnT are
289      * all treated as the same plugin.
290      *
291      * @throws NullPointerException If a null pluginName is passed, then
292      * a NullPointerException will occur. It's recommended to not pass a
293      * null pluginName.
294      */

295     public boolean isPluginRegistered(String JavaDoc pluginName) {
296         boolean isRegistered = plugins.containsKey(pluginName.toLowerCase());
297         if (!isRegistered && parentRegistry != null) {
298             isRegistered = parentRegistry.isPluginRegistered(pluginName);
299         }
300         return isRegistered;
301     }
302
303     /**
304      * Returns a PluginRegistry containing all the default plugins.
305      * The key is the plugin name (e.g. ant) and the value is
306      * the fully qualified classname
307      * (e.g. net.sourceforge.cruisecontrol.builders.AntBuilder).
308      * @throws RuntimeException in case of IOException during the reading of the properties-file
309      */

310     static PluginRegistry loadDefaultPluginRegistry() {
311         PluginRegistry rootRegistry = new PluginRegistry(null);
312         Properties JavaDoc pluginDefinitions = new Properties JavaDoc();
313         try {
314             pluginDefinitions.load(PluginRegistry.class.getResourceAsStream("default-plugins.properties"));
315         } catch (IOException JavaDoc e) {
316             throw new RuntimeException JavaDoc("Failed to load plugin-definitions from default-plugins.properties: " + e);
317         }
318         for (Iterator JavaDoc iter = pluginDefinitions.entrySet().iterator(); iter.hasNext(); ) {
319             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iter.next();
320             rootRegistry.register((String JavaDoc) entry.getKey(), (String JavaDoc) entry.getValue());
321         }
322         return rootRegistry;
323     }
324
325     /**
326      * Get the plugin configuration particular to this plugin, merged with the parents
327      * @throws NullPointerException if pluginName is null
328      */

329     public Element getPluginConfig(String JavaDoc pluginName) {
330         pluginName = pluginName.toLowerCase();
331         String JavaDoc className = getPluginClassname(pluginName);
332         return overridePluginConfig(pluginName, className, null);
333     }
334
335     /**
336      * Return a merged plugin configuration, taking into account parent classes where appropriate.
337      *
338      * This method is used recursively to fill up the specified pluginConfig Element.
339      *
340      * Properties are taken from parent plugins if they have not been defined in the child.
341      * Nested elements of the parent are always added to the child's config.
342      *
343      * Note: as we have no way to enforce the cardinality of the nested elements, the parent/default nested
344      * elements are always added to the config of the child. The validity of the resulting config then
345      * depends on the config to be correctly specified.
346      *
347      * @param pluginName the name of the plugin to create a config for (must be lower case)
348      * @param pluginClass the mapped class name for the plugin
349      * @param pluginConfig the current config, passed up for completion
350      * @return an Element representing the combination of the various plugin configurations for
351      * the same plugin, following the hierachy.
352      */

353     private Element overridePluginConfig(final String JavaDoc pluginName, final String JavaDoc pluginClass, Element pluginConfig) {
354         Element pluginElement = (Element) this.pluginConfigs.get(pluginName);
355         // clone the first found plugin config
356
if (pluginElement != null && pluginConfig == null) {
357             pluginElement = (Element) pluginElement.clone();
358         }
359         if (pluginConfig == null) {
360             pluginConfig = pluginElement;
361         } else {
362             // do not override if class names do not match
363
if (pluginElement != null && pluginClass.equals(this.internalGetPluginClassname(pluginName))) {
364                 // override properties
365
List JavaDoc attributes = pluginElement.getAttributes();
366                 for (int i = 0; i < attributes.size(); i++) {
367                     Attribute attribute = (Attribute) attributes.get(i);
368                     String JavaDoc name = attribute.getName();
369                     if (pluginConfig.getAttribute(name) == null) {
370                         pluginConfig.setAttribute(name, attribute.getValue());
371                     }
372                 }
373                 // combine child elements
374
List JavaDoc children = pluginElement.getChildren();
375                 for (int i = 0; i < children.size(); i++) {
376                     Element child = (Element) children.get(i);
377                     pluginConfig.addContent((Element) child.clone());
378                 }
379             }
380         }
381         if (this.parentRegistry != null) {
382             pluginConfig = this.parentRegistry.overridePluginConfig(pluginName, pluginClass, pluginConfig);
383         }
384         return pluginConfig;
385     }
386
387     /**
388      * Returns a Map containing the default properties for the plugin
389      * with the given name. If there's no such plugin, an empty
390      * Map will be returned. The default properties can be inherited
391      * from a parent registry.
392      * @deprecated use FIXME that also supports preconfiguration of nested elements
393      */

394     public Map JavaDoc getDefaultProperties(String JavaDoc pluginName) {
395         Map JavaDoc defaultProperties = new HashMap JavaDoc();
396         Element pluginConfig = this.getPluginConfig(pluginName);
397         if (pluginConfig != null) {
398             List JavaDoc attributes = pluginConfig.getAttributes();
399             for (Iterator JavaDoc iter = attributes.iterator(); iter.hasNext(); ) {
400                 Attribute attr = (Attribute) iter.next();
401                 String JavaDoc name = attr.getName();
402                 if (name.equals("name") || name.equals("classname")) {
403                     continue;
404                 }
405                 if (LOG.isDebugEnabled()) {
406                     LOG.debug("setting default property " + name + " to '" + attr.getValue()
407                        + "' for " + pluginName);
408                 }
409                 defaultProperties.put(name, attr.getValue());
410             }
411         }
412         return Collections.unmodifiableMap(defaultProperties);
413     }
414 }
415
Popular Tags