001/* 002 Licensed to the Apache Software Foundation (ASF) under one 003 or more contributor license agreements. See the NOTICE file 004 distributed with this work for additional information 005 regarding copyright ownership. The ASF licenses this file 006 to you under the Apache License, Version 2.0 (the 007 "License"); you may not use this file except in compliance 008 with the License. You may obtain a copy of the License at 009 010 http://www.apache.org/licenses/LICENSE-2.0 011 012 Unless required by applicable law or agreed to in writing, 013 software distributed under the License is distributed on an 014 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 KIND, either express or implied. See the License for the 016 specific language governing permissions and limitations 017 under the License. 018 */ 019 020package org.apache.wiki.plugin; 021 022import org.apache.commons.lang3.ClassUtils; 023import org.apache.commons.lang3.StringUtils; 024import org.apache.log4j.Logger; 025import org.apache.oro.text.regex.MalformedPatternException; 026import org.apache.oro.text.regex.MatchResult; 027import org.apache.oro.text.regex.Pattern; 028import org.apache.oro.text.regex.PatternCompiler; 029import org.apache.oro.text.regex.PatternMatcher; 030import org.apache.oro.text.regex.Perl5Compiler; 031import org.apache.oro.text.regex.Perl5Matcher; 032import org.apache.wiki.InternalWikiException; 033import org.apache.wiki.ajax.WikiAjaxDispatcherServlet; 034import org.apache.wiki.ajax.WikiAjaxServlet; 035import org.apache.wiki.api.core.Context; 036import org.apache.wiki.api.core.Engine; 037import org.apache.wiki.api.exceptions.PluginException; 038import org.apache.wiki.api.plugin.InitializablePlugin; 039import org.apache.wiki.api.plugin.Plugin; 040import org.apache.wiki.modules.BaseModuleManager; 041import org.apache.wiki.modules.WikiModuleInfo; 042import org.apache.wiki.preferences.Preferences; 043import org.apache.wiki.util.ClassUtil; 044import org.apache.wiki.util.FileUtil; 045import org.apache.wiki.util.TextUtil; 046import org.apache.wiki.util.XHTML; 047import org.apache.wiki.util.XhtmlUtil; 048import org.apache.wiki.util.XmlUtil; 049import org.jdom2.Element; 050 051import javax.servlet.http.HttpServlet; 052import java.io.IOException; 053import java.io.PrintWriter; 054import java.io.StreamTokenizer; 055import java.io.StringReader; 056import java.io.StringWriter; 057import java.text.MessageFormat; 058import java.util.ArrayList; 059import java.util.Collection; 060import java.util.HashMap; 061import java.util.List; 062import java.util.Map; 063import java.util.NoSuchElementException; 064import java.util.Properties; 065import java.util.ResourceBundle; 066import java.util.StringTokenizer; 067 068/** 069 * Manages plugin classes. There exists a single instance of PluginManager 070 * per each instance of Engine, that is, each JSPWiki instance. 071 * <P> 072 * A plugin is defined to have three parts: 073 * <OL> 074 * <li>The plugin class 075 * <li>The plugin parameters 076 * <li>The plugin body 077 * </ol> 078 * 079 * For example, in the following line of code: 080 * <pre> 081 * [{INSERT org.apache.wiki.plugin.FunnyPlugin foo='bar' 082 * blob='goo' 083 * 084 * abcdefghijklmnopqrstuvw 085 * 01234567890}] 086 * </pre> 087 * 088 * The plugin class is "org.apache.wiki.plugin.FunnyPlugin", the 089 * parameters are "foo" and "blob" (having values "bar" and "goo", 090 * respectively), and the plugin body is then 091 * "abcdefghijklmnopqrstuvw\n01234567890". The plugin body is 092 * accessible via a special parameter called "_body". 093 * <p> 094 * If the parameter "debug" is set to "true" for the plugin, 095 * JSPWiki will output debugging information directly to the page if there 096 * is an exception. 097 * <P> 098 * The class name can be shortened, and marked without the package. 099 * For example, "FunnyPlugin" would be expanded to 100 * "org.apache.wiki.plugin.FunnyPlugin" automatically. It is also 101 * possible to define other packages, by setting the 102 * "jspwiki.plugin.searchPath" property. See the included 103 * jspwiki.properties file for examples. 104 * <P> 105 * Even though the nominal way of writing the plugin is 106 * <pre> 107 * [{INSERT pluginclass WHERE param1=value1...}], 108 * </pre> 109 * it is possible to shorten this quite a lot, by skipping the 110 * INSERT, and WHERE words, and dropping the package name. For 111 * example: 112 * 113 * <pre> 114 * [{INSERT org.apache.wiki.plugin.Counter WHERE name='foo'}] 115 * </pre> 116 * 117 * is the same as 118 * <pre> 119 * [{Counter name='foo'}] 120 * </pre> 121 * <h3>Plugin property files</h3> 122 * <p> 123 * Since 2.3.25 you can also define a generic plugin XML properties file per 124 * each JAR file. 125 * <pre> 126 * <modules> 127 * <plugin class="org.apache.wiki.foo.TestPlugin"> 128 * <author>Janne Jalkanen</author> 129 * <script>foo.js</script> 130 * <stylesheet>foo.css</stylesheet> 131 * <alias>code</alias> 132 * </plugin> 133 * <plugin class="org.apache.wiki.foo.TestPlugin2"> 134 * <author>Janne Jalkanen</author> 135 * </plugin> 136 * </modules> 137 * </pre> 138 * <h3>Plugin lifecycle</h3> 139 * 140 * <p>Plugin can implement multiple interfaces to let JSPWiki know at which stages they should 141 * be invoked: 142 * <ul> 143 * <li>InitializablePlugin: If your plugin implements this interface, the initialize()-method is 144 * called once for this class 145 * before any actual execute() methods are called. You should use the initialize() for e.g. 146 * precalculating things. But notice that this method is really called only once during the 147 * entire Engine lifetime. The InitializablePlugin is available from 2.5.30 onwards.</li> 148 * <li>ParserStagePlugin: If you implement this interface, the executeParse() method is called 149 * when JSPWiki is forming the DOM tree. You will receive an incomplete DOM tree, as well 150 * as the regular parameters. However, since JSPWiki caches the DOM tree to speed up later 151 * places, which means that whatever this method returns would be irrelevant. You can do some DOM 152 * tree manipulation, though. The ParserStagePlugin is available from 2.5.30 onwards.</li> 153 * <li>Plugin: The regular kind of plugin which is executed at every rendering stage. Each 154 * new page load is guaranteed to invoke the plugin, unlike with the ParserStagePlugins.</li> 155 * </ul> 156 * 157 * @since 1.6.1 158 */ 159public class DefaultPluginManager extends BaseModuleManager implements PluginManager { 160 161 private static final String PLUGIN_INSERT_PATTERN = "\\{?(INSERT)?\\s*([\\w\\._]+)[ \\t]*(WHERE)?[ \\t]*"; 162 private static final Logger log = Logger.getLogger( DefaultPluginManager.class ); 163 private static final String DEFAULT_FORMS_PACKAGE = "org.apache.wiki.forms"; 164 165 private ArrayList< String > m_searchPath = new ArrayList<>(); 166 private ArrayList< String > m_externalJars = new ArrayList<>(); 167 private Pattern m_pluginPattern; 168 private boolean m_pluginsEnabled = true; 169 170 /** Keeps a list of all known plugin classes. */ 171 private Map< String, WikiPluginInfo > m_pluginClassMap = new HashMap<>(); 172 173 /** 174 * Create a new PluginManager. 175 * 176 * @param engine Engine which owns this manager. 177 * @param props Contents of a "jspwiki.properties" file. 178 */ 179 public DefaultPluginManager( final Engine engine, final Properties props ) { 180 super( engine ); 181 final String packageNames = props.getProperty( Engine.PROP_SEARCHPATH ); 182 if ( packageNames != null ) { 183 final StringTokenizer tok = new StringTokenizer( packageNames, "," ); 184 while( tok.hasMoreTokens() ) { 185 m_searchPath.add( tok.nextToken().trim() ); 186 } 187 } 188 189 final String externalJars = props.getProperty( PROP_EXTERNALJARS ); 190 if( externalJars != null ) { 191 final StringTokenizer tok = new StringTokenizer( externalJars, "," ); 192 while( tok.hasMoreTokens() ) { 193 m_externalJars.add( tok.nextToken().trim() ); 194 } 195 } 196 197 registerPlugins(); 198 199 // The default packages are always added. 200 m_searchPath.add( DEFAULT_PACKAGE ); 201 m_searchPath.add( DEFAULT_FORMS_PACKAGE ); 202 203 final PatternCompiler compiler = new Perl5Compiler(); 204 try { 205 m_pluginPattern = compiler.compile( PLUGIN_INSERT_PATTERN ); 206 } catch( final MalformedPatternException e ) { 207 log.fatal( "Internal error: someone messed with pluginmanager patterns.", e ); 208 throw new InternalWikiException( "PluginManager patterns are broken" , e ); 209 } 210 } 211 212 /** {@inheritDoc} */ 213 @Override 214 public void enablePlugins( final boolean enabled ) { 215 m_pluginsEnabled = enabled; 216 } 217 218 /** {@inheritDoc} */ 219 @Override 220 public boolean pluginsEnabled() { 221 return m_pluginsEnabled; 222 } 223 224 /** {@inheritDoc} */ 225 @Override 226 public Pattern getPluginPattern() { 227 return m_pluginPattern; 228 } 229 230 /** 231 * Attempts to locate a plugin class from the class path set in the property file. 232 * 233 * @param classname Either a fully fledged class name, or just the name of the file (that is, "org.apache.wiki.plugin.Counter" or just plain "Counter"). 234 * @return A found class. 235 * @throws ClassNotFoundException if no such class exists. 236 */ 237 private Class< ? > findPluginClass( final String classname ) throws ClassNotFoundException { 238 return ClassUtil.findClass( m_searchPath, m_externalJars, classname ); 239 } 240 241 /** Outputs a HTML-formatted version of a stack trace. */ 242 private String stackTrace( final Map<String,String> params, final Throwable t ) { 243 final Element div = XhtmlUtil.element( XHTML.div, "Plugin execution failed, stack trace follows:" ); 244 div.setAttribute( XHTML.ATTR_class, "debug" ); 245 246 final StringWriter out = new StringWriter(); 247 t.printStackTrace( new PrintWriter( out ) ); 248 div.addContent( XhtmlUtil.element( XHTML.pre, out.toString() ) ); 249 div.addContent( XhtmlUtil.element( XHTML.b, "Parameters to the plugin" ) ); 250 251 final Element list = XhtmlUtil.element( XHTML.ul ); 252 for( final Map.Entry< String, String > e : params.entrySet() ) { 253 final String key = e.getKey(); 254 list.addContent( XhtmlUtil.element( XHTML.li, key + "'='" + e.getValue() ) ); 255 } 256 div.addContent( list ); 257 return XhtmlUtil.serialize( div ); 258 } 259 260 /** {@inheritDoc} */ 261 @Override 262 public String execute( final Context context, final String classname, final Map< String, String > params ) throws PluginException { 263 if( !m_pluginsEnabled ) { 264 return ""; 265 } 266 267 final ResourceBundle rb = Preferences.getBundle( context, Plugin.CORE_PLUGINS_RESOURCEBUNDLE ); 268 final boolean debug = TextUtil.isPositive( params.get( PARAM_DEBUG ) ); 269 try { 270 // Create... 271 final Plugin plugin = newWikiPlugin( classname, rb ); 272 if( plugin == null ) { 273 return "Plugin '" + classname + "' not compatible with this version of JSPWiki"; 274 } 275 276 // ...and launch. 277 try { 278 return plugin.execute( context, params ); 279 } catch( final PluginException e ) { 280 if( debug ) { 281 return stackTrace( params, e ); 282 } 283 284 // Just pass this exception onward. 285 throw ( PluginException )e.fillInStackTrace(); 286 } catch( final Throwable t ) { 287 // But all others get captured here. 288 log.info( "Plugin failed while executing:", t ); 289 if( debug ) { 290 return stackTrace( params, t ); 291 } 292 293 throw new PluginException( rb.getString( "plugin.error.failed" ), t ); 294 } 295 296 } catch( final ClassCastException e ) { 297 throw new PluginException( MessageFormat.format( rb.getString( "plugin.error.notawikiplugin" ), classname ), e ); 298 } 299 } 300 301 /** {@inheritDoc} */ 302 @Override 303 public Map< String, String > parseArgs( final String argstring ) throws IOException { 304 final Map< String, String > arglist = new HashMap<>(); 305 // Protection against funny users. 306 if( argstring == null ) { 307 return arglist; 308 } 309 310 arglist.put( PARAM_CMDLINE, argstring ); 311 final StringReader in = new StringReader( argstring ); 312 final StreamTokenizer tok = new StreamTokenizer( in ); 313 tok.eolIsSignificant( true ); 314 315 String param = null; 316 String value; 317 boolean potentialEmptyLine = false; 318 boolean quit = false; 319 while( !quit ) { 320 final String s; 321 final int type = tok.nextToken(); 322 323 switch( type ) { 324 case StreamTokenizer.TT_EOF: 325 quit = true; 326 s = null; 327 break; 328 329 case StreamTokenizer.TT_WORD: 330 s = tok.sval; 331 potentialEmptyLine = false; 332 break; 333 334 case StreamTokenizer.TT_EOL: 335 quit = potentialEmptyLine; 336 potentialEmptyLine = true; 337 s = null; 338 break; 339 340 case StreamTokenizer.TT_NUMBER: 341 s = Integer.toString( ( int )tok.nval ); 342 potentialEmptyLine = false; 343 break; 344 345 case '\'': 346 s = tok.sval; 347 break; 348 349 default: 350 s = null; 351 } 352 353 // Assume that alternate words on the line are parameter and value, respectively. 354 if( s != null ) { 355 if( param == null ) { 356 param = s; 357 } else { 358 value = s; 359 arglist.put( param, value ); 360 param = null; 361 } 362 } 363 } 364 365 // Now, we'll check the body. 366 if( potentialEmptyLine ) { 367 final StringWriter out = new StringWriter(); 368 FileUtil.copyContents( in, out ); 369 final String bodyContent = out.toString(); 370 if( bodyContent != null ) { 371 arglist.put( PARAM_BODY, bodyContent ); 372 } 373 } 374 375 return arglist; 376 } 377 378 /** {@inheritDoc} */ 379 @Override 380 public String execute( final Context context, final String commandline ) throws PluginException { 381 if( !m_pluginsEnabled ) { 382 return ""; 383 } 384 385 final ResourceBundle rb = Preferences.getBundle( context, Plugin.CORE_PLUGINS_RESOURCEBUNDLE ); 386 final PatternMatcher matcher = new Perl5Matcher(); 387 388 try { 389 if( matcher.contains( commandline, m_pluginPattern ) ) { 390 final MatchResult res = matcher.getMatch(); 391 final String plugin = res.group( 2 ); 392 final int endIndex = commandline.length() - ( commandline.charAt( commandline.length() - 1 ) == '}' ? 1 : 0 ); 393 final String args = commandline.substring( res.endOffset( 0 ), endIndex ); 394 final Map< String, String > arglist = parseArgs( args ); 395 return execute( context, plugin, arglist ); 396 } 397 } catch( final NoSuchElementException e ) { 398 final String msg = "Missing parameter in plugin definition: " + commandline; 399 log.warn( msg, e ); 400 throw new PluginException( MessageFormat.format( rb.getString( "plugin.error.missingparameter" ), commandline ) ); 401 } catch( final IOException e ) { 402 final String msg = "Zyrf. Problems with parsing arguments: " + commandline; 403 log.warn( msg, e ); 404 throw new PluginException( MessageFormat.format( rb.getString( "plugin.error.parsingarguments" ), commandline ) ); 405 } 406 407 // FIXME: We could either return an empty string "", or the original line. If we want unsuccessful requests 408 // to be invisible, then we should return an empty string. 409 return commandline; 410 } 411 412 /** Register a plugin. */ 413 private void registerPlugin( final WikiPluginInfo pluginClass ) { 414 String name; 415 416 // Register the plugin with the className without the package-part 417 name = pluginClass.getName(); 418 if( name != null ) { 419 log.debug( "Registering plugin [name]: " + name ); 420 m_pluginClassMap.put( name, pluginClass ); 421 } 422 423 // Register the plugin with a short convenient name. 424 name = pluginClass.getAlias(); 425 if( name != null ) { 426 log.debug( "Registering plugin [shortName]: " + name ); 427 m_pluginClassMap.put( name, pluginClass ); 428 } 429 430 // Register the plugin with the className with the package-part 431 name = pluginClass.getClassName(); 432 if( name != null ) { 433 log.debug( "Registering plugin [className]: " + name ); 434 m_pluginClassMap.put( name, pluginClass ); 435 } 436 437 pluginClass.initializePlugin( pluginClass, m_engine, m_searchPath, m_externalJars ); 438 } 439 440 private void registerPlugins() { 441 // Register all plugins which have created a resource containing its properties. 442 log.info( "Registering plugins" ); 443 final List< Element > plugins = XmlUtil.parse( PLUGIN_RESOURCE_LOCATION, "/modules/plugin" ); 444 445 // Get all resources of all plugins. 446 for( final Element pluginEl : plugins ) { 447 final String className = pluginEl.getAttributeValue( "class" ); 448 final WikiPluginInfo pluginInfo = WikiPluginInfo.newInstance( className, pluginEl ,m_searchPath, m_externalJars ); 449 if( pluginInfo != null ) { 450 registerPlugin( pluginInfo ); 451 } 452 } 453 } 454 455 /** 456 * Contains information about a bunch of plugins. 457 */ 458 // FIXME: This class needs a better interface to return all sorts of possible information from the plugin XML. In fact, it probably 459 // should have some sort of a superclass system. 460 public static final class WikiPluginInfo extends WikiModuleInfo { 461 462 private String m_className; 463 private String m_alias; 464 private String m_ajaxAlias; 465 private Class<?> m_clazz; 466 467 private boolean m_initialized = false; 468 469 /** 470 * Creates a new plugin info object which can be used to access a plugin. 471 * 472 * @param className Either a fully qualified class name, or a "short" name which is then checked against the internal list of plugin packages. 473 * @param el A JDOM Element containing the information about this class. 474 * @param searchPath A List of Strings, containing different package names. 475 * @param externalJars the list of external jars to search 476 * @return A WikiPluginInfo object. 477 */ 478 protected static WikiPluginInfo newInstance( final String className, final Element el, final List<String> searchPath, final List<String> externalJars ) { 479 if( className == null || className.length() == 0 ) { 480 return null; 481 } 482 483 final WikiPluginInfo info = new WikiPluginInfo( className ); 484 info.initializeFromXML( el ); 485 return info; 486 } 487 488 /** 489 * Initializes a plugin, if it has not yet been initialized. If the plugin extends {@link HttpServlet} it will automatically 490 * register it as AJAX using {@link WikiAjaxDispatcherServlet#registerServlet(String, WikiAjaxServlet)}. 491 * 492 * @param engine The Engine 493 * @param searchPath A List of Strings, containing different package names. 494 * @param externalJars the list of external jars to search 495 */ 496 protected void initializePlugin( final WikiPluginInfo info, final Engine engine , final List<String> searchPath, final List<String> externalJars) { 497 if( !m_initialized ) { 498 // This makes sure we only try once per class, even if init fails. 499 m_initialized = true; 500 501 try { 502 final Plugin p = newPluginInstance(searchPath, externalJars); 503 if( p instanceof InitializablePlugin ) { 504 ( ( InitializablePlugin )p ).initialize( engine ); 505 } 506 if( p instanceof WikiAjaxServlet ) { 507 WikiAjaxDispatcherServlet.registerServlet( (WikiAjaxServlet) p ); 508 final String ajaxAlias = info.getAjaxAlias(); 509 if (StringUtils.isNotBlank(ajaxAlias)) { 510 WikiAjaxDispatcherServlet.registerServlet( info.getAjaxAlias(), (WikiAjaxServlet) p ); 511 } 512 } 513 } catch( final Exception e ) { 514 log.info( "Cannot initialize plugin " + m_className, e ); 515 } 516 } 517 } 518 519 /** 520 * {@inheritDoc} 521 */ 522 @Override 523 protected void initializeFromXML( final Element el ) { 524 super.initializeFromXML( el ); 525 m_alias = el.getChildText( "alias" ); 526 m_ajaxAlias = el.getChildText( "ajaxAlias" ); 527 } 528 529 /** 530 * Create a new WikiPluginInfo based on the Class information. 531 * 532 * @param clazz The class to check 533 * @return A WikiPluginInfo instance 534 */ 535 protected static WikiPluginInfo newInstance( final Class< ? > clazz ) { 536 return new WikiPluginInfo( clazz.getName() ); 537 } 538 539 private WikiPluginInfo( final String className ) { 540 super( className ); 541 setClassName( className ); 542 } 543 544 private void setClassName( final String fullClassName ) { 545 m_name = ClassUtils.getShortClassName( fullClassName ); 546 m_className = fullClassName; 547 } 548 549 /** 550 * Returns the full class name of this object. 551 * @return The full class name of the object. 552 */ 553 public String getClassName() { 554 return m_className; 555 } 556 557 /** 558 * Returns the alias name for this object. 559 * @return An alias name for the plugin. 560 */ 561 public String getAlias() { 562 return m_alias; 563 } 564 565 /** 566 * Returns the ajax alias name for this object. 567 * @return An ajax alias name for the plugin. 568 */ 569 public String getAjaxAlias() { 570 return m_ajaxAlias; 571 } 572 573 /** 574 * Creates a new plugin instance. 575 * 576 * @param searchPath A List of Strings, containing different package names. 577 * @param externalJars the list of external jars to search 578 * @return A new plugin. 579 * @throws ClassNotFoundException If the class declared was not found. 580 * @throws InstantiationException If the class cannot be instantiated- 581 * @throws IllegalAccessException If the class cannot be accessed. 582 */ 583 584 public Plugin newPluginInstance( final List< String > searchPath, final List< String > externalJars) throws ClassNotFoundException, InstantiationException, IllegalAccessException { 585 if( m_clazz == null ) { 586 m_clazz = ClassUtil.findClass( searchPath, externalJars ,m_className ); 587 } 588 589 return ( Plugin )m_clazz.newInstance(); 590 } 591 592 /** 593 * Returns a text for IncludeResources. 594 * 595 * @param type Either "script" or "stylesheet" 596 * @return Text, or an empty string, if there is nothing to be included. 597 */ 598 public String getIncludeText( final String type ) { 599 try { 600 if( "script".equals( type ) ) { 601 return getScriptText(); 602 } else if( "stylesheet".equals( type ) ) { 603 return getStylesheetText(); 604 } 605 } catch( final Exception ex ) { 606 // We want to fail gracefully here 607 return ex.getMessage(); 608 } 609 610 return null; 611 } 612 613 private String getScriptText() throws IOException { 614 if( m_scriptText != null ) { 615 return m_scriptText; 616 } 617 618 if( m_scriptLocation == null ) { 619 return ""; 620 } 621 622 try { 623 m_scriptText = getTextResource(m_scriptLocation); 624 } catch( final IOException ex ) { 625 // Only throw this exception once! 626 m_scriptText = ""; 627 throw ex; 628 } 629 630 return m_scriptText; 631 } 632 633 private String getStylesheetText() throws IOException { 634 if( m_stylesheetText != null ) { 635 return m_stylesheetText; 636 } 637 638 if( m_stylesheetLocation == null ) { 639 return ""; 640 } 641 642 try { 643 m_stylesheetText = getTextResource(m_stylesheetLocation); 644 } catch( final IOException ex ) { 645 // Only throw this exception once! 646 m_stylesheetText = ""; 647 throw ex; 648 } 649 650 return m_stylesheetText; 651 } 652 653 /** 654 * Returns a string suitable for debugging. Don't assume that the format would stay the same. 655 * 656 * @return Something human-readable 657 */ 658 @Override 659 public String toString() { 660 return "Plugin :[name=" + m_name + "][className=" + m_className + "]"; 661 } 662 663 } // WikiPluginClass 664 665 /** 666 * {@inheritDoc} 667 */ 668 @Override 669 public Collection< WikiModuleInfo > modules() { 670 return modules( m_pluginClassMap.values().iterator() ); 671 } 672 673 /** 674 * {@inheritDoc} 675 */ 676 @Override 677 public WikiPluginInfo getModuleInfo( final String moduleName) { 678 return m_pluginClassMap.get(moduleName); 679 } 680 681 /** 682 * Creates a {@link Plugin}. 683 * 684 * @param pluginName plugin's classname 685 * @param rb {@link ResourceBundle} with i18ned text for exceptions. 686 * @return a {@link Plugin}. 687 * @throws PluginException if there is a problem building the {@link Plugin}. 688 */ 689 @Override 690 public Plugin newWikiPlugin( final String pluginName, final ResourceBundle rb ) throws PluginException { 691 Plugin plugin = null; 692 WikiPluginInfo pluginInfo = m_pluginClassMap.get( pluginName ); 693 try { 694 if( pluginInfo == null ) { 695 pluginInfo = WikiPluginInfo.newInstance( findPluginClass( pluginName ) ); 696 registerPlugin( pluginInfo ); 697 } 698 699 if( !checkCompatibility( pluginInfo ) ) { 700 final String msg = "Plugin '" + pluginInfo.getName() + "' not compatible with this version of JSPWiki"; 701 log.info( msg ); 702 } else { 703 plugin = pluginInfo.newPluginInstance(m_searchPath, m_externalJars); 704 } 705 } catch( final ClassNotFoundException e ) { 706 throw new PluginException( MessageFormat.format( rb.getString( "plugin.error.couldnotfind" ), pluginName ), e ); 707 } catch( final InstantiationException e ) { 708 throw new PluginException( MessageFormat.format( rb.getString( "plugin.error.cannotinstantiate" ), pluginName ), e ); 709 } catch( final IllegalAccessException e ) { 710 throw new PluginException( MessageFormat.format( rb.getString( "plugin.error.notallowed" ), pluginName ), e ); 711 } catch( final Exception e ) { 712 throw new PluginException( MessageFormat.format( rb.getString( "plugin.error.instantationfailed" ), pluginName ), e ); 713 } 714 return plugin; 715 } 716 717}