JSPWIKI-806: add the possibility of loading custom managers on WikiEngine (was: EntityManager Proposal)

* WikiEngine will look on classpath for a ini/classmappings-extra.xml file, with the same structure as ini/classmappings.xml
* if found, will register each requestedClass with its correspondent mappedClass
* these custom manager must have a no-arg constructor
* if there's a need to perform some initialization tasks querying the Engine, the custom manager should implement o.a.w.api.engine.Initializable and perform those tasks there
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/WikiEngine.java b/jspwiki-main/src/main/java/org/apache/wiki/WikiEngine.java
index 22ca833..7664c19 100644
--- a/jspwiki-main/src/main/java/org/apache/wiki/WikiEngine.java
+++ b/jspwiki-main/src/main/java/org/apache/wiki/WikiEngine.java
@@ -328,22 +328,22 @@
             final Class< ? > urlclass = ClassUtil.findClass( "org.apache.wiki.url", urlConstructorClassName );
 
             initComponent( CommandResolver.class, this, props );
-            initComponent( urlclass.getName(), URLConstructor.class, ( Object )null );
+            initComponent( urlclass.getName(), URLConstructor.class );
             initComponent( PageManager.class, this, props );
             initComponent( PluginManager.class, this, props );
             initComponent( DifferenceManager.class, this, props );
             initComponent( AttachmentManager.class, this, props );
             initComponent( VariableManager.class, props );
             initComponent( SearchManager.class, this, props );
-            initComponent( AuthenticationManager.class, ( Object )null );
-            initComponent( AuthorizationManager.class, ( Object )null );
-            initComponent( UserManager.class, ( Object )null );
-            initComponent( GroupManager.class, ( Object )null );
+            initComponent( AuthenticationManager.class );
+            initComponent( AuthorizationManager.class );
+            initComponent( UserManager.class );
+            initComponent( GroupManager.class );
             initComponent( EditorManager.class, this );
             initComponent( ProgressManager.class, this );
-            initComponent( aclClassName, AclManager.class, ( Object )null );
-            initComponent( WorkflowManager.class, ( Object )null );
-            initComponent( TasksManager.class, ( Object )null );
+            initComponent( aclClassName, AclManager.class );
+            initComponent( WorkflowManager.class );
+            initComponent( TasksManager.class );
             initComponent( InternationalizationManager.class, this );
             initComponent( TemplateManager.class, this, props );
             initComponent( FilterManager.class, this, props );
@@ -388,12 +388,26 @@
             log.error( "Unable to start RSS generator - JSPWiki will still work, but there will be no RSS feed.", e );
         }
 
+        final Map< String, String > extraComponents = ClassUtil.getExtraClassMappings();
+        initExtraComponents( extraComponents );
+
         fireEvent( WikiEngineEvent.INITIALIZED ); // initialization complete
 
         log.info( "WikiEngine configured." );
         m_isConfigured = true;
     }
 
+    void initExtraComponents( final Map< String, String > extraComponents ) {
+        for( final Map.Entry< String, String > extraComponent : extraComponents.entrySet() ) {
+            try {
+                log.info( "Registering on WikiEngine " + extraComponent.getKey() + " as " + extraComponent.getValue() );
+                initComponent( extraComponent.getKey(), Class.forName( extraComponent.getValue() ) );
+            } catch( final Exception e ) {
+                log.error( "Unable to start " + extraComponent.getKey(), e );
+            }
+        }
+    }
+
     < T > void initComponent( final Class< T > componentClass, final Object... initArgs ) throws Exception {
         initComponent( componentClass.getName(), componentClass, initArgs );
     }
@@ -405,10 +419,10 @@
         } else {
             component = ClassUtil.getMappedObject( componentInitClass, initArgs );
         }
+        managers.put( componentClass, component );
         if( Initializable.class.isAssignableFrom( componentClass ) ) {
             ( ( Initializable )component ).initialize( this, m_properties );
         }
-        managers.put( componentClass, component );
     }
 
     /** {@inheritDoc} */
diff --git a/jspwiki-util/src/main/java/org/apache/wiki/util/ClassUtil.java b/jspwiki-util/src/main/java/org/apache/wiki/util/ClassUtil.java
index bf51959..8fe90e2 100644
--- a/jspwiki-util/src/main/java/org/apache/wiki/util/ClassUtil.java
+++ b/jspwiki-util/src/main/java/org/apache/wiki/util/ClassUtil.java
@@ -52,29 +52,34 @@
     /** The location of the classmappings.xml document. It will be searched for in the classpath.  It's value is "{@value}". */
     public  static final String MAPPINGS = "ini/classmappings.xml";
 
-    private static Map< String, String > c_classMappings = new ConcurrentHashMap<>();
+    /** The location of the classmappings-extra.xml document. It will be searched for in the classpath.  It's value is "{@value}". */
+    public  static final String MAPPINGS_EXTRA = "ini/classmappings-extra.xml";
+
+    /** Initialize the class mappings document. */
+    private static Map< String, String > c_classMappings = populateClassMappingsFrom( MAPPINGS );
+
+    /** Initialize the class mappings extra document. */
+    private static Map< String, String > c_classMappingsExtra = populateClassMappingsFrom( MAPPINGS_EXTRA ) ;
 
     private static boolean classLoaderSetup = false;
     private static ClassLoader loader = null;
 
-
-    /*
-     *  Initialize the class mappings document.
-     */
-    static {
-        final List< Element > nodes = XmlUtil.parse( MAPPINGS, "/classmappings/mapping" );
+    private static Map< String, String > populateClassMappingsFrom( final String fileLoc ) {
+        final Map< String, String > map = new ConcurrentHashMap<>();
+        final List< Element > nodes = XmlUtil.parse( fileLoc, "/classmappings/mapping" );
 
         if( nodes.size() > 0 ) {
             for( final Element f : nodes ) {
                 final String key = f.getChildText( "requestedClass" );
                 final String className = f.getChildText( "mappedClass" );
 
-                c_classMappings.put( key, className );
+                map.put( key, className );
                 log.debug( "Mapped class '" + key + "' to class '" + className + "'" );
             }
         } else {
             log.info( "Didn't find class mapping document in " + MAPPINGS );
         }
+        return map;
     }
 
     /**
@@ -343,5 +348,9 @@
         }
         return false;
     }
+
+    public static Map< String, String > getExtraClassMappings() {
+        return c_classMappingsExtra;
+    }
     
 }