View Javadoc
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