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