You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jspwiki.apache.org by me...@apache.org on 2014/02/10 20:49:57 UTC

svn commit: r1566717 - in /jspwiki/trunk: ./ jspwiki-war/src/main/java/org/apache/wiki/ jspwiki-war/src/main/java/org/apache/wiki/api/engine/ jspwiki-war/src/main/java/org/apache/wiki/plugin/ jspwiki-war/src/main/java/org/apache/wiki/util/ jspwiki-war/...

Author: metskem
Date: Mon Feb 10 19:49:56 2014
New Revision: 1566717

URL: http://svn.apache.org/r1566717
Log:
2014-02-10  Harry Metske (metskem@apache.org)

       * 2.10.0-svn-72

       * Fixed JSPWIKI-812 - plugin jars should be loadable from outside the war

Modified:
    jspwiki/trunk/ChangeLog
    jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/Release.java
    jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/api/engine/PluginManager.java
    jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/plugin/DefaultPluginManager.java
    jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/util/ClassUtil.java
    jspwiki/trunk/jspwiki-war/src/main/resources/ini/jspwiki.properties

Modified: jspwiki/trunk/ChangeLog
URL: http://svn.apache.org/viewvc/jspwiki/trunk/ChangeLog?rev=1566717&r1=1566716&r2=1566717&view=diff
==============================================================================
--- jspwiki/trunk/ChangeLog (original)
+++ jspwiki/trunk/ChangeLog Mon Feb 10 19:49:56 2014
@@ -1,9 +1,17 @@
+2014-02-10  Harry Metske (metskem@apache.org)
+
+       * 2.10.0-svn-72
+
+       * Fixed JSPWIKI-812 - plugin jars should be loadable from outside the war
+
 2014-02-09  Harry Metske (metskem@apache.org)
+
        * 2.10.0-svn-71
 
        * Fixed JSPWIKI-813 - ReferenceManagerTest - two cases fail, patch by Brian Burch
 
 2014-02-08  Harry Metske (metskem@apache.org)
+
        * 2.10.0-svn-70
 
        * Fixed JSPWIKI-817 - Install.jsp is broken ==> Translation corrections required (EN en NL done)

Modified: jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/Release.java
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/Release.java?rev=1566717&r1=1566716&r2=1566717&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/Release.java (original)
+++ jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/Release.java Mon Feb 10 19:49:56 2014
@@ -72,7 +72,7 @@ public final class Release {
      *  <p>
      *  If the build identifier is empty, it is not added.
      */
-    public static final String     BUILD         = "71";
+    public static final String     BUILD         = "72";
     
     /**
      *  This is the generic version string you should use when printing out the version.  It is of 

Modified: jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/api/engine/PluginManager.java
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/api/engine/PluginManager.java?rev=1566717&r1=1566716&r2=1566717&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/api/engine/PluginManager.java (original)
+++ jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/api/engine/PluginManager.java Mon Feb 10 19:49:56 2014
@@ -30,10 +30,13 @@ import org.apache.wiki.api.plugin.WikiPl
 
 
 public interface PluginManager {
-	
-    /** The property name defining which packages will be searched for properties. */
+
+    /** The property name defining which packages will be searched for plugin classes. */
     String PROP_SEARCHPATH = "jspwiki.plugin.searchPath";
-    
+
+    /** The property name defining which external jars will be added to the classpath when searching for plugin classes. */
+    String PROP_EXTERNALJARS = "jspwiki.plugin.externalJars";
+
     /** This is the default package to try in case the instantiation fails. */
     String DEFAULT_PACKAGE = "org.apache.wiki.plugin";
 

Modified: jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/plugin/DefaultPluginManager.java
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/plugin/DefaultPluginManager.java?rev=1566717&r1=1566716&r2=1566717&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/plugin/DefaultPluginManager.java (original)
+++ jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/plugin/DefaultPluginManager.java Mon Feb 10 19:49:56 2014
@@ -166,7 +166,9 @@ public class DefaultPluginManager extend
 
     private static final String DEFAULT_FORMS_PACKAGE = "org.apache.wiki.forms";
 
-    private ArrayList<String>  m_searchPath = new ArrayList<String>();
+    private ArrayList<String> m_searchPath = new ArrayList<String>();
+
+    private ArrayList<String> m_externalJars = new ArrayList<String>();
 
     private Pattern m_pluginPattern;
 
@@ -195,6 +197,16 @@ public class DefaultPluginManager extend
             }
         }
 
+        String externalJars = props.getProperty( PROP_EXTERNALJARS );
+
+        if( externalJars != null ) {
+            StringTokenizer tok = new StringTokenizer( externalJars, "," );
+
+            while( tok.hasMoreTokens() ) {
+                m_externalJars.add( tok.nextToken().trim() );
+            }
+        }
+
         registerPlugins();
 
         //
@@ -253,7 +265,7 @@ public class DefaultPluginManager extend
      *  @throws ClassNotFoundException if no such class exists.
      */
     private Class< ? > findPluginClass( String classname ) throws ClassNotFoundException {
-        return ClassUtil.findClass( m_searchPath, classname );
+        return ClassUtil.findClass( m_searchPath, m_externalJars, classname );
     }
 
     /**
@@ -531,7 +543,7 @@ public class DefaultPluginManager extend
             m_pluginClassMap.put( name, pluginClass );
         }
 
-        pluginClass.initializePlugin( m_engine );
+        pluginClass.initializePlugin( m_engine , m_searchPath, m_externalJars);
     }
 
     private void registerPlugins() {
@@ -545,7 +557,7 @@ public class DefaultPluginManager extend
         //
         for( Element pluginEl : plugins ) {
             String className = pluginEl.getAttributeValue( "class" );
-            WikiPluginInfo pluginInfo = WikiPluginInfo.newInstance( className, pluginEl );
+            WikiPluginInfo pluginInfo = WikiPluginInfo.newInstance( className, pluginEl ,m_searchPath, m_externalJars);
 
             if( pluginInfo != null ) {
                 registerPlugin( pluginInfo );
@@ -557,7 +569,6 @@ public class DefaultPluginManager extend
      *  Contains information about a bunch of plugins.
      *
      *
-     *  @since
      */
     // FIXME: This class needs a better interface to return all sorts of possible
     //        information from the plugin XML.  In fact, it probably should have
@@ -576,11 +587,13 @@ public class DefaultPluginManager extend
          *  @param className Either a fully qualified class name, or a "short" name which is then
          *                   checked against the internal list of plugin packages.
          *  @param el A JDOM Element containing the information about this class.
+         *  @param searchPath A List of Strings, containing different package names.
+         *  @param externalJars the list of external jars to search
          *  @return A WikiPluginInfo object.
          */
-        protected static WikiPluginInfo newInstance( String className, Element el ) {
+        protected static WikiPluginInfo newInstance( String className, Element el, List<String> searchPath, List<String> externalJars ) {
             if( className == null || className.length() == 0 ) return null;
-            
+
             WikiPluginInfo info = new WikiPluginInfo( className );
             info.initializeFromXML( el );
             return info;
@@ -590,14 +603,16 @@ public class DefaultPluginManager extend
          *  Initializes a plugin, if it has not yet been initialized.
          *
          *  @param engine The WikiEngine
+         *  @param searchPath A List of Strings, containing different package names.
+         *  @param externalJars the list of external jars to search
          */
-        protected void initializePlugin( WikiEngine engine ) {
+        protected void initializePlugin( WikiEngine engine , List<String> searchPath, List<String> externalJars) {
             if( !m_initialized ) {
                 // This makes sure we only try once per class, even if init fails.
                 m_initialized = true;
 
                 try {
-                    WikiPlugin p = newPluginInstance();
+                    WikiPlugin p = newPluginInstance(searchPath, externalJars);
                     if( p instanceof InitializablePlugin ) {
                         ( ( InitializablePlugin )p ).initialize( engine );
                     }
@@ -655,14 +670,17 @@ public class DefaultPluginManager extend
         /**
          *  Creates a new plugin instance.
          *
+         *  @param searchPath A List of Strings, containing different package names.
+         *  @param externalJars the list of external jars to search
+
          *  @return A new plugin.
          *  @throws ClassNotFoundException If the class declared was not found.
          *  @throws InstantiationException If the class cannot be instantiated-
          *  @throws IllegalAccessException If the class cannot be accessed.
          */
-        public WikiPlugin newPluginInstance() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
+        public WikiPlugin newPluginInstance(List<String> searchPath, List<String> externalJars) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
             if( m_clazz == null ) {
-                m_clazz = Class.forName(m_className);
+                m_clazz = ClassUtil.findClass(searchPath, externalJars ,m_className);
             }
 
             return (WikiPlugin) m_clazz.newInstance();
@@ -774,7 +792,7 @@ public class DefaultPluginManager extend
                 String msg = "Plugin '" + pluginInfo.getName() + "' not compatible with this version of JSPWiki";
                 log.info( msg );
             } else {
-                plugin = pluginInfo.newPluginInstance();
+                plugin = pluginInfo.newPluginInstance(m_searchPath, m_externalJars);
             }
         } catch( ClassNotFoundException e ) {
             throw new PluginException( MessageFormat.format( rb.getString( "plugin.error.couldnotfind" ), pluginName ), e );

Modified: jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/util/ClassUtil.java
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/util/ClassUtil.java?rev=1566717&r1=1566716&r2=1566717&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/util/ClassUtil.java (original)
+++ jspwiki/trunk/jspwiki-war/src/main/java/org/apache/wiki/util/ClassUtil.java Mon Feb 10 19:49:56 2014
@@ -23,7 +23,9 @@ import java.io.IOException;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.net.JarURLConnection;
+import java.net.MalformedURLException;
 import java.net.URL;
+import java.net.URLClassLoader;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.Hashtable;
@@ -36,6 +38,8 @@ import java.util.jar.JarFile;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
+import org.apache.wiki.WikiEngine;
+import org.apache.wiki.api.engine.PluginManager;
 import org.apache.wiki.api.exceptions.WikiException;
 import org.jdom2.Element;
 
@@ -56,6 +60,10 @@ public final class ClassUtil {
     
     private static Map<String, String> c_classMappings = new Hashtable<String, String>();
 
+    private static boolean classLoaderSetup = false;
+    private static ClassLoader loader = null;
+
+
     /**
      *  Initialize the class mappings document.
      */
@@ -88,23 +96,24 @@ public final class ClassUtil {
      *  attempt to find the class based on just the className parameter, but
      *  should that fail, will iterate through the "packages" -list, prefixes
      *  the package name to the className, and then tries to find the class
-     *  again. If that still fails, we try the old (pre-2.9) com.ecyrd.jspwiki package.
+     *  again.
      *
-     *  @param packages A List of Strings, containing different package names.
+     * @param packages A List of Strings, containing different package names.
      *  @param className The name of the class to find.
-     *  @return The class, if it was found.
+     * @return The class, if it was found.
      *  @throws ClassNotFoundException if this particular class cannot be found
      *          from the list.
      */
-    public static Class<?> findClass( List< String > packages, String className ) throws ClassNotFoundException {
-        ClassLoader loader = ClassUtil.class.getClassLoader();
+    public static Class<?> findClass( List< String > packages,  List< String > externaljars, String className ) throws ClassNotFoundException {
+        if (!classLoaderSetup) {
+            loader = setupClassLoader(externaljars);
+        }
 
         try {
             return loader.loadClass( className );
         } catch( ClassNotFoundException e ) {
             for( Iterator< String > i = packages.iterator(); i.hasNext(); ) {
                 String packageName = i.next();
-
                 try {
                     return loader.loadClass( packageName + "." + className );
                 } catch( ClassNotFoundException ex ) {
@@ -112,21 +121,44 @@ public final class ClassUtil {
                 }
             }
 
-            // try the old (pre 2.9) package name for compatibility :
-            try {
-                className = className.replaceFirst( "com\\.ecyrd\\.jspwiki", "org.apache.wiki" );
-                return loader.loadClass( className );
-            } catch( ClassNotFoundException ex ) {
-                // This is okay, if we fail we throw our own CNFE..
-            }
-
         }
 
         throw new ClassNotFoundException( "Class '" + className + "' not found in search path!" );
     }
-    
+
+    /**
+     * Setup the plugin classloader.
+     * Check if there are external JARS to add via property {@link org.apache.wiki.api.engine.PluginManager#PROP_EXTERNALJARS}
+     *
+     * @return the classloader that can load classes from the configured external jars or
+     *         ,if not specified, the classloader that loaded this class.
+     * @param externaljars
+     */
+    private static ClassLoader setupClassLoader(List<String> externaljars) {
+        classLoaderSetup = true;
+        log.info("setting up classloaders for external (plugin) jars");
+        if (externaljars.size() == 0) {
+            log.info("no external jars configured, using standard classloading");
+            return ClassUtil.class.getClassLoader();
+        }
+        URL[] urls = new URL[externaljars.size()];
+        int i = 0;
+        try {
+            for (String externaljar : externaljars) {
+                File jarFile = new File(externaljar);
+                URL ucl = jarFile.toURI().toURL();
+                urls[i++] = ucl;
+                log.info("added " + ucl + " to list of external jars");
+            }
+        } catch (MalformedURLException e) {
+            log.error("exception while setting up classloaders for external jars via property" + PluginManager.PROP_EXTERNALJARS + ", continuing without external jars.");
+            return ClassUtil.class.getClassLoader();
+        }
+        return new URLClassLoader(urls, ClassUtil.class.getClassLoader());
+    }
+
     /**
-     *  A shortcut for findClass when you only have a singular package to search.
+     *
      *  It will first attempt to instantiate the class directly from the className,
      *  and will then try to prefix it with the packageName.
      *
@@ -136,11 +168,12 @@ public final class ClassUtil {
      *  @throws ClassNotFoundException if this particular class cannot be found.
      */
 
-    public static Class<?> findClass( String packageName, String className ) throws ClassNotFoundException {
-        ArrayList<String> list = new ArrayList<String>();
-        list.add( packageName );
-
-        return findClass( list, className );
+    public static Class<?> findClass(String packageName, String className) throws ClassNotFoundException {
+        try {
+            return ClassUtil.class.getClassLoader().loadClass(className);
+        } catch (ClassNotFoundException e) {
+            return ClassUtil.class.getClassLoader().loadClass(packageName + "." + className);
+        }
     }
     
     /**

Modified: jspwiki/trunk/jspwiki-war/src/main/resources/ini/jspwiki.properties
URL: http://svn.apache.org/viewvc/jspwiki/trunk/jspwiki-war/src/main/resources/ini/jspwiki.properties?rev=1566717&r1=1566716&r2=1566717&view=diff
==============================================================================
--- jspwiki/trunk/jspwiki-war/src/main/resources/ini/jspwiki.properties (original)
+++ jspwiki/trunk/jspwiki-war/src/main/resources/ini/jspwiki.properties Mon Feb 10 19:49:56 2014
@@ -392,6 +392,11 @@ jspwiki.specialPage.FindPage = FindPage.
 #
 jspwiki.plugin.searchPath =
 
+# You can specify external jars containing plugin classes. These will be added to the classpath
+# when plugins are loaded.
+# Using this you don't have to put the jars in your WEB-INF/lib directory thereby preventing war-surgery.
+jspwiki.plugin.externalJars =
+
 #############################################################################
 #
 #  Page filters