JSPWIKI-120: rename + extract interfaces from InternationalizationManager, SearchManager, EditorManager and TemplateManager
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/i18n/DefaultInternationalizationManager.java b/jspwiki-main/src/main/java/org/apache/wiki/i18n/DefaultInternationalizationManager.java
new file mode 100644
index 0000000..a05ba13
--- /dev/null
+++ b/jspwiki-main/src/main/java/org/apache/wiki/i18n/DefaultInternationalizationManager.java
@@ -0,0 +1,39 @@
+/*
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.  
+ */
+package org.apache.wiki.i18n;
+
+import org.apache.wiki.api.core.Engine;
+
+
+/**
+ *  Manages all internationalization in JSPWiki.
+ *
+ *  @since 2.6
+ */
+public class DefaultInternationalizationManager implements InternationalizationManager {
+
+    /**
+     *  Constructs a new InternationalizationManager.
+     *
+     *  @param engine To which engine this belongs to
+     */
+    public DefaultInternationalizationManager( final Engine engine ) {
+    }
+
+}
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/i18n/InternationalizationManager.java b/jspwiki-main/src/main/java/org/apache/wiki/i18n/InternationalizationManager.java
index 6d2e07d..1feb01c 100644
--- a/jspwiki-main/src/main/java/org/apache/wiki/i18n/InternationalizationManager.java
+++ b/jspwiki-main/src/main/java/org/apache/wiki/i18n/InternationalizationManager.java
@@ -18,8 +18,6 @@
  */
 package org.apache.wiki.i18n;
 
-import org.apache.wiki.api.core.Engine;
-
 import java.text.MessageFormat;
 import java.util.Locale;
 import java.util.MissingResourceException;
@@ -31,32 +29,24 @@
  *
  *  @since 2.6
  */
-public class InternationalizationManager {
+public interface InternationalizationManager {
 
     /** The name of the ResourceBundle which contains any and all JSPWiki core resource strings.  It's value is {@value}. */
-    public static final String CORE_BUNDLE = "CoreResources";
+    String CORE_BUNDLE = "CoreResources";
     
     /** The name of the ResourceBundle which contains any and all JSPWiki default templates resource strings.  It's value is {@value}. */
-    public static final String DEF_TEMPLATE = "templates.default";
+    String DEF_TEMPLATE = "templates.default";
     // public static final String JSPWIKI_BUNDLE = "jspwiki";
     // public static final String PLUGINS_BUNDLE = "plugins";
 
     /**
-     *  Constructs a new InternationalizationManager.
-     *
-     *  @param engine To which engine this belongs to
-     */
-    public InternationalizationManager( final Engine engine ) {
-    }
-
-    /**
      *  Returns a String from the CORE_BUNDLE using English as the default locale.
      *
      *  @param key Key to find
      *  @return The English string
      *  @throws MissingResourceException If there is no such key
      */
-    public String get( final String key ) throws MissingResourceException {
+    default String get( final String key ) throws MissingResourceException {
         return get( CORE_BUNDLE, Locale.ENGLISH, key );
     }
     
@@ -68,7 +58,7 @@
      *  @return A localized string
      *  @throws MissingResourceException If the key cannot be located at all, even from the default locale.
      */
-    public ResourceBundle getBundle( final String bundle, Locale locale ) throws MissingResourceException {
+    default ResourceBundle getBundle( final String bundle, Locale locale ) throws MissingResourceException {
         if( locale == null ) {
             locale = Locale.getDefault();
         }
@@ -85,7 +75,7 @@
      *  @return A localized string (or from the default language, if not found)
      *  @throws MissingResourceException If the key cannot be located at all, even from the default locale.
      */
-    public String get( final String bundle, final Locale locale, final String key ) throws MissingResourceException {
+    default String get( final String bundle, final Locale locale, final String key ) throws MissingResourceException {
         return getBundle( bundle, locale ).getString( key );
     }
 
@@ -99,7 +89,7 @@
      *  @return A localized string (or from the default language, if not found)
      *  @throws MissingResourceException If the key cannot be located at all, even from the default locale.
      */
-    public String get( final String bundle, final Locale locale, final String key, final Object... args ) throws MissingResourceException {
+    default String get( final String bundle, final Locale locale, final String key, final Object... args ) throws MissingResourceException {
         final MessageFormat mf = new MessageFormat( get( bundle, locale, key ), locale );
         return mf.format( args );
     }
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/search/DefaultSearchManager.java b/jspwiki-main/src/main/java/org/apache/wiki/search/DefaultSearchManager.java
new file mode 100644
index 0000000..1831d92
--- /dev/null
+++ b/jspwiki-main/src/main/java/org/apache/wiki/search/DefaultSearchManager.java
@@ -0,0 +1,271 @@
+/*
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+ */
+package org.apache.wiki.search;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.StopWatch;
+import org.apache.log4j.Logger;
+import org.apache.wiki.WikiContext;
+import org.apache.wiki.WikiPage;
+import org.apache.wiki.ajax.AjaxUtil;
+import org.apache.wiki.ajax.WikiAjaxDispatcherServlet;
+import org.apache.wiki.ajax.WikiAjaxServlet;
+import org.apache.wiki.api.core.Engine;
+import org.apache.wiki.api.exceptions.FilterException;
+import org.apache.wiki.api.exceptions.NoRequiredPropertyException;
+import org.apache.wiki.api.filters.BasicPageFilter;
+import org.apache.wiki.event.WikiEvent;
+import org.apache.wiki.event.WikiEventManager;
+import org.apache.wiki.event.WikiPageEvent;
+import org.apache.wiki.pages.PageManager;
+import org.apache.wiki.parser.MarkupParser;
+import org.apache.wiki.references.ReferenceManager;
+import org.apache.wiki.util.ClassUtil;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+
+/**
+ *  Manages searching the Wiki.
+ *
+ *  @since 2.2.21.
+ */
+public class DefaultSearchManager extends BasicPageFilter implements SearchManager {
+
+    private static final Logger log = Logger.getLogger( DefaultSearchManager.class );
+
+    private SearchProvider m_searchProvider;
+
+    /**
+     *  Creates a new SearchManager.
+     *
+     *  @param engine The Engine that owns this SearchManager.
+     *  @param properties The list of Properties.
+     *  @throws FilterException If it cannot be instantiated.
+     */
+    public DefaultSearchManager( final Engine engine, final Properties properties ) throws FilterException {
+        initialize( engine, properties );
+        WikiEventManager.getInstance().addWikiEventListener( m_engine.getManager( PageManager.class ), this );
+
+        // TODO: Replace with custom annotations. See JSPWIKI-566
+        WikiAjaxDispatcherServlet.registerServlet( JSON_SEARCH, new JSONSearch() );
+    }
+
+    /**
+     *  Provides a JSON AJAX API to the JSPWiki Search Engine.
+     */
+    public class JSONSearch implements WikiAjaxServlet {
+
+        public static final String AJAX_ACTION_SUGGESTIONS = "suggestions";
+        public static final String AJAX_ACTION_PAGES = "pages";
+        public static final int DEFAULT_MAX_RESULTS = 20;
+        public int maxResults = DEFAULT_MAX_RESULTS;
+
+        /** {@inheritDoc} */
+        @Override
+        public String getServletMapping() {
+            return JSON_SEARCH;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void service( final HttpServletRequest req,
+                             final HttpServletResponse resp,
+                             final String actionName,
+                             final List< String > params ) throws IOException {
+            String result = "";
+            if( StringUtils.isNotBlank( actionName ) ) {
+                if( params.size() < 1 ) {
+                    return;
+                }
+                final String itemId = params.get( 0 );
+                log.debug( "itemId=" + itemId );
+                if( params.size() > 1 ) {
+                    final String maxResultsParam = params.get( 1 );
+                    log.debug( "maxResultsParam=" + maxResultsParam );
+                    if( StringUtils.isNotBlank( maxResultsParam ) && StringUtils.isNumeric( maxResultsParam ) ) {
+                        maxResults = Integer.parseInt( maxResultsParam );
+                    }
+                }
+
+                if( actionName.equals( AJAX_ACTION_SUGGESTIONS ) ) {
+                    log.debug( "Calling getSuggestions() START" );
+                    final List< String > callResults = getSuggestions( itemId, maxResults );
+                    log.debug( "Calling getSuggestions() DONE. " + callResults.size() );
+                    result = AjaxUtil.toJson( callResults );
+                } else if( actionName.equals( AJAX_ACTION_PAGES ) ) {
+                    log.debug("Calling findPages() START");
+                    final WikiContext wikiContext = new WikiContext( m_engine, req, WikiContext.VIEW );
+                    final List< Map< String, Object > > callResults = findPages( itemId, maxResults, wikiContext );
+                    log.debug( "Calling findPages() DONE. " + callResults.size() );
+                    result = AjaxUtil.toJson( callResults );
+                }
+            }
+            log.debug( "result=" + result );
+            resp.getWriter().write( result );
+        }
+
+        /**
+         *  Provides a list of suggestions to use for a page name. Currently the algorithm just looks into the value parameter,
+         *  and returns all page names from that.
+         *
+         *  @param wikiName the page name
+         *  @param maxLength maximum number of suggestions
+         *  @return the suggestions
+         */
+        public List< String > getSuggestions( String wikiName, final int maxLength ) {
+            final StopWatch sw = new StopWatch();
+            sw.start();
+            final List< String > list = new ArrayList<>( maxLength );
+            if( wikiName.length() > 0 ) {
+                // split pagename and attachment filename
+                String filename = "";
+                final int pos = wikiName.indexOf("/");
+                if( pos >= 0 ) {
+                    filename = wikiName.substring( pos ).toLowerCase();
+                    wikiName = wikiName.substring( 0, pos );
+                }
+
+                final String cleanWikiName = MarkupParser.cleanLink(wikiName).toLowerCase() + filename;
+                final String oldStyleName = MarkupParser.wikifyLink(wikiName).toLowerCase() + filename;
+                final Set< String > allPages = m_engine.getManager( ReferenceManager.class ).findCreated();
+
+                int counter = 0;
+                for( final Iterator< String > i = allPages.iterator(); i.hasNext() && counter < maxLength; ) {
+                    final String p = i.next();
+                    final String pp = p.toLowerCase();
+                    if( pp.startsWith( cleanWikiName) || pp.startsWith( oldStyleName ) ) {
+                        list.add( p );
+                        counter++;
+                    }
+                }
+            }
+
+            sw.stop();
+            if( log.isDebugEnabled() ) {
+                log.debug( "Suggestion request for " + wikiName + " done in " + sw );
+            }
+            return list;
+        }
+
+        /**
+         *  Performs a full search of pages.
+         *
+         *  @param searchString The query string
+         *  @param maxLength How many hits to return
+         *  @return the pages found
+         */
+        public List< Map< String, Object > > findPages( final String searchString, final int maxLength, final WikiContext wikiContext ) {
+            final StopWatch sw = new StopWatch();
+            sw.start();
+
+            final List< Map< String, Object > > list = new ArrayList<>( maxLength );
+            if( searchString.length() > 0 ) {
+                try {
+                    final Collection< SearchResult > c;
+                    if( m_searchProvider instanceof LuceneSearchProvider ) {
+                        c = ( ( LuceneSearchProvider )m_searchProvider ).findPages( searchString, 0, wikiContext );
+                    } else {
+                        c = m_searchProvider.findPages( searchString, wikiContext );
+                    }
+
+                    int count = 0;
+                    for( final Iterator< SearchResult > i = c.iterator(); i.hasNext() && count < maxLength; count++ ) {
+                        final SearchResult sr = i.next();
+                        final HashMap< String, Object > hm = new HashMap<>();
+                        hm.put( "page", sr.getPage().getName() );
+                        hm.put( "score", sr.getScore() );
+                        list.add( hm );
+                    }
+                } catch( final Exception e ) {
+                    log.info( "AJAX search failed; ", e );
+                }
+            }
+
+            sw.stop();
+            if( log.isDebugEnabled() ) {
+                log.debug( "AJAX search complete in " + sw );
+            }
+            return list;
+        }
+    }
+
+
+    /** {@inheritDoc} */
+    @Override
+    public void initialize( final Engine engine, final Properties properties ) throws FilterException {
+        m_engine = engine;
+        loadSearchProvider(properties);
+
+        try {
+            m_searchProvider.initialize( engine, properties );
+        } catch( final NoRequiredPropertyException | IOException e ) {
+            log.error( e.getMessage(), e );
+        }
+    }
+
+    private void loadSearchProvider( final Properties properties ) {
+        // See if we're using Lucene, and if so, ensure that its index directory is up to date.
+        final String providerClassName = properties.getProperty( PROP_SEARCHPROVIDER, DEFAULT_SEARCHPROVIDER );
+
+        try {
+            final Class<?> providerClass = ClassUtil.findClass( "org.apache.wiki.search", providerClassName );
+            m_searchProvider = ( SearchProvider )providerClass.newInstance();
+        } catch( final ClassNotFoundException | InstantiationException | IllegalAccessException e ) {
+            log.warn("Failed loading SearchProvider, will use BasicSearchProvider.", e);
+        }
+
+        if( null == m_searchProvider ) {
+            // FIXME: Make a static with the default search provider
+            m_searchProvider = new BasicSearchProvider();
+        }
+        log.debug("Loaded search provider " + m_searchProvider);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public SearchProvider getSearchEngine()
+    {
+        return m_searchProvider;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void actionPerformed( final WikiEvent event ) {
+        if( event instanceof WikiPageEvent && event.getType() == WikiPageEvent.PAGE_DELETE_REQUEST ) {
+            final String pageName = ( ( WikiPageEvent ) event ).getPageName();
+
+            final WikiPage p = m_engine.getManager( PageManager.class ).getPage( pageName );
+            if( p != null ) {
+                pageRemoved( p );
+            }
+        }
+    }
+
+}
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/search/SearchManager.java b/jspwiki-main/src/main/java/org/apache/wiki/search/SearchManager.java
index dcc0eae..131be40 100644
--- a/jspwiki-main/src/main/java/org/apache/wiki/search/SearchManager.java
+++ b/jspwiki-main/src/main/java/org/apache/wiki/search/SearchManager.java
@@ -18,269 +18,41 @@
  */
 package org.apache.wiki.search;
 
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.time.StopWatch;
-import org.apache.log4j.Logger;
 import org.apache.wiki.WikiContext;
 import org.apache.wiki.WikiPage;
-import org.apache.wiki.ajax.AjaxUtil;
-import org.apache.wiki.ajax.WikiAjaxDispatcherServlet;
-import org.apache.wiki.ajax.WikiAjaxServlet;
-import org.apache.wiki.api.core.Engine;
-import org.apache.wiki.api.exceptions.FilterException;
-import org.apache.wiki.api.exceptions.NoRequiredPropertyException;
 import org.apache.wiki.api.exceptions.ProviderException;
-import org.apache.wiki.api.filters.BasicPageFilter;
-import org.apache.wiki.event.WikiEvent;
+import org.apache.wiki.api.filters.PageFilter;
 import org.apache.wiki.event.WikiEventListener;
-import org.apache.wiki.event.WikiEventManager;
-import org.apache.wiki.event.WikiPageEvent;
 import org.apache.wiki.modules.InternalModule;
-import org.apache.wiki.pages.PageManager;
-import org.apache.wiki.parser.MarkupParser;
-import org.apache.wiki.references.ReferenceManager;
-import org.apache.wiki.util.ClassUtil;
 
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
+
 
 /**
  *  Manages searching the Wiki.
  *
  *  @since 2.2.21.
  */
-public class SearchManager extends BasicPageFilter implements InternalModule, WikiEventListener {
+public interface SearchManager extends PageFilter, InternalModule, WikiEventListener {
 
-    private static final Logger log = Logger.getLogger( SearchManager.class );
-
-    private static final String DEFAULT_SEARCHPROVIDER  = "org.apache.wiki.search.LuceneSearchProvider";
+    String DEFAULT_SEARCHPROVIDER = "org.apache.wiki.search.LuceneSearchProvider";
 
     /** Property name for setting the search provider. Value is <tt>{@value}</tt>. */
-    public static final String PROP_SEARCHPROVIDER      = "jspwiki.searchProvider";
-
-    private SearchProvider    m_searchProvider;
+    String PROP_SEARCHPROVIDER = "jspwiki.searchProvider";
 
     /** The name of the JSON object that manages search. */
-    public static final String JSON_SEARCH = "search";
-
-    /**
-     *  Creates a new SearchManager.
-     *
-     *  @param engine The Engine that owns this SearchManager.
-     *  @param properties The list of Properties.
-     *  @throws FilterException If it cannot be instantiated.
-     */
-    public SearchManager( final Engine engine, final Properties properties ) throws FilterException {
-        initialize( engine, properties );
-        WikiEventManager.getInstance().addWikiEventListener( m_engine.getManager( PageManager.class ), this );
-
-        //TODO: Replace with custom annotations. See JSPWIKI-566
-        WikiAjaxDispatcherServlet.registerServlet( JSON_SEARCH, new JSONSearch() );
-    }
-
-    /**
-     *  Provides a JSON RPC API to the JSPWiki Search Engine.
-     */
-    public class JSONSearch implements WikiAjaxServlet {
-
-        public static final String AJAX_ACTION_SUGGESTIONS = "suggestions";
-        public static final String AJAX_ACTION_PAGES = "pages";
-        public static final int DEFAULT_MAX_RESULTS = 20;
-        public int maxResults = DEFAULT_MAX_RESULTS;
-
-        @Override
-        public String getServletMapping() {
-            return JSON_SEARCH;
-        }
-
-        @Override
-        public void service( final HttpServletRequest req,
-                             final HttpServletResponse resp,
-                             final String actionName,
-                             final List< String > params ) throws ServletException, IOException {
-            String result = "";
-            if( StringUtils.isNotBlank( actionName ) ) {
-                if( params.size() < 1 ) {
-                    return;
-                }
-                final String itemId = params.get( 0 );
-                log.debug( "itemId=" + itemId );
-                if( params.size() > 1 ) {
-                    final String maxResultsParam = params.get( 1 );
-                    log.debug( "maxResultsParam=" + maxResultsParam );
-                    if( StringUtils.isNotBlank( maxResultsParam ) && StringUtils.isNumeric( maxResultsParam ) ) {
-                        maxResults = Integer.parseInt( maxResultsParam );
-                    }
-                }
-
-                if( actionName.equals( AJAX_ACTION_SUGGESTIONS ) ) {
-                    log.debug( "Calling getSuggestions() START" );
-                    final List< String > callResults = getSuggestions( itemId, maxResults );
-                    log.debug( "Calling getSuggestions() DONE. " + callResults.size() );
-                    result = AjaxUtil.toJson( callResults );
-                } else if( actionName.equals( AJAX_ACTION_PAGES ) ) {
-                    log.debug("Calling findPages() START");
-                    final WikiContext wikiContext = new WikiContext( m_engine, req, WikiContext.VIEW );
-                    final List< Map< String, Object > > callResults = findPages( itemId, maxResults, wikiContext );
-                    log.debug( "Calling findPages() DONE. " + callResults.size() );
-                    result = AjaxUtil.toJson( callResults );
-                }
-            }
-            log.debug( "result=" + result );
-            resp.getWriter().write( result );
-        }
-
-        /**
-         *  Provides a list of suggestions to use for a page name. Currently the algorithm just looks into the value parameter,
-         *  and returns all page names from that.
-         *
-         *  @param wikiName the page name
-         *  @param maxLength maximum number of suggestions
-         *  @return the suggestions
-         */
-        public List< String > getSuggestions( String wikiName, final int maxLength ) {
-            final StopWatch sw = new StopWatch();
-            sw.start();
-            final List< String > list = new ArrayList<>( maxLength );
-
-            if( wikiName.length() > 0 ) {
-                // split pagename and attachment filename
-                String filename = "";
-                final int pos = wikiName.indexOf("/");
-                if( pos >= 0 ) {
-                    filename = wikiName.substring( pos ).toLowerCase();
-                    wikiName = wikiName.substring( 0, pos );
-                }
-
-                final String cleanWikiName = MarkupParser.cleanLink(wikiName).toLowerCase() + filename;
-                final String oldStyleName = MarkupParser.wikifyLink(wikiName).toLowerCase() + filename;
-                final Set< String > allPages = m_engine.getManager( ReferenceManager.class ).findCreated();
-
-                int counter = 0;
-                for( final Iterator< String > i = allPages.iterator(); i.hasNext() && counter < maxLength; ) {
-                    final String p = i.next();
-                    final String pp = p.toLowerCase();
-                    if( pp.startsWith( cleanWikiName) || pp.startsWith( oldStyleName ) ) {
-                        list.add( p );
-                        counter++;
-                    }
-                }
-            }
-
-            sw.stop();
-            if( log.isDebugEnabled() ) {
-                log.debug( "Suggestion request for " + wikiName + " done in " + sw );
-            }
-            return list;
-        }
-
-        /**
-         *  Performs a full search of pages.
-         *
-         *  @param searchString The query string
-         *  @param maxLength How many hits to return
-         *  @return the pages found
-         */
-        public List< Map< String, Object > > findPages( final String searchString, final int maxLength, final WikiContext wikiContext ) {
-            final StopWatch sw = new StopWatch();
-            sw.start();
-
-            final List< Map< String, Object > > list = new ArrayList<>( maxLength );
-            if( searchString.length() > 0 ) {
-                try {
-                    final Collection< SearchResult > c;
-
-                    if( m_searchProvider instanceof LuceneSearchProvider ) {
-                        c = ( ( LuceneSearchProvider )m_searchProvider ).findPages( searchString, 0, wikiContext );
-                    } else {
-                        c = m_searchProvider.findPages( searchString, wikiContext );
-                    }
-
-                    int count = 0;
-                    for( final Iterator< SearchResult > i = c.iterator(); i.hasNext() && count < maxLength; count++ ) {
-                        final SearchResult sr = i.next();
-                        final HashMap< String, Object > hm = new HashMap<>();
-                        hm.put( "page", sr.getPage().getName() );
-                        hm.put( "score", sr.getScore() );
-                        list.add( hm );
-                    }
-                } catch( final Exception e ) {
-                    log.info( "AJAX search failed; ", e );
-                }
-            }
-
-            sw.stop();
-            if( log.isDebugEnabled() ) {
-                log.debug( "AJAX search complete in " + sw );
-            }
-            return list;
-        }
-    }
-
-
-    /**
-     *  This particular method starts off indexing and all sorts of various activities,
-     *  so you need to run this last, after things are done.
-     *
-     * @param engine the wiki engine
-     * @param properties the properties used to initialize the wiki engine
-     * @throws FilterException if the search provider failed to initialize
-     */
-    @Override
-    public void initialize( final Engine engine, final Properties properties ) throws FilterException {
-        m_engine = engine;
-        loadSearchProvider(properties);
-
-        try {
-            m_searchProvider.initialize( engine, properties );
-        } catch( final NoRequiredPropertyException | IOException e ) {
-            log.error( e.getMessage(), e );
-        }
-    }
-
-    private void loadSearchProvider( final Properties properties ) {
-        //
-        // See if we're using Lucene, and if so, ensure that its index directory is up to date.
-        //
-        final String providerClassName = properties.getProperty( PROP_SEARCHPROVIDER, DEFAULT_SEARCHPROVIDER );
-
-        try {
-            final Class<?> providerClass = ClassUtil.findClass( "org.apache.wiki.search", providerClassName );
-            m_searchProvider = (SearchProvider)providerClass.newInstance();
-        } catch( final ClassNotFoundException | InstantiationException | IllegalAccessException e ) {
-            log.warn("Failed loading SearchProvider, will use BasicSearchProvider.", e);
-        }
-
-        if( null == m_searchProvider ) {
-            // FIXME: Make a static with the default search provider
-            m_searchProvider = new BasicSearchProvider();
-        }
-        log.debug("Loaded search provider " + m_searchProvider);
-    }
+    String JSON_SEARCH = "search";
 
     /**
      *  Returns the SearchProvider used.
      *
      *  @return The current SearchProvider.
      */
-    public SearchProvider getSearchEngine()
-    {
-        return m_searchProvider;
-    }
+    SearchProvider getSearchEngine();
 
     /**
-     *  Sends a search to the current search provider. The query is is whatever native format
-     *  the query engine wants to use.
+     *  Sends a search to the current search provider. The query is is whatever native format the query engine wants to use.
      *
      * @param query The query.  Null is safe, and is interpreted as an empty query.
      * @param wikiContext the context within which to run the search
@@ -288,33 +60,20 @@
      * @throws ProviderException If the provider fails and a search cannot be completed.
      * @throws IOException If something else goes wrong.
      */
-    public Collection< SearchResult > findPages( String query, final WikiContext wikiContext ) throws ProviderException, IOException {
+    default Collection< SearchResult > findPages( String query, final WikiContext wikiContext ) throws ProviderException, IOException {
         if( query == null ) {
             query = "";
         }
-        return m_searchProvider.findPages( query, wikiContext );
+        return getSearchEngine().findPages( query, wikiContext );
     }
 
     /**
      *  Removes the page from the search cache (if any).
+     *
      *  @param page  The page to remove
      */
-    public void pageRemoved( final WikiPage page )
-    {
-        m_searchProvider.pageRemoved(page);
-    }
-
-    /**
-     *  Reindexes the page.
-     *
-     *  @param wikiContext {@inheritDoc}
-     *  @param content {@inheritDoc}
-     */
-    @Override
-    public void postSave( final WikiContext wikiContext, final String content ) {
-        //  Makes sure that we're indexing the latest version of this page.
-        final WikiPage p = m_engine.getManager( PageManager.class ).getPage( wikiContext.getPage().getName() );
-        reindexPage( p );
+    default void pageRemoved( final WikiPage page ) {
+        getSearchEngine().pageRemoved( page );
     }
 
     /**
@@ -322,26 +81,8 @@
      *
      *   @param page The page.
      */
-    public void reindexPage( final WikiPage page )
-    {
-        m_searchProvider.reindexPage( page );
-    }
-
-    /**
-     *  If the page has been deleted, removes it from the index.
-     *
-     *  @param event {@inheritDoc}
-     */
-    @Override
-    public void actionPerformed( final WikiEvent event ) {
-        if( event instanceof WikiPageEvent && event.getType() == WikiPageEvent.PAGE_DELETE_REQUEST ) {
-            final String pageName = ( ( WikiPageEvent ) event ).getPageName();
-
-            final WikiPage p = m_engine.getManager( PageManager.class ).getPage( pageName );
-            if( p != null ) {
-                pageRemoved( p );
-            }
-        }
+    default void reindexPage( final WikiPage page ) {
+        getSearchEngine().reindexPage( page );
     }
 
 }
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/ui/DefaultEditorManager.java b/jspwiki-main/src/main/java/org/apache/wiki/ui/DefaultEditorManager.java
new file mode 100644
index 0000000..71a1cb4
--- /dev/null
+++ b/jspwiki-main/src/main/java/org/apache/wiki/ui/DefaultEditorManager.java
@@ -0,0 +1,190 @@
+/*
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+ */
+package org.apache.wiki.ui;
+
+import org.apache.log4j.Logger;
+import org.apache.wiki.WikiContext;
+import org.apache.wiki.api.core.Engine;
+import org.apache.wiki.api.exceptions.NoSuchVariableException;
+import org.apache.wiki.modules.ModuleManager;
+import org.apache.wiki.modules.WikiModuleInfo;
+import org.apache.wiki.preferences.Preferences;
+import org.apache.wiki.util.XmlUtil;
+import org.apache.wiki.variables.VariableManager;
+import org.jdom2.Element;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+
+/**
+ *  Defines an editor manager.  An editor can be added by adding a suitable JSP file under templates/default/editors
+ *  If you want your editor to include any scripts or something, you can simply request it by adding the following in your
+ *  {@code ini/jspwiki_module.xml}:
+ *
+ *  <pre>
+ *  &lt;modules>
+ *   &lt;editor name="myeditor">
+ *       &lt;author>Janne Jalkanen&lt;/author>
+ *       &lt;script>foo.js&lt;/script>
+ *       &lt;stylesheet>foo.css&lt;/stylesheet>
+ *       &lt;path>editors/myeditor.jsp&lt;/path>
+ *   &lt;/editor>
+ *  &lt;/modules>
+ *  </pre>
+ *
+ *  @since 2.4
+ */
+public class DefaultEditorManager extends ModuleManager implements EditorManager {
+
+    private Map< String, WikiEditorInfo > m_editors;
+
+    private static final Logger log = Logger.getLogger( DefaultEditorManager.class );
+
+    public DefaultEditorManager( final Engine engine ) {
+        super( engine );
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void initialize( final Properties props ) {
+        registerEditors();
+    }
+
+    /** This method goes through the jspwiki_module.xml files and hunts for editors. Any editors found are put in the registry. */
+    private void registerEditors() {
+        log.info( "Registering editor modules" );
+        m_editors = new HashMap<>();
+
+        // Register all editors which have created a resource containing its properties. Get all resources of all modules
+        final List< Element > editors = XmlUtil.parse( PLUGIN_RESOURCE_LOCATION, "/modules/editor" );
+        for( final Element pluginEl : editors ) {
+            final String name = pluginEl.getAttributeValue( "name" );
+            final WikiEditorInfo info = WikiEditorInfo.newInstance( name, pluginEl );
+
+            if( checkCompatibility( info ) ) {
+                m_editors.put( name, info );
+                log.debug( "Registered editor " + name );
+            } else {
+                log.info( "Editor '" + name + "' not compatible with this version of JSPWiki." );
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getEditorName( final WikiContext context ) {
+        if( context.getRequestContext().equals( WikiContext.PREVIEW ) ) {
+            return EDITOR_PREVIEW;
+        }
+
+        // User has set an editor in preferences
+        String editor = Preferences.getPreference( context, PARA_EDITOR );
+
+        /* FIXME: actual default 'editor' property is read by the Preferences class */
+        if( editor == null ) {
+            // or use the default editor in jspwiki.properties
+            try {
+                editor = m_engine.getManager( VariableManager.class ).getValue( context, PROP_EDITORTYPE );
+            } catch( final NoSuchVariableException e ) {} // This is fine
+        }
+
+        if( editor != null ) {
+            final String[] editorlist = getEditorList();
+            editor = editor.trim();
+            for( final String s : editorlist ) {
+                if( s.equalsIgnoreCase( editor ) ) {
+                    return s;
+                }
+            }
+        }
+
+        return EDITOR_PLAIN;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String[] getEditorList() {
+        final String[] editors = new String[ m_editors.size() ];
+        final Set< String > keys = m_editors.keySet();
+
+        return keys.toArray( editors );
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getEditorPath( final WikiContext context ) {
+        final String editor = getEditorName( context );
+        final WikiEditorInfo ed = m_editors.get( editor );
+        final String path;
+        if( ed != null ) {
+            path = ed.getPath();
+        } else {
+            path = "editors/"+editor+".jsp";
+        }
+
+        return path;
+    }
+
+    /**  Contains info about an editor. */
+    private static final class WikiEditorInfo extends WikiModuleInfo {
+        private String m_path;
+
+        protected static WikiEditorInfo newInstance( final String name, final Element el ) {
+            if( name == null || name.length() == 0 ) {
+                return null;
+            }
+            final WikiEditorInfo info = new WikiEditorInfo( name );
+            info.initializeFromXML( el );
+            return info;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        protected void initializeFromXML( final Element el ) {
+            super.initializeFromXML( el );
+            m_path = el.getChildText("path");
+        }
+
+        private WikiEditorInfo( final String name ) {
+            super( name );
+        }
+
+        public String getPath() {
+            return m_path;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection< WikiModuleInfo > modules() {
+        return modules( m_editors.values().iterator() );
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public WikiEditorInfo getModuleInfo( final String moduleName ) {
+        return m_editors.get( moduleName );
+    }
+
+}
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/ui/DefaultTemplateManager.java b/jspwiki-main/src/main/java/org/apache/wiki/ui/DefaultTemplateManager.java
new file mode 100644
index 0000000..85065fe
--- /dev/null
+++ b/jspwiki-main/src/main/java/org/apache/wiki/ui/DefaultTemplateManager.java
@@ -0,0 +1,353 @@
+/*
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+ */
+package org.apache.wiki.ui;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.log4j.Logger;
+import org.apache.wiki.InternalWikiException;
+import org.apache.wiki.WikiContext;
+import org.apache.wiki.api.core.Engine;
+import org.apache.wiki.modules.ModuleManager;
+import org.apache.wiki.modules.WikiModuleInfo;
+import org.apache.wiki.preferences.Preferences;
+import org.apache.wiki.preferences.Preferences.TimeFormat;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.jsp.PageContext;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.TreeSet;
+
+
+/**
+ *  This class takes care of managing JSPWiki templates.  This class also provides the ResourceRequest mechanism.
+ *
+ *  @since 2.1.62
+ */
+public class DefaultTemplateManager extends ModuleManager implements TemplateManager {
+
+    private static final Logger log = Logger.getLogger( DefaultTemplateManager.class );
+
+    /**
+     *  Creates a new TemplateManager.  There is typically one manager per engine.
+     *
+     *  @param engine The owning engine.
+     *  @param properties The property list used to initialize this.
+     */
+    public DefaultTemplateManager( final Engine engine, final Properties properties ) {
+        super( engine );
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    // FIXME: Does not work yet
+    public boolean templateExists( final String templateName ) {
+        final ServletContext context = m_engine.getServletContext();
+        try( final InputStream in = context.getResourceAsStream( getPath( templateName ) + "ViewTemplate.jsp" ) ) {
+            if( in != null ) {
+                return true;
+            }
+        } catch( final IOException e ) {
+            log.error( e.getMessage(), e );
+        }
+        return false;
+    }
+
+    /**
+     *  Tries to locate a given resource from the template directory. If the given resource is not found under the current name, returns the
+     *  path to the corresponding one in the default template.
+     *
+     *  @param sContext The servlet context
+     *  @param name The name of the resource
+     *  @return The name of the resource which was found.
+     */
+    private static String findResource( final ServletContext sContext, final String name ) {
+        String resourceName = name;
+        try( final InputStream is = sContext.getResourceAsStream( resourceName ) ) {
+            if( is == null ) {
+                final String defname = makeFullJSPName( DEFAULT_TEMPLATE, removeTemplatePart( resourceName ) );
+                try( final InputStream iis = sContext.getResourceAsStream( defname ) ) {
+                    resourceName = iis != null ? defname : null;
+                }
+            }
+        } catch( final IOException e ) {
+            log.error( "unable to open " + name + " as resource stream", e );
+        }
+        return resourceName;
+    }
+
+    /**
+     *  Attempts to find a resource from the given template, and if it's not found
+     *  attempts to locate it from the default template.
+     * @param sContext servlet context used to search for the resource
+     * @param template template used to search for the resource
+     * @param name resource name
+     * @return the Resource for the given template and name.
+     */
+    private static String findResource( final ServletContext sContext, final String template, final String name ) {
+        if( name.charAt(0) == '/' ) {
+            // This is already a full path
+            return findResource( sContext, name );
+        }
+        final String fullname = makeFullJSPName( template, name );
+        return findResource( sContext, fullname );
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String findJSP( final PageContext pageContext, final String name ) {
+        final ServletContext sContext = pageContext.getServletContext();
+        return findResource( sContext, name );
+    }
+
+    /**
+     *  Removes the template part of a name.
+     */
+    private static String removeTemplatePart( String name ) {
+        int idx = 0;
+        if( name.startsWith( "/" ) ) {
+            idx = 1;
+        }
+
+        idx = name.indexOf('/', idx);
+        if( idx != -1 ) {
+            idx = name.indexOf('/', idx+1); // Find second "/"
+            if( idx != -1 ) {
+                name = name.substring( idx+1 );
+            }
+        }
+
+        if( log.isDebugEnabled() ) {
+            log.debug( "Final name = "+name );
+        }
+        return name;
+    }
+
+    /**
+     *  Returns the full name (/templates/foo/bar) for name=bar, template=foo.
+     *
+     * @param template The name of the template.
+     * @param name The name of the resource.
+     * @return The full name for a template.
+     */
+    private static String makeFullJSPName( final String template, final String name ) {
+        return "/" + DIRECTORY + "/" + template + "/" + name;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String findJSP( final PageContext pageContext, final String template, final String name ) {
+        if( name == null || template == null ) {
+            log.fatal("findJSP() was asked to find a null template or name (" + template + "," + name + ")." + " JSP page '" +
+                      ( ( HttpServletRequest )pageContext.getRequest() ).getRequestURI() + "'" );
+            throw new InternalWikiException( "Illegal arguments to findJSP(); please check logs." );
+        }
+
+        return findResource( pageContext.getServletContext(), template, name );
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String findResource( final WikiContext ctx, final String template, final String name ) {
+        if( m_engine.getServletContext() != null ) {
+            return findResource( m_engine.getServletContext(), template, name );
+        }
+
+        return getPath(template)+"/"+name;
+    }
+
+    /*
+     *  Returns a property, as defined in the template.  The evaluation is lazy, i.e. the properties are not loaded until the template is
+     *  actually used for the first time.
+     */
+    /*
+    public String getTemplateProperty( WikiContext context, String key )
+    {
+        String template = context.getTemplate();
+
+        try
+        {
+            Properties props = (Properties)m_propertyCache.getFromCache( template, -1 );
+
+            if( props == null )
+            {
+                try
+                {
+                    props = getTemplateProperties( template );
+
+                    m_propertyCache.putInCache( template, props );
+                }
+                catch( IOException e )
+                {
+                    log.warn("IO Exception while reading template properties",e);
+
+                    return null;
+                }
+            }
+
+            return props.getProperty( key );
+        }
+        catch( NeedsRefreshException ex )
+        {
+            // FIXME
+            return null;
+        }
+    }
+*/
+    /**
+     *  Returns an absolute path to a given template.
+     */
+    private static String getPath( final String template ) {
+        return "/" + DIRECTORY + "/" + template + "/";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Set< String > listSkins( final PageContext pageContext, final String template ) {
+        final String place = makeFullJSPName( template, SKIN_DIRECTORY );
+        final ServletContext sContext = pageContext.getServletContext();
+        final Set< String > skinSet = sContext.getResourcePaths( place );
+        final Set< String > resultSet = new TreeSet<>();
+
+        if( log.isDebugEnabled() ) {
+            log.debug( "Listings skins from " + place );
+        }
+
+        if( skinSet != null ) {
+            final String[] skins = skinSet.toArray( new String[]{} );
+            for( final String skin : skins ) {
+                final String[] s = StringUtils.split( skin, "/" );
+                if( s.length > 2 && skin.endsWith( "/" ) ) {
+                    final String skinName = s[ s.length - 1 ];
+                    resultSet.add( skinName );
+                    if( log.isDebugEnabled() ) {
+                        log.debug( "...adding skin '" + skinName + "'" );
+                    }
+                }
+            }
+        }
+
+        return resultSet;
+    }
+
+
+    /** {@inheritDoc} */
+    @Override
+    public Map< String, String > listTimeFormats( final PageContext pageContext ) {
+        final WikiContext context = WikiContext.findContext( pageContext );
+        final Properties props = m_engine.getWikiProperties();
+        final ArrayList< String > tfArr = new ArrayList<>(40);
+        final LinkedHashMap< String, String > resultMap = new LinkedHashMap<>();
+
+        /* filter timeformat properties */
+        for( final Enumeration< ? > e = props.propertyNames(); e.hasMoreElements(); ) {
+            final String name = ( String )e.nextElement();
+            if( name.startsWith( TIMEFORMATPROPERTIES ) ) {
+                tfArr.add( name );
+            }
+        }
+
+        /* fetch actual formats */
+        if( tfArr.size() == 0 )  {/* no props found - make sure some default formats are avail */
+            tfArr.add( "dd-MMM-yy" );
+            tfArr.add( "d-MMM-yyyy" );
+            tfArr.add( "EEE, dd-MMM-yyyy, zzzz" );
+        } else {
+            Collections.sort( tfArr );
+
+            for (int i = 0; i < tfArr.size(); i++) {
+                tfArr.set(i, props.getProperty(tfArr.get(i)));
+            }
+        }
+
+        final String prefTimeZone = Preferences.getPreference( context, "TimeZone" );
+        final TimeZone tz = TimeZone.getTimeZone( prefTimeZone );
+
+        final Date d = new Date(); // current date
+        try {
+            // dummy format pattern
+            final SimpleDateFormat fmt = Preferences.getDateFormat( context, TimeFormat.DATETIME );
+            fmt.setTimeZone( tz );
+
+            for( final String s : tfArr ) {
+                try {
+                    final String f = s;
+                    fmt.applyPattern( f );
+                    resultMap.put( f, fmt.format( d ) );
+                } catch( final IllegalArgumentException e ) {
+                } // skip parameter
+            }
+        } catch( final IllegalArgumentException e ) {} // skip parameter
+
+        return resultMap;
+    }
+
+    /*
+     *  Always returns a valid property map.
+     */
+    /*
+    private Properties getTemplateProperties( String templateName )
+        throws IOException
+    {
+        Properties p = new Properties();
+
+        ServletContext context = m_engine.getServletContext();
+
+        InputStream propertyStream = context.getResourceAsStream(getPath(templateName)+PROPERTYFILE);
+
+        if( propertyStream != null )
+        {
+            p.load( propertyStream );
+
+            propertyStream.close();
+        }
+        else
+        {
+            log.debug("Template '"+templateName+"' does not have a propertyfile '"+PROPERTYFILE+"'.");
+        }
+
+        return p;
+    }
+*/
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection< WikiModuleInfo > modules() {
+        return new ArrayList<>();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public WikiModuleInfo getModuleInfo( final String moduleName ) {
+    	return null;
+    }
+
+}
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/ui/EditorManager.java b/jspwiki-main/src/main/java/org/apache/wiki/ui/EditorManager.java
index 9d173fd..31b0c80 100644
--- a/jspwiki-main/src/main/java/org/apache/wiki/ui/EditorManager.java
+++ b/jspwiki-main/src/main/java/org/apache/wiki/ui/EditorManager.java
@@ -18,24 +18,12 @@
  */
 package org.apache.wiki.ui;
 
-import org.apache.log4j.Logger;
 import org.apache.wiki.WikiContext;
-import org.apache.wiki.api.core.Engine;
-import org.apache.wiki.api.exceptions.NoSuchVariableException;
-import org.apache.wiki.modules.ModuleManager;
 import org.apache.wiki.modules.WikiModuleInfo;
-import org.apache.wiki.preferences.Preferences;
-import org.apache.wiki.util.XmlUtil;
-import org.apache.wiki.variables.VariableManager;
-import org.jdom2.Element;
 
 import javax.servlet.jsp.PageContext;
 import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
 import java.util.Properties;
-import java.util.Set;
 
 
 /**
@@ -56,68 +44,32 @@
  *
  *  @since 2.4
  */
-public class EditorManager extends ModuleManager {
+public interface EditorManager {
 
     /** The property name for setting the editor. Current value is "jspwiki.editor" - not used anymore: replaced by defaultpref.template.editor */
-    public static final String PROP_EDITORTYPE = "jspwiki.editor";
+    String PROP_EDITORTYPE = "jspwiki.editor";
 
     /** Parameter for changing editors at run-time */
-    public static final String PARA_EDITOR = "editor";
+    String PARA_EDITOR = "editor";
 
     /** Known name for the plain wikimarkup editor. */
-    public static final String EDITOR_PLAIN = "plain";
+    String EDITOR_PLAIN = "plain";
 
     /** Known name for the preview editor component. */
-    public static final String EDITOR_PREVIEW = "preview";
+    String EDITOR_PREVIEW = "preview";
 
     /** Known attribute name for storing the user edited text inside a HTTP parameter. */
-    public static final String REQ_EDITEDTEXT = "_editedtext";
+    String REQ_EDITEDTEXT = "_editedtext";
 
     /** Known attribute name for storing the user edited text inside a session or a page context */
-    public static final String ATTR_EDITEDTEXT = REQ_EDITEDTEXT;
-
-    private Map< String, WikiEditorInfo > m_editors;
-
-    private static final Logger log = Logger.getLogger( EditorManager.class );
-
-    public EditorManager( final Engine engine ) {
-        super( engine );
-    }
+    String ATTR_EDITEDTEXT = REQ_EDITEDTEXT;
 
     /**
      *  Initializes the EditorManager.  It also registers any editors it can find.
      *
      *  @param props  Properties for setup.
      */
-    public void initialize( final Properties props ) {
-        registerEditors();
-    }
-
-    /**
-     *  This method goes through the jspwiki_module.xml files and hunts for editors. Any editors found are put in the registry.
-     */
-    private void registerEditors() {
-        log.info( "Registering editor modules" );
-        m_editors = new HashMap<>();
-
-        //
-        // Register all editors which have created a resource containing its properties.
-        //
-        // Get all resources of all modules
-        //
-        final List< Element > editors = XmlUtil.parse( PLUGIN_RESOURCE_LOCATION, "/modules/editor" );
-        for( final Element pluginEl : editors ) {
-            final String name = pluginEl.getAttributeValue( "name" );
-            final WikiEditorInfo info = WikiEditorInfo.newInstance( name, pluginEl );
-
-            if( checkCompatibility( info ) ) {
-                m_editors.put( name, info );
-                log.debug( "Registered editor " + name );
-            } else {
-                log.info( "Editor '" + name + "' not compatible with this version of JSPWiki." );
-            }
-        }
-    }
+    void initialize( Properties props );
 
     /**
      *  Returns an editor for the current context.  The editor names are matched in a case insensitive manner.  At the moment, the only
@@ -132,46 +84,14 @@
      * @param context The context that is chosen.
      * @return The name of the chosen editor. If no match could be found, will revert to the default "plain" editor.
      */
-    public String getEditorName( final WikiContext context ) {
-        if( context.getRequestContext().equals( WikiContext.PREVIEW ) ) {
-            return EDITOR_PREVIEW;
-        }
-
-        // User has set an editor in preferences
-        String editor = Preferences.getPreference( context, PARA_EDITOR );
-
-        /* FIXME: actual default 'editor' property is read by the Preferences class */
-        if( editor == null ) {
-            // or use the default editor in jspwiki.properties
-            try {
-                editor = m_engine.getManager( VariableManager.class ).getValue( context, PROP_EDITORTYPE );
-            } catch( final NoSuchVariableException e ) {} // This is fine
-        }
-
-        if( editor != null ) {
-            final String[] editorlist = getEditorList();
-            editor = editor.trim();
-            for( final String s : editorlist ) {
-                if( s.equalsIgnoreCase( editor ) ) {
-                    return s;
-                }
-            }
-        }
-
-        return EDITOR_PLAIN;
-    }
+    String getEditorName( WikiContext context );
 
     /**
      *  Returns a list of editors as Strings of editor names.
      *
      *  @return the list of available editors
      */
-    public String[] getEditorList() {
-        final String[] editors = new String[ m_editors.size() ];
-        final Set< String > keys = m_editors.keySet();
-
-        return keys.toArray( editors );
-    }
+    String[] getEditorList();
 
     /**
      *  Convenience method for getting the path to the editor JSP file.
@@ -179,18 +99,7 @@
      *  @param context WikiContext from where the editor name is retrieved.
      *  @return e.g. "editors/plain.jsp"
      */
-    public String getEditorPath( final WikiContext context ) {
-        final String editor = getEditorName( context );
-        final WikiEditorInfo ed = m_editors.get( editor );
-        final String path;
-        if( ed != null ) {
-            path = ed.getPath();
-        } else {
-            path = "editors/"+editor+".jsp";
-        }
-
-        return path;
-    }
+    String getEditorPath( WikiContext context );
 
     /**
      *  Convenience function which examines the current context and attempts to figure out whether the edited text is in the HTTP
@@ -199,7 +108,7 @@
      *  @param ctx the JSP page context
      *  @return the edited text, if present in the session page context or as a parameter
      */
-    public static String getEditedText( final PageContext ctx ) {
+    static String getEditedText( final PageContext ctx ) {
         String usertext = ctx.getRequest().getParameter( REQ_EDITEDTEXT );
         if( usertext == null ) {
             usertext = ( String )ctx.findAttribute( ATTR_EDITEDTEXT );
@@ -208,48 +117,13 @@
         return usertext;
     }
 
-    /**  Contains info about an editor. */
-    private static final class WikiEditorInfo extends WikiModuleInfo {
-        private String m_path;
-
-        protected static WikiEditorInfo newInstance( final String name, final Element el ) {
-            if( name == null || name.length() == 0 ) {
-                return null;
-            }
-            final WikiEditorInfo info = new WikiEditorInfo( name );
-            info.initializeFromXML( el );
-            return info;
-        }
-
-        @Override
-        protected void initializeFromXML( final Element el ) {
-            super.initializeFromXML( el );
-            m_path = el.getChildText("path");
-        }
-
-        private WikiEditorInfo( final String name ) {
-            super( name );
-        }
-
-        public String getPath() {
-            return m_path;
-        }
-    }
-
     /**
-     *  {@inheritDoc}
+     * Returns a collection of modules currently managed by this ModuleManager.  Each
+     * entry is an instance of the WikiModuleInfo class.  This method should return something
+     * which is safe to iterate over, even if the underlying collection changes.
+     *
+     * @return A Collection of WikiModuleInfo instances.
      */
-    @Override
-    public Collection< WikiModuleInfo > modules() {
-        return modules( m_editors.values().iterator() );
-    }
-
-    /**
-     *  {@inheritDoc}
-     */
-    @Override
-    public WikiEditorInfo getModuleInfo( final String moduleName ) {
-        return m_editors.get( moduleName );
-    }
+    Collection< WikiModuleInfo > modules();
 
 }
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/ui/TemplateManager.java b/jspwiki-main/src/main/java/org/apache/wiki/ui/TemplateManager.java
index 68441f9..57114d1 100644
--- a/jspwiki-main/src/main/java/org/apache/wiki/ui/TemplateManager.java
+++ b/jspwiki-main/src/main/java/org/apache/wiki/ui/TemplateManager.java
@@ -18,40 +18,25 @@
  */
 package org.apache.wiki.ui;
 
-import org.apache.commons.lang3.StringUtils;
 import org.apache.log4j.Logger;
-import org.apache.wiki.InternalWikiException;
 import org.apache.wiki.WikiContext;
-import org.apache.wiki.api.core.Engine;
 import org.apache.wiki.i18n.InternationalizationManager;
-import org.apache.wiki.modules.ModuleManager;
 import org.apache.wiki.modules.WikiModuleInfo;
 import org.apache.wiki.preferences.Preferences;
-import org.apache.wiki.preferences.Preferences.TimeFormat;
 import org.apache.wiki.util.ClassUtil;
 
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServletRequest;
 import javax.servlet.jsp.PageContext;
 import javax.servlet.jsp.jstl.fmt.LocaleSupport;
-import java.io.IOException;
-import java.io.InputStream;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Properties;
 import java.util.ResourceBundle;
 import java.util.Set;
 import java.util.TimeZone;
-import java.util.TreeSet;
 import java.util.Vector;
 
 
@@ -60,132 +45,62 @@
  *
  *  @since 2.1.62
  */
-public class TemplateManager extends ModuleManager {
+public interface TemplateManager {
 
-    private static final String SKIN_DIRECTORY = "skins";
+    String SKIN_DIRECTORY = "skins";
 
     /** Requests a JavaScript function to be called during window.onload. Value is {@value}. */
-    public static final String RESOURCE_JSFUNCTION = "jsfunction";
+    String RESOURCE_JSFUNCTION = "jsfunction";
 
     /** Requests a JavaScript associative array with all localized strings. */
-    public static final String RESOURCE_JSLOCALIZEDSTRINGS = "jslocalizedstrings";
+    String RESOURCE_JSLOCALIZEDSTRINGS = "jslocalizedstrings";
 
     /** Requests a stylesheet to be inserted. Value is {@value}. */
-    public static final String RESOURCE_STYLESHEET = "stylesheet";
+    String RESOURCE_STYLESHEET = "stylesheet";
 
     /** Requests a script to be loaded. Value is {@value}. */
-    public static final String RESOURCE_SCRIPT = "script";
+    String RESOURCE_SCRIPT = "script";
 
     /** Requests inlined CSS. Value is {@value}. */
-    public static final String RESOURCE_INLINECSS = "inlinecss";
+    String RESOURCE_INLINECSS = "inlinecss";
 
     /** The default directory for the properties. Value is {@value}. */
-    public static final String DIRECTORY = "templates";
+    String DIRECTORY = "templates";
 
     /** The name of the default template. Value is {@value}. */
-    public static final String DEFAULT_TEMPLATE = "default";
+    String DEFAULT_TEMPLATE = "default";
 
     /** Name of the file that contains the properties. */
-    public static final String PROPERTYFILE = "template.properties";
+    String PROPERTYFILE = "template.properties";
 
     /** Location of I18N Resource bundles, and path prefix and suffixes */
-    public static final String I18NRESOURCE_PREFIX = "templates/default_";
+    String I18NRESOURCE_PREFIX = "templates/default_";
 
-    public static final String I18NRESOURCE_SUFFIX = ".properties";
+    String I18NRESOURCE_SUFFIX = ".properties";
 
     /** The default (en) RESOURCE name and id. */
-    public static final String I18NRESOURCE_EN = "templates/default.properties";
-    public static final String I18NRESOURCE_EN_ID = "en";
+    String I18NRESOURCE_EN = "templates/default.properties";
+    String I18NRESOURCE_EN_ID = "en";
 
     /** I18N string to mark the default locale */
-    public static final String I18NDEFAULT_LOCALE = "prefs.user.language.default";
+    String I18NDEFAULT_LOCALE = "prefs.user.language.default";
 
     /** I18N string to mark the server timezone */
-    public static final String I18NSERVER_TIMEZONE = "prefs.user.timezone.server";
+    String I18NSERVER_TIMEZONE = "prefs.user.timezone.server";
 
     /** Prefix of the default timeformat properties. */
-    public static final String TIMEFORMATPROPERTIES = "jspwiki.defaultprefs.timeformat.";
+    String TIMEFORMATPROPERTIES = "jspwiki.defaultprefs.timeformat.";
 
     /** The name under which the resource includes map is stored in the  WikiContext. */
-    public static final String RESOURCE_INCLUDES = "jspwiki.resourceincludes";
-
-    // private Cache m_propertyCache;
-
-    private static final Logger log = Logger.getLogger( TemplateManager.class );
+    String RESOURCE_INCLUDES = "jspwiki.resourceincludes";
 
     /** Requests a HTTP header. Value is {@value}. */
-    public static final String RESOURCE_HTTPHEADER = "httpheader";
-
-    /**
-     *  Creates a new TemplateManager.  There is typically one manager per engine.
-     *
-     *  @param engine The owning engine.
-     *  @param properties The property list used to initialize this.
-     */
-    public TemplateManager( final Engine engine, final Properties properties ) {
-        super( engine );
-
-        //
-        //  Uses the unlimited cache.
-        //
-        // m_propertyCache = new Cache( true, false );
-    }
+    String RESOURCE_HTTPHEADER = "httpheader";
 
     /**
      *  Check the existence of a template.
      */
-    // FIXME: Does not work yet
-    public boolean templateExists( final String templateName ) {
-        final ServletContext context = m_engine.getServletContext();
-        try( final InputStream in = context.getResourceAsStream( getPath( templateName ) + "ViewTemplate.jsp" ) ) {
-            if( in != null ) {
-                return true;
-            }
-        } catch( final IOException e ) {
-            log.error( e.getMessage(), e );
-        }
-        return false;
-    }
-
-    /**
-     *  Tries to locate a given resource from the template directory. If the given resource is not found under the current name, returns the
-     *  path to the corresponding one in the default template.
-     *
-     *  @param sContext The servlet context
-     *  @param name The name of the resource
-     *  @return The name of the resource which was found.
-     */
-    private static String findResource( final ServletContext sContext, final String name ) {
-        String resourceName = name;
-        try( final InputStream is = sContext.getResourceAsStream( resourceName ) ) {
-            if( is == null ) {
-                final String defname = makeFullJSPName( DEFAULT_TEMPLATE, removeTemplatePart( resourceName ) );
-                try( final InputStream iis = sContext.getResourceAsStream( defname ) ) {
-                    resourceName = iis != null ? defname : null;
-                }
-            }
-        } catch( final IOException e ) {
-            log.error( "unable to open " + name + " as resource stream", e );
-        }
-        return resourceName;
-    }
-
-    /**
-     *  Attempts to find a resource from the given template, and if it's not found
-     *  attempts to locate it from the default template.
-     * @param sContext
-     * @param template
-     * @param name
-     * @return the Resource for the given template and name.
-     */
-    private static String findResource( final ServletContext sContext, final String template, final String name ) {
-        if( name.charAt(0) == '/' ) {
-            // This is already a full path
-            return findResource( sContext, name );
-        }
-        final String fullname = makeFullJSPName( template, name );
-        return findResource( sContext, fullname );
-    }
+    boolean templateExists( String templateName );
 
     /**
      *  An utility method for finding a JSP page.  It searches only under either current context or by the absolute name.
@@ -194,44 +109,7 @@
      *  @param name The name of the JSP page to look for (e.g "Wiki.jsp")
      *  @return The context path to the resource
      */
-    public String findJSP( final PageContext pageContext, final String name ) {
-        final ServletContext sContext = pageContext.getServletContext();
-        return findResource( sContext, name );
-    }
-
-    /**
-     *  Removes the template part of a name.
-     */
-    private static String removeTemplatePart( String name ) {
-        int idx = 0;
-        if( name.startsWith( "/" ) ) {
-            idx = 1;
-        }
-
-        idx = name.indexOf('/', idx);
-        if( idx != -1 ) {
-            idx = name.indexOf('/', idx+1); // Find second "/"
-            if( idx != -1 ) {
-                name = name.substring( idx+1 );
-            }
-        }
-
-        if( log.isDebugEnabled() ) {
-            log.debug( "Final name = "+name );
-        }
-        return name;
-    }
-
-    /**
-     *  Returns the full name (/templates/foo/bar) for name=bar, template=foo.
-     *
-     * @param template The name of the template.
-     * @param name The name of the resource.
-     * @return The full name for a template.
-     */
-    private static String makeFullJSPName( final String template, final String name ) {
-        return "/" + DIRECTORY + "/" + template + "/" + name;
-    }
+    String findJSP( PageContext pageContext, String name );
 
     /**
      *  Attempts to locate a resource under the given template.  If that template does not exist, or the page does not exist under that
@@ -244,15 +122,7 @@
      *  @param name Which resource are we looking for (e.g. "ViewTemplate.jsp")
      *  @return path to the JSP page; null, if it was not found.
      */
-    public String findJSP( final PageContext pageContext, final String template, final String name ) {
-        if( name == null || template == null ) {
-            log.fatal("findJSP() was asked to find a null template or name (" + template + "," + name + ")." + " JSP page '" +
-                      ( ( HttpServletRequest )pageContext.getRequest() ).getRequestURI() + "'" );
-            throw new InternalWikiException( "Illegal arguments to findJSP(); please check logs." );
-        }
-
-        return findResource( pageContext.getServletContext(), template, name );
-    }
+    String findJSP( PageContext pageContext, String template, String name );
 
     /**
      *  Attempts to locate a resource under the given template.  This matches the functionality findJSP(), but uses the WikiContext as
@@ -266,58 +136,7 @@
      *  @param name the name of the resource to fine
      *  @return the path to the resource
      */
-    public String findResource( final WikiContext ctx, final String template, final String name ) {
-        if( m_engine.getServletContext() != null ) {
-            return findResource( m_engine.getServletContext(), template, name );
-        }
-
-        return getPath(template)+"/"+name;
-    }
-
-    /**
-     *  Returns a property, as defined in the template.  The evaluation is lazy, i.e. the properties are not loaded until the template is
-     *  actually used for the first time.
-     */
-    /*
-    public String getTemplateProperty( WikiContext context, String key )
-    {
-        String template = context.getTemplate();
-
-        try
-        {
-            Properties props = (Properties)m_propertyCache.getFromCache( template, -1 );
-
-            if( props == null )
-            {
-                try
-                {
-                    props = getTemplateProperties( template );
-
-                    m_propertyCache.putInCache( template, props );
-                }
-                catch( IOException e )
-                {
-                    log.warn("IO Exception while reading template properties",e);
-
-                    return null;
-                }
-            }
-
-            return props.getProperty( key );
-        }
-        catch( NeedsRefreshException ex )
-        {
-            // FIXME
-            return null;
-        }
-    }
-*/
-    /**
-     *  Returns an absolute path to a given template.
-     */
-    private static String getPath( final String template ) {
-        return "/" + DIRECTORY + "/" + template + "/";
-    }
+    String findResource( WikiContext ctx, String template, String name );
 
     /**
      *   Lists the skins available under this template.  Returns an empty Set, if there are no extra skins available.  Note that
@@ -329,43 +148,18 @@
      *   @return Set of Strings with the skin names.
      *   @since 2.3.26
      */
-    public Set< String > listSkins( final PageContext pageContext, final String template ) {
-        final String place = makeFullJSPName( template, SKIN_DIRECTORY );
-        final ServletContext sContext = pageContext.getServletContext();
-        final Set< String > skinSet = sContext.getResourcePaths( place );
-        final Set< String > resultSet = new TreeSet<>();
-
-        if( log.isDebugEnabled() ) {
-            log.debug( "Listings skins from " + place );
-        }
-
-        if( skinSet != null ) {
-            final String[] skins = skinSet.toArray( new String[]{} );
-            for( final String skin : skins ) {
-                final String[] s = StringUtils.split( skin, "/" );
-                if( s.length > 2 && skin.endsWith( "/" ) ) {
-                    final String skinName = s[ s.length - 1 ];
-                    resultSet.add( skinName );
-                    if( log.isDebugEnabled() ) {
-                        log.debug( "...adding skin '" + skinName + "'" );
-                    }
-                }
-            }
-        }
-
-        return resultSet;
-    }
+    Set< String > listSkins( PageContext pageContext, String template );
 
     /**
      * List all installed i18n language properties by classpath searching for files like :
      *    templates/default_*.properties
      *    templates/default.properties
      *
-     * @param pageContext
+     * @param pageContext page context
      * @return map of installed Languages
      * @since 2.7.x
      */
-    public Map< String, String > listLanguages( final PageContext pageContext ) {
+    default Map< String, String > listLanguages( final PageContext pageContext ) {
         final Map< String, String > resultMap = new LinkedHashMap<>();
         final String clientLanguage = pageContext.getRequest().getLocale().toString();
         final List< String > entries = ClassUtil.classpathEntriesUnder( DIRECTORY );
@@ -376,7 +170,7 @@
                 } else {
                     name = name.substring( I18NRESOURCE_PREFIX.length(), name.lastIndexOf( I18NRESOURCE_SUFFIX ) );
                 }
-                final Locale locale = new Locale( name.substring( 0, 2 ), ( ( name.indexOf( "_" ) == -1 ) ? "" : name.substring( 3, 5 ) ) );
+                final Locale locale = new Locale( name.substring( 0, 2 ), !name.contains( "_" ) ? "" : name.substring( 3, 5 ) );
                 String defaultLanguage = "";
                 if( clientLanguage.startsWith( name ) ) {
                     defaultLanguage = LocaleSupport.getLocalizedMessage( pageContext, I18NDEFAULT_LOCALE );
@@ -392,76 +186,30 @@
     /**
      * List all available timeformats, read from the jspwiki.properties
      *
-     * @param pageContext
+     * @param pageContext page context
      * @return map of TimeFormats
      * @since 2.7.x
      */
-    public Map< String, String > listTimeFormats( final PageContext pageContext ) {
-        final WikiContext context = WikiContext.findContext( pageContext );
-        final Properties props = m_engine.getWikiProperties();
-        final ArrayList< String > tfArr = new ArrayList<>(40);
-        final LinkedHashMap< String, String > resultMap = new LinkedHashMap<>();
+    Map< String, String > listTimeFormats( final PageContext pageContext );
 
-        /* filter timeformat properties */
-        for( final Enumeration< ? > e = props.propertyNames(); e.hasMoreElements(); ) {
-            final String name = ( String )e.nextElement();
-            if( name.startsWith( TIMEFORMATPROPERTIES ) ) {
-                tfArr.add( name );
-            }
-        }
-
-        /* fetch actual formats */
-        if( tfArr.size() == 0 )  {/* no props found - make sure some default formats are avail */
-            tfArr.add( "dd-MMM-yy" );
-            tfArr.add( "d-MMM-yyyy" );
-            tfArr.add( "EEE, dd-MMM-yyyy, zzzz" );
-        } else {
-            Collections.sort( tfArr );
-
-            for (int i = 0; i < tfArr.size(); i++) {
-                tfArr.set(i, props.getProperty(tfArr.get(i)));
-            }
-        }
-
-        final String prefTimeZone = Preferences.getPreference( context, "TimeZone" );
-        //TimeZone tz = TimeZone.getDefault();
-        final TimeZone tz = TimeZone.getTimeZone(prefTimeZone);
-        /*try
-        {
-            tz.setRawOffset(Integer.parseInt(prefTimeZone));
-        }
-        catch (Exception e)
-        {
-        }*/
-
-        final Date d = new Date(); // current date
-        try {
-            // dummy format pattern
-            final SimpleDateFormat fmt = Preferences.getDateFormat( context, TimeFormat.DATETIME );
-            fmt.setTimeZone( tz );
-
-            for( int i = 0; i < tfArr.size(); i++ ) {
-                try {
-                    final String f = tfArr.get( i );
-                    fmt.applyPattern( f );
-                    resultMap.put( f, fmt.format( d ) );
-                } catch( final IllegalArgumentException e ) {} // skip parameter
-            }
-        }
-        catch( final IllegalArgumentException e ) {} // skip parameter
-
-        return resultMap;
-    }
+    /**
+     * Returns a collection of modules currently managed by this ModuleManager.  Each
+     * entry is an instance of the WikiModuleInfo class.  This method should return something
+     * which is safe to iterate over, even if the underlying collection changes.
+     *
+     * @return A Collection of WikiModuleInfo instances.
+     */
+    Collection< WikiModuleInfo > modules();
 
     /**
      * List all timezones, with special marker for server timezone
      *
-     * @param pageContext
+     * @param pageContext page context
      * @return map of TimeZones
      * @since 2.7.x
      */
-    public Map< String, String > listTimeZones( final PageContext pageContext ) {
-        final Map<String,String> resultMap = new LinkedHashMap<>();
+    default Map< String, String > listTimeZones( final PageContext pageContext ) {
+        final Map< String, String > resultMap = new LinkedHashMap<>();
         final String[][] tzs = {
                           { "GMT-12", "Enitwetok, Kwajalien" },
                           { "GMT-11", "Nome, Midway Island, Samoa" },
@@ -511,33 +259,6 @@
     }
 
     /**
-     *  Always returns a valid property map.
-     */
-    /*
-    private Properties getTemplateProperties( String templateName )
-        throws IOException
-    {
-        Properties p = new Properties();
-
-        ServletContext context = m_engine.getServletContext();
-
-        InputStream propertyStream = context.getResourceAsStream(getPath(templateName)+PROPERTYFILE);
-
-        if( propertyStream != null )
-        {
-            p.load( propertyStream );
-
-            propertyStream.close();
-        }
-        else
-        {
-            log.debug("Template '"+templateName+"' does not have a propertyfile '"+PROPERTYFILE+"'.");
-        }
-
-        return p;
-    }
-*/
-    /**
      *  Returns the include resources marker for a given type.  This is in a
      *  HTML or Javascript comment format.
      *
@@ -545,7 +266,7 @@
      *  @param type the marker
      *  @return the generated marker comment
      */
-    public static String getMarker( final WikiContext context, final String type ) {
+    static String getMarker( final WikiContext context, final String type ) {
         if( type.equals( RESOURCE_JSLOCALIZEDSTRINGS ) ) {
             return getJSLocalizedStrings( context );
         } else if( type.equals( RESOURCE_JSFUNCTION ) ) {
@@ -561,7 +282,7 @@
      *  @return Javascript snippet which defines the LocalizedStrings array
      *  @since 2.5.108
      */
-    private static String getJSLocalizedStrings( final WikiContext context ) {
+    static String getJSLocalizedStrings( final WikiContext context ) {
         final StringBuilder sb = new StringBuilder();
         sb.append( "var LocalizedStrings = {\n");
         final ResourceBundle rb = Preferences.getBundle( context, InternationalizationManager.DEF_TEMPLATE );
@@ -575,7 +296,7 @@
                 } else {
                     sb.append( ",\n" );
                 }
-                sb.append( "\""+key+"\":\""+rb.getString(key)+"\"" );
+                sb.append( "\"" ).append( key ).append( "\":\"" ).append( rb.getString( key ) ).append( "\"" );
             }
         }
         sb.append("\n};\n");
@@ -604,7 +325,7 @@
      *  @param resource The resource to add.
      */
     @SuppressWarnings("unchecked")
-    public static void addResourceRequest( final WikiContext ctx, final String type, final String resource ) {
+    static void addResourceRequest( final WikiContext ctx, final String type, final String resource ) {
         HashMap< String, Vector< String > > resourcemap = ( HashMap< String, Vector< String > > ) ctx.getVariable( RESOURCE_INCLUDES );
         if( resourcemap == null ) {
             resourcemap = new HashMap<>();
@@ -636,7 +357,7 @@
             resources.add( resourceString );
         }
 
-        log.debug( "Request to add a resource: " + resourceString );
+        Logger.getLogger( TemplateManager.class ).debug( "Request to add a resource: " + resourceString );
 
         resourcemap.put( type, resources );
         ctx.setVariable( RESOURCE_INCLUDES, resourcemap );
@@ -650,7 +371,7 @@
      *  @return a String array for the resource requests
      */
     @SuppressWarnings("unchecked")
-    public static String[] getResourceRequests( final WikiContext ctx, final String type ) {
+    static String[] getResourceRequests( final WikiContext ctx, final String type ) {
         final HashMap< String, Vector< String > > hm = ( HashMap< String, Vector< String > > ) ctx.getVariable( RESOURCE_INCLUDES );
         if( hm == null ) {
             return new String[0];
@@ -672,7 +393,7 @@
      * @return the array of types requested
      */
     @SuppressWarnings("unchecked")
-    public static String[] getResourceTypes( final WikiContext ctx ) {
+    static String[] getResourceTypes( final WikiContext ctx ) {
         String[] res = new String[0];
         if( ctx != null ) {
             final HashMap< String, String > hm = ( HashMap< String, String > ) ctx.getVariable( RESOURCE_INCLUDES );
@@ -685,23 +406,4 @@
         return res;
     }
 
-    /**
-     *  Returns an empty collection, since at the moment the TemplateManager does not manage any modules.
-     *
-     *  @return {@inheritDoc}
-     */
-    @Override
-    public Collection< WikiModuleInfo > modules() {
-        return new ArrayList<>();
-    }
-
-    /**
-     *  Returns null!
-     *  {@inheritDoc}
-     */
-    @Override
-    public WikiModuleInfo getModuleInfo( final String moduleName ) {
-    	return null;
-    }
-
 }
diff --git a/jspwiki-main/src/main/resources/ini/classmappings.xml b/jspwiki-main/src/main/resources/ini/classmappings.xml
index da8d88f..a1817fb 100644
--- a/jspwiki-main/src/main/resources/ini/classmappings.xml
+++ b/jspwiki-main/src/main/resources/ini/classmappings.xml
@@ -97,7 +97,7 @@
   </mapping>
   <mapping>
     <requestedClass>org.apache.wiki.i18n.InternationalizationManager</requestedClass>
-    <mappedClass>org.apache.wiki.i18n.InternationalizationManager</mappedClass>
+    <mappedClass>org.apache.wiki.i18n.DefaultInternationalizationManager</mappedClass>
   </mapping>
   <mapping>
     <requestedClass>org.apache.wiki.references.ReferenceManager</requestedClass>
@@ -109,7 +109,7 @@
   </mapping>
   <mapping>
     <requestedClass>org.apache.wiki.search.SearchManager</requestedClass>
-    <mappedClass>org.apache.wiki.search.SearchManager</mappedClass>
+    <mappedClass>org.apache.wiki.search.DefaultSearchManager</mappedClass>
   </mapping>
   <mapping>
     <requestedClass>org.apache.wiki.tasks.TasksManager</requestedClass>
@@ -121,11 +121,11 @@
   </mapping>
   <mapping>
     <requestedClass>org.apache.wiki.ui.EditorManager</requestedClass>
-    <mappedClass>org.apache.wiki.ui.EditorManager</mappedClass>
+    <mappedClass>org.apache.wiki.ui.DefaultEditorManager</mappedClass>
   </mapping>
   <mapping>
     <requestedClass>org.apache.wiki.ui.TemplateManager</requestedClass>
-    <mappedClass>org.apache.wiki.ui.TemplateManager</mappedClass>
+    <mappedClass>org.apache.wiki.ui.DefaultTemplateManager</mappedClass>
   </mapping>
   <mapping>
     <requestedClass>org.apache.wiki.ui.progress.ProgressManager</requestedClass>
diff --git a/jspwiki-main/src/test/java/org/apache/wiki/i18n/InternationalizationManagerTest.java b/jspwiki-main/src/test/java/org/apache/wiki/i18n/InternationalizationManagerTest.java
index a397fea..7ede665 100644
--- a/jspwiki-main/src/test/java/org/apache/wiki/i18n/InternationalizationManagerTest.java
+++ b/jspwiki-main/src/test/java/org/apache/wiki/i18n/InternationalizationManagerTest.java
@@ -17,17 +17,15 @@
     under the License.    
  */
 package org.apache.wiki.i18n;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
-
 import org.junit.jupiter.api.Test;
 
 import java.util.Locale;
 
-import org.junit.jupiter.api.Assertions;
-
 public class InternationalizationManagerTest
 {
-    InternationalizationManager i18n = new InternationalizationManager( null );
+    InternationalizationManager i18n = new DefaultInternationalizationManager( null );
     
     @BeforeEach
     public void setUp() throws Exception
@@ -41,7 +39,7 @@
     @Test
     public void testGetFromCoreWithArgs() 
     {
-        String str = i18n.get( InternationalizationManager.CORE_BUNDLE, 
+        final String str = i18n.get( InternationalizationManager.CORE_BUNDLE,
                                Locale.ENGLISH, 
                                "security.error.cannot.rename", 
                                "Test User" );
@@ -51,7 +49,7 @@
     @Test
     public void testGetFromDefTemplateWithArgs() 
     {
-        String str = i18n.get( InternationalizationManager.DEF_TEMPLATE, 
+        final String str = i18n.get( InternationalizationManager.DEF_TEMPLATE,
                                Locale.ENGLISH, 
                                "notification.createUserProfile.accept.content", 
                                "JSPWiki", "testUser", "Test User", "test@user.com", "www.foo.com" );