1 package xdoclet;
2
3 import org.apache.commons.logging.LogFactory;
4
5 import xdoclet.plugins.JellyPlugin;
6 import xdoclet.plugins.VelocityPlugin;
7 import xdoclet.sdk.beans.BeanInfoPlugin;
8 import xdoclet.sdk.beans.ManifestPlugin;
9 import xdoclet.util.ClasspathManager;
10
11 import java.beans.BeanDescriptor;
12 import java.beans.BeanInfo;
13 import java.beans.Beans;
14 import java.beans.IntrospectionException;
15 import java.beans.Introspector;
16 import java.beans.MethodDescriptor;
17
18 import java.lang.reflect.Method;
19
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Locale;
25 import java.util.Map;
26 import java.io.IOException;
27
28 /***
29 * <p>
30 * This class is responsible for discovering pluggable components such as
31 * {@link MetadataProvider}s and {@link Plugin}s that are available on the
32 * classpath. In order to be discovered, these classes must be declared as
33 * Java Beans in their MANIFEST.MF file, and they must also be accompanied
34 * with a BeanInfo class.
35 * </p>
36 * <p>
37 * BeanInfo classes can be generated automatically with the built-in
38 * {@link BeanInfoPlugin}.
39 * </p>
40 *
41 * @author <a href="mailto:aslak.hellesoy at netcom.no">Aslak Hellesøy</a>
42 * @version $Revision: 1.16 $
43 */
44 public final class PluginFactory {
45 private static final String XJAVADOC_METADATA_PROVIDER_CLASS_NAME = "xdoclet.xjavadoc.XJavadocMetadataProvider";
46
47 /*** Helps us get files from the classpath. */
48 private final ClasspathManager _classpathManager;
49
50 /*** Maps metadataprovider name to MetadataProvider class. */
51 private final Map _nameToMetadataProviderClassMap = new HashMap();
52
53 /*** Maps plugin name to plugin class. */
54 private final Map _nameToPluginClassMap = new HashMap();
55
56 /*** Maps plugin class to parent class. */
57 private final Map _pluginClassToXDocletClassMap = new HashMap();
58
59 /***
60 * Constructs a new PluginFactory.
61 *
62 * @param classpathManager the classpathManager to use
63 */
64 public PluginFactory(ClasspathManager classpathManager) {
65 _classpathManager = classpathManager;
66
67 // Register the built-in plugins needed for bootstrapping
68 registerPlugin("velocity", VelocityPlugin.class, XDoclet.class);
69 registerPlugin("jelly", JellyPlugin.class, XDoclet.class);
70 registerPlugin("manifest", ManifestPlugin.class, XDoclet.class);
71 registerPlugin("beaninfo", BeanInfoPlugin.class, XDoclet.class);
72
73 // By default, try to register XJavaDoc.
74 // TODO XJavaDocMetadataProvider should move to XJavaDoc, but we
75 // have a little chicken and egg problem with the BeanInfo.
76 // Well, just put BeanInfo for XJavaDoc in CVS too.
77 try {
78 Class xjavadoc = getClass().getClassLoader().loadClass(XJAVADOC_METADATA_PROVIDER_CLASS_NAME);
79 _nameToMetadataProviderClassMap.put( "xjavadoc", xjavadoc);
80 } catch( ClassNotFoundException e ) {
81 e.printStackTrace();
82 }
83
84 // Register beans (plugins and MetadataProviders from classpath
85 registerBeans();
86 }
87
88 /***
89 * Gets that ClasspathManager.
90 * @return the ClasspathManager.
91 */
92 public ClasspathManager getClasspathManager() {
93 return _classpathManager;
94 }
95
96 /***
97 * Updates the MethodDescriptor of XDoclet's createPlugin method with an
98 * attribute indicating what plugins are allowed.
99 *
100 * @param xdocletClass
101 */
102 private void updateBeanInfo(Class xdocletClass, String pluginName) {
103 try {
104 BeanInfo beanInfo = Introspector.getBeanInfo(xdocletClass);
105 MethodDescriptor[] md = beanInfo.getMethodDescriptors();
106 Method createPlugin = xdocletClass.getMethod("createPlugin", new Class[] { String.class });
107 boolean didUpdate = false;
108
109 for (int i = 0; i < md.length; i++) {
110 if (md[i].getMethod().equals(createPlugin)) {
111 List pluginNames = (List) md[i].getValue("pluginNames");
112
113 if (pluginNames == null) {
114 pluginNames = new ArrayList();
115 md[i].setValue("pluginNames", pluginNames);
116 }
117
118 pluginNames.add(pluginName);
119 didUpdate = true;
120
121 break;
122 }
123 }
124
125 if (!didUpdate) {
126 throw new IllegalStateException(
127 "BeanInfo was not properly updated. Couldn't find createPlugin method in " + xdocletClass.getName());
128 }
129 } catch (IntrospectionException e) {
130 LogFactory.getLog(XDoclet.class).error("Couldn't update BeanInfo", e);
131 throw new IllegalStateException(e.getMessage());
132 } catch (NoSuchMethodException e) {
133 LogFactory.getLog(XDoclet.class).error("Couldn't update BeanInfo", e);
134 throw new IllegalStateException(e.getMessage());
135 } catch (SecurityException e) {
136 LogFactory.getLog(XDoclet.class).error("Couldn't update BeanInfo", e);
137 throw new IllegalStateException(e.getMessage());
138 }
139 }
140
141 /***
142 * Checks whether a particular plugin class is allowed under a particular XDoclet
143 * @param pluginName name of the plugin to check for.
144 * @param xdocletClass the XDoclet class to check for.
145 * @return true if the plugin can be used with the XDoclet instance.
146 */
147 private boolean allowsPlugin(String pluginName, Class xdocletClass) {
148 try {
149 // plugin names are case insensitive
150 pluginName = pluginName.toLowerCase(Locale.US);
151
152 // Look up the plugin class.
153 Class pluginClass = getPluginClass(pluginName);
154
155 // Look up the allowed parent class
156 Class parentClass = getContainerClass(pluginClass);
157
158 // Verify that the passed xdoclet is allowed to contain the plugin.
159 return parentClass.isAssignableFrom(xdocletClass);
160 } catch (XDocletException e) {
161 LogFactory.getLog(PluginFactory.class).error("Error in allowsPlugin", e);
162 throw new IllegalStateException(e.getMessage());
163 }
164 }
165
166 /***
167 * Creates a new plugin and attaches it to an XDoclet instance.
168 *
169 * @param pluginName the name of the plugin, as specified in xdoclet-plugin.xml
170 * @param xdoclet the XDoclet instance that will contain the created plugin
171 * @return the created plugin
172 * @throws XDocletException if there is no registered plugin for pluginName, or if the corresponding plugin
173 * is not allowed within the XDoclet instance. What XDoclet (sub)classes are allowed is declared
174 * in the plugin's deployment descriptor.
175 */
176 public final Plugin createPlugin(String pluginName, XDoclet xdoclet)
177 throws XDocletException {
178 if (xdoclet == null) {
179 throw new IllegalArgumentException("xdoclet cannot be null");
180 }
181
182 // plugin names are case insensitive
183 pluginName = pluginName.toLowerCase(Locale.US);
184
185 // Look up the plugin class.
186 Class pluginClass = getPluginClass(pluginName);
187
188 // Look up the allowed parent class
189 Class parentClass = getContainerClass(pluginClass);
190
191 // Verify that the passed xdoclet is allowed to contain the plugin.
192 if (!allowsPlugin(pluginName, xdoclet.getClass())) {
193 throw new XDocletException("Not allowed to have " + pluginClass.getName() + " in "
194 + xdoclet.getClass().getName() + ". The enclosing XDoclet must be a subclass of "
195 + parentClass.getName());
196 }
197
198 try {
199 LogFactory.getLog(getClass()).debug("Creating plugin " + pluginClass.getName() + ". pluginClass="
200 + pluginClass.getName() + ", parentClass=" + parentClass.getName());
201
202 // Instantiate the plugin. The instantiated
203 // plugin will be added to the XDoclet's BeanContext by Beans.instantiate.
204 Plugin plugin = (Plugin) Beans.instantiate(pluginClass.getClassLoader(), pluginClass.getName(), xdoclet);
205
206 LogFactory.getLog(getClass()).debug("Creating plugin " + pluginClass.getName());
207
208 plugin.setName(pluginName);
209
210 return plugin;
211 } catch (NoClassDefFoundError e) {
212 LogFactory.getLog(getClass()).error(e.getMessage(), e);
213 throw new XDocletException(e.getMessage(), e);
214 } catch (ClassCastException e) {
215 LogFactory.getLog(getClass()).error(e.getMessage(), e);
216 throw new XDocletException("Couldn't cast " + pluginClass.getName() + " to " + Plugin.class.getName(), e);
217 } catch (Throwable e) {
218 LogFactory.getLog(getClass()).error(e.getMessage(), e);
219 throw new XDocletException(e.getMessage(), e);
220 }
221 }
222
223 /***
224 * Registers all plugins by looking at manifest and beaninfo
225 */
226 private final void registerBeans() {
227 LogFactory.getLog(getClass()).debug("Registering java beans available on the classpath...");
228 for( Iterator i = _classpathManager.findJavaBeans().iterator(); i.hasNext(); ) {
229 Class beanClass = (Class) i.next();
230 if( Plugin.class.isAssignableFrom( beanClass ) ) {
231 registerPlugin( beanClass );
232 }
233 if( MetadataProvider.class.isAssignableFrom( beanClass ) ) {
234 registerMetadataProvider( beanClass );
235 }
236
237 }
238 LogFactory.getLog(getClass()).debug("Done registering plugins");
239 }
240
241 private void registerPlugin( Class pluginClass ) {
242 String parentClassName = null;
243 try {
244 BeanInfo beanInfo = Introspector.getBeanInfo(pluginClass);
245 BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor();
246
247 String pluginName = beanDescriptor.getName();
248 parentClassName = (String) beanDescriptor.getValue("xdoclet-class");
249
250 if ((pluginName != null) && (parentClassName != null)) {
251 Class xdocletClass = pluginClass.getClassLoader().loadClass(parentClassName);
252
253 registerPlugin(pluginName, pluginClass, xdocletClass);
254 }
255 } catch (ClassNotFoundException e) {
256 String errorMessage = parentClassName + " not found.";
257
258 LogFactory.getLog(PluginFactory.class).error(errorMessage, e);
259 throw new IllegalStateException(errorMessage);
260 } catch (IntrospectionException e) {
261 String errorMessage = "Couldn't find BeanInfo for " + pluginClass.getName();
262
263 LogFactory.getLog(PluginFactory.class).error(errorMessage, e);
264 throw new IllegalStateException(errorMessage);
265 }
266 }
267
268 /***
269 * Registers a plugin manually.
270 *
271 * @param pluginName the name of the plugin
272 * @param pluginClass the class of the plugin
273 * @param xdocletClass the parent xdoclet class
274 */
275 private void registerPlugin(String pluginName, Class pluginClass, Class xdocletClass) {
276 // plugin names are case insensitive
277 pluginName = pluginName.toLowerCase(Locale.US);
278 LogFactory.getLog(getClass()).debug("Registering plugin: " + pluginName);
279 _nameToPluginClassMap.put(pluginName, pluginClass);
280 _pluginClassToXDocletClassMap.put(pluginClass, xdocletClass);
281
282 updateBeanInfo(xdocletClass, pluginName);
283 }
284
285 private void registerMetadataProvider( Class metadataProviderClass ) {
286 try {
287 LogFactory.getLog(getClass()).debug("Registering metadata provider: " + metadataProviderClass.getName() );
288 BeanInfo beanInfo = Introspector.getBeanInfo(metadataProviderClass);
289 BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor();
290
291 String metadataProviderName = beanDescriptor.getName();
292 _nameToMetadataProviderClassMap.put( metadataProviderName, metadataProviderClass);
293 } catch( IntrospectionException e ) {
294 String errorMessage = "Couldn't find BeanInfo for " + metadataProviderClass.getName();
295
296 LogFactory.getLog(PluginFactory.class).error(errorMessage, e);
297 throw new IllegalStateException(errorMessage);
298 }
299 }
300
301 /***
302 * Returns the plugin class associated with pluginName.
303 *
304 * @param pluginName the name of the plugin
305 * @return the corresponding plugin class
306 */
307 private Class getPluginClass(String pluginName)
308 throws XDocletException {
309 Class pluginClass = (Class) _nameToPluginClassMap.get(pluginName);
310
311 if (pluginClass == null) {
312 throw new XDocletException("No registered plugin class for plugin \"" + pluginName + "\". "
313 + "Make sure the classpath contains the plugin. classpath=" + ClasspathManager.getNiceClasspath( _classpathManager.getClasspath() ) );
314 }
315
316 return pluginClass;
317 }
318
319 /***
320 * Returns the plugin's container class, which is XDoclet.class or a subclass of it.
321 *
322 * @param pluginClass the plugin class
323 * @return the corresponding plugin class
324 */
325 private Class getContainerClass(Class pluginClass)
326 throws XDocletException {
327 Class xdocletClass = (Class) _pluginClassToXDocletClassMap.get(pluginClass);
328
329 if (xdocletClass == null) {
330 throw new XDocletException("No registered plugin container for plugin class \"" + pluginClass.getName()
331 + "\"");
332 }
333
334 return xdocletClass;
335 }
336
337 public MetadataProvider createMetadataProvider( String name, XDoclet xdoclet ) throws XDocletException {
338 Class metadataProviderClass = (Class) _nameToMetadataProviderClassMap.get( name );
339 if( metadataProviderClass == null ) {
340 throw new XDocletException( "No MetadataProvider class is registered for '" + name + "'. Registered: " + _nameToMetadataProviderClassMap.keySet() );
341 }
342 try {
343 MetadataProvider metadataProvider = (MetadataProvider) Beans.instantiate(metadataProviderClass.getClassLoader(), metadataProviderClass.getName(),
344 xdoclet);
345 return metadataProvider;
346 } catch( IOException e ) {
347 throw new XDocletException( "Couldn't instantiate " + metadataProviderClass.getName(), e );
348 } catch( ClassNotFoundException e ) {
349 throw new XDocletException( "Couldn't instantiate " + metadataProviderClass.getName(), e );
350 } catch( ClassCastException e ) {
351 throw new XDocletException( "Couldn't cast " + metadataProviderClass.getName() + " to " + MetadataProvider.class.getName(), e );
352 }
353 }
354 }
This page was automatically generated by Maven