    001/*
    002    Licensed to the Apache Software Foundation (ASF) under one
    003    or more contributor license agreements.  See the NOTICE file
    004    distributed with this work for additional information
    005    regarding copyright ownership.  The ASF licenses this file
    006    to you under the Apache License, Version 2.0 (the
    007    "License"); you may not use this file except in compliance
    008    with the License.  You may obtain a copy of the License at
    009
    010       http://www.apache.org/licenses/LICENSE-2.0
    011
    012    Unless required by applicable law or agreed to in writing,
    013    software distributed under the License is distributed on an
    014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015    KIND, either express or implied.  See the License for the
    016    specific language governing permissions and limitations
    017    under the License.
    018 */
    019package org.apache.wiki;
    020
    021import org.apache.commons.lang3.StringUtils;
    022import org.apache.logging.log4j.LogManager;
    023import org.apache.logging.log4j.Logger;
    024import org.apache.wiki.api.Release;
    025import org.apache.wiki.api.core.Engine;
    026import org.apache.wiki.api.core.Page;
    027import org.apache.wiki.api.engine.Initializable;
    028import org.apache.wiki.api.exceptions.ProviderException;
    029import org.apache.wiki.api.exceptions.WikiException;
    030import org.apache.wiki.attachment.AttachmentManager;
    031import org.apache.wiki.auth.AuthenticationManager;
    032import org.apache.wiki.auth.AuthorizationManager;
    033import org.apache.wiki.auth.UserManager;
    034import org.apache.wiki.auth.acl.AclManager;
    035import org.apache.wiki.auth.authorize.GroupManager;
    036import org.apache.wiki.cache.CachingManager;
    037import org.apache.wiki.content.PageRenamer;
    038import org.apache.wiki.diff.DifferenceManager;
    039import org.apache.wiki.event.WikiEngineEvent;
    040import org.apache.wiki.event.WikiEventListener;
    041import org.apache.wiki.event.WikiEventManager;
    042import org.apache.wiki.event.WikiPageEvent;
    043import org.apache.wiki.filters.FilterManager;
    044import org.apache.wiki.i18n.InternationalizationManager;
    045import org.apache.wiki.pages.PageManager;
    046import org.apache.wiki.plugin.PluginManager;
    047import org.apache.wiki.references.ReferenceManager;
    048import org.apache.wiki.render.RenderingManager;
    049import org.apache.wiki.rss.RSSGenerator;
    050import org.apache.wiki.search.SearchManager;
    051import org.apache.wiki.tasks.TasksManager;
    052import org.apache.wiki.ui.CommandResolver;
    053import org.apache.wiki.ui.EditorManager;
    054import org.apache.wiki.ui.TemplateManager;
    055import org.apache.wiki.ui.admin.AdminBeanManager;
    056import org.apache.wiki.ui.progress.ProgressManager;
    057import org.apache.wiki.url.URLConstructor;
    058import org.apache.wiki.util.ClassUtil;
    059import org.apache.wiki.util.PropertyReader;
    060import org.apache.wiki.util.TextUtil;
    061import org.apache.wiki.variables.VariableManager;
    062import org.apache.wiki.workflow.WorkflowManager;
    063
    064import javax.servlet.ServletConfig;
    065import javax.servlet.ServletContext;
    066import java.io.File;
    067import java.io.UnsupportedEncodingException;
    068import java.net.MalformedURLException;
    069import java.net.URL;
    070import java.net.URLDecoder;
    071import java.net.URLEncoder;
    072import java.nio.charset.Charset;
    073import java.nio.charset.StandardCharsets;
    074import java.util.ArrayList;
    075import java.util.Collection;
    076import java.util.Date;
    077import java.util.Enumeration;
    078import java.util.List;
    079import java.util.Locale;
    080import java.util.Map;
    081import java.util.Properties;
    082import java.util.TimeZone;
    083import java.util.concurrent.ConcurrentHashMap;
    084import java.util.stream.Collectors;
    085
    086
    087/**
    088 *  Main implementation for {@link Engine}.
    089 *
    090 *  <P>
    091 *  Using this class:  Always get yourself an instance from JSP page by using the {@code WikiEngine.getInstance(..)} method.  Never create
    092 *  a new WikiEngine() from scratch, unless you're writing tests.
    093 *
    094 *  <p>
    095 *  {@inheritDoc}
    096 */
    097public class WikiEngine implements Engine {
    098
    099    private static final String ATTR_WIKIENGINE = "org.apache.wiki.WikiEngine";
    100    private static final Logger LOG = LogManager.getLogger( WikiEngine.class );
    101
    102    /** Stores properties. */
    103    private Properties m_properties;
    104
    105    /** Should the user info be saved with the page data as well? */
    106    private boolean m_saveUserInfo = true;
    107
    108    /** If true, uses UTF8 encoding for all data */
    109    private boolean m_useUTF8 = true;
    110
    111    /** Store the file path to the basic URL.  When we're not running as a servlet, it defaults to the user's current directory. */
    112    private String m_rootPath = System.getProperty( "user.dir" );
    113
    114    /** Store the ServletContext that we're in.  This may be null if WikiEngine is not running inside a servlet container (i.e. when testing). */
    115    private ServletContext   m_servletContext;
    116
    117    /** Stores the template path.  This is relative to "templates". */
    118    private String           m_templateDir;
    119
    120    /** The default front page name.  Defaults to "Main". */
    121    private String           m_frontPage;
    122
    123    /** The time when this engine was started. */
    124    private Date             m_startTime;
    125
    126    /** The location where the work directory is. */
    127    private String           m_workDir;
    128
    129    /** Each engine has their own application id. */
    130    private String           m_appid = "";
    131
    132    /** engine is up and running or not */
    133    private boolean          m_isConfigured;
    134
    135    /** Stores wikiengine attributes. */
    136    private final Map< String, Object > m_attributes = new ConcurrentHashMap<>();
    137
    138    /** Stores WikiEngine's associated managers. */
    139    protected final Map< Class< ? >, Object > managers = new ConcurrentHashMap<>();
    140
    141    /**
    142     *  Gets a WikiEngine related to this servlet.  Since this method is only called from JSP pages (and JspInit()) to be specific,
    143     *  we throw a RuntimeException if things don't work.
    144     *
    145     *  @param config The ServletConfig object for this servlet.
    146     *  @return A WikiEngine instance.
    147     *  @throws InternalWikiException in case something fails. This is a RuntimeException, so be prepared for it.
    148     */
    149    public static synchronized WikiEngine getInstance( final ServletConfig config ) throws InternalWikiException {
    150        return getInstance( config.getServletContext(), null );
    151    }
    152
    153    /**
    154     *  Gets a WikiEngine related to the servlet. Works like getInstance(ServletConfig), but does not force the Properties object.
    155     *  This method is just an optional way of initializing a WikiEngine for embedded JSPWiki applications; normally, you
    156     *  should use getInstance(ServletConfig).
    157     *
    158     *  @param config The ServletConfig of the webapp servlet/JSP calling this method.
    159     *  @param props  A set of properties, or null, if we are to load JSPWiki's default jspwiki.properties (this is the usual case).
    160     *
    161     *  @return One well-behaving WikiEngine instance.
    162     */
    163    public static synchronized WikiEngine getInstance( final ServletConfig config, final Properties props ) {
    164        return getInstance( config.getServletContext(), props );
    165    }
    166
    167    /**
    168     *  Gets a WikiEngine related to the servlet. Works just like getInstance( ServletConfig )
    169     *
    170     *  @param context The ServletContext of the webapp servlet/JSP calling this method.
    171     *  @param props  A set of properties, or null, if we are to load JSPWiki's default jspwiki.properties (this is the usual case).
    172     *  @return One fully functional, properly behaving WikiEngine.
    173     *  @throws InternalWikiException If the WikiEngine instantiation fails.
    174     */
    175    public static synchronized WikiEngine getInstance( final ServletContext context, Properties props ) throws InternalWikiException {
    176        WikiEngine engine = ( WikiEngine )context.getAttribute( ATTR_WIKIENGINE );
    177        if( engine == null ) {
    178            final String appid = Integer.toString( context.hashCode() );
    179            context.log( " Assigning new engine to " + appid );
    180            try {
    181                if( props == null ) {
    182                    props = PropertyReader.loadWebAppProps( context );
    183                }
    184
    185                engine = new WikiEngine( context, appid );
    186                try {
    187                    //  Note: May be null, if JSPWiki has been deployed in a WAR file.
    188                    engine.start( props );
    189                    LOG.info( "Root path for this Wiki is: '{}'", engine.getRootPath() );
    190                } catch( final Exception e ) {
    191                    final String msg = Release.APPNAME + ": Unable to load and setup properties from jspwiki.properties. " + e.getMessage();
    192                    context.log( msg );
    193                    LOG.error( msg, e );
    194                    throw new WikiException( msg, e );
    195                }
    196                context.setAttribute( ATTR_WIKIENGINE, engine );
    197            } catch( final Exception e ) {
    198                context.log( "ERROR: Failed to create a Wiki engine: " + e.getMessage() );
    199                LOG.error( "ERROR: Failed to create a Wiki engine, stacktrace follows ", e );
    200                throw new InternalWikiException( "No wiki engine, check logs.", e );
    201            }
    202        }
    203        return engine;
    204    }
    205
    206    /**
    207     *  Instantiate the WikiEngine using a given set of properties. Use this constructor for testing purposes only.
    208     *
    209     *  @param properties A set of properties to use to initialize this WikiEngine.
    210     *  @throws WikiException If the initialization fails.
    211     */
    212    public WikiEngine( final Properties properties ) throws WikiException {
    213        start( properties );
    214    }
    215
    216    /**
    217     *  Instantiate using this method when you're running as a servlet and WikiEngine will figure out where to look for the property file.
    218     *  Do not use this method - use WikiEngine.getInstance() instead.
    219     *
    220     *  @param context A ServletContext.
    221     *  @param appid   An Application ID.  This application is a unique random string which is used to recognize this WikiEngine.
    222     *  @throws WikiException If the WikiEngine construction fails.
    223     */
    224    protected WikiEngine( final ServletContext context, final String appid ) throws WikiException {
    225        m_servletContext = context;
    226        m_appid          = appid;
    227
    228        // Stash the WikiEngine in the servlet context
    229        if ( context != null ) {
    230            context.setAttribute( ATTR_WIKIENGINE,  this );
    231            m_rootPath = context.getRealPath( "/" );
    232        }
    233    }
    234
    235    /**
    236     *  Does all the real initialization.
    237     */
    238    @Override
    239    public void initialize( final Properties props ) throws WikiException {
    240        m_startTime  = new Date();
    241        m_properties = props;
    242
    243        LOG.info( "*******************************************" );
    244        LOG.info( "{} {} starting. Whee!", Release.APPNAME, Release.getVersionString() );
    245        LOG.debug( "Java version: {}", System.getProperty( "java.runtime.version" ) );
    246        LOG.debug( "Java vendor: {}", System.getProperty( "java.vm.vendor" ) );
    247        LOG.debug( "OS: {} {} {}", System.getProperty( "os.name" ), System.getProperty( "os.version" ), System.getProperty( "os.arch" ) );
    248        LOG.debug( "Default server locale: {}", Locale.getDefault() );
    249        LOG.debug( "Default server timezone: {}", TimeZone.getDefault().getDisplayName( true, TimeZone.LONG ) );
    250
    251        if( m_servletContext != null ) {
    252            LOG.info( "Servlet container: {}", m_servletContext.getServerInfo() );
    253            if( m_servletContext.getMajorVersion() < 3 || ( m_servletContext.getMajorVersion() == 3 && m_servletContext.getMinorVersion() < 1 ) ) {
    254                throw new InternalWikiException( "JSPWiki requires a container which supports at least version 3.1 of Servlet specification" );
    255            }
    256        }
    257
    258        fireEvent( WikiEngineEvent.INITIALIZING ); // begin initialization
    259
    260        LOG.debug( "Configuring WikiEngine..." );
    261
    262        createAndFindWorkingDirectory( props );
    263
    264        m_useUTF8        = StandardCharsets.UTF_8.name().equals( TextUtil.getStringProperty( props, PROP_ENCODING, StandardCharsets.ISO_8859_1.name() ) );
    265        m_saveUserInfo   = TextUtil.getBooleanProperty( props, PROP_STOREUSERNAME, m_saveUserInfo );
    266        m_frontPage      = TextUtil.getStringProperty( props, PROP_FRONTPAGE, "Main" );
    267        m_templateDir    = TextUtil.getStringProperty( props, PROP_TEMPLATEDIR, "default" );
    268        enforceValidTemplateDirectory();
    269
    270        //
    271        //  Initialize the important modules.  Any exception thrown by the managers means that we will not start up.
    272        //
    273        try {
    274            final String aclClassName = m_properties.getProperty( PROP_ACL_MANAGER_IMPL, ClassUtil.getMappedClass( AclManager.class.getName() ).getName() );
    275            final String urlConstructorClassName = TextUtil.getStringProperty( props, PROP_URLCONSTRUCTOR, "DefaultURLConstructor" );
    276            final Class< URLConstructor > urlclass = ClassUtil.findClass( "org.apache.wiki.url", urlConstructorClassName );
    277
    278            initComponent( CommandResolver.class, this, props );
    279            initComponent( urlclass.getName(), URLConstructor.class );
    280            initComponent( CachingManager.class, this, props );
    281            initComponent( PageManager.class, this, props );
    282            initComponent( PluginManager.class, this, props );
    283            initComponent( DifferenceManager.class, this, props );
    284            initComponent( AttachmentManager.class, this, props );
    285            initComponent( VariableManager.class, props );
    286            initComponent( SearchManager.class, this, props );
    287            initComponent( AuthenticationManager.class );
    288            initComponent( AuthorizationManager.class );
    289            initComponent( UserManager.class );
    290            initComponent( GroupManager.class );
    291            initComponent( EditorManager.class, this );
    292            initComponent( ProgressManager.class, this );
    293            initComponent( aclClassName, AclManager.class );
    294            initComponent( WorkflowManager.class );
    295            initComponent( TasksManager.class );
    296            initComponent( InternationalizationManager.class, this );
    297            initComponent( TemplateManager.class, this, props );
    298            initComponent( FilterManager.class, this, props );
    299            initComponent( AdminBeanManager.class, this );
    300            initComponent( PageRenamer.class, this, props );
    301
    302            // RenderingManager depends on FilterManager events.
    303            initComponent( RenderingManager.class );
    304
    305            //  ReferenceManager has the side effect of loading all pages. Therefore, after this point, all page attributes are available.
    306            //  initReferenceManager is indirectly using m_filterManager, so it has to be called after it was initialized.
    307            initReferenceManager();
    308
    309            //  Hook the different manager routines into the system.
    310            getManager( FilterManager.class ).addPageFilter( getManager( ReferenceManager.class ), -1001 );
    311            getManager( FilterManager.class ).addPageFilter( getManager( SearchManager.class ), -1002 );
    312        } catch( final RuntimeException e ) {
    313            // RuntimeExceptions may occur here, even if they shouldn't.
    314            LOG.fatal( "Failed to start managers.", e );
    315            throw new WikiException( "Failed to start managers: " + e.getMessage(), e );
    316        } catch( final ClassNotFoundException e ) {
    317            LOG.fatal( "JSPWiki could not start, URLConstructor was not found: {}", e.getMessage(), e );
    318            throw new WikiException( e.getMessage(), e );
    319        } catch( final InstantiationException e ) {
    320            LOG.fatal( "JSPWiki could not start, URLConstructor could not be instantiated: {}", e.getMessage(), e );
    321            throw new WikiException( e.getMessage(), e );
    322        } catch( final IllegalAccessException e ) {
    323            LOG.fatal( "JSPWiki could not start, URLConstructor cannot be accessed: {}", e.getMessage(), e );
    324            throw new WikiException( e.getMessage(), e );
    325        } catch( final Exception e ) {
    326            // Final catch-all for everything
    327            LOG.fatal( "JSPWiki could not start, due to an unknown exception when starting.", e );
    328            throw new WikiException( "Failed to start. Caused by: " + e.getMessage() + "; please check log files for better information.", e );
    329        }
    330
    331        //  Initialize the good-to-have-but-not-fatal modules.
    332        try {
    333            if( TextUtil.getBooleanProperty( props, RSSGenerator.PROP_GENERATE_RSS,false ) ) {
    334                initComponent( RSSGenerator.class, this, props );
    335            }
    336        } catch( final Exception e ) {
    337            LOG.error( "Unable to start RSS generator - JSPWiki will still work, but there will be no RSS feed.", e );
    338        }
    339
    340        final Map< String, String > extraComponents = ClassUtil.getExtraClassMappings();
    341        initExtraComponents( extraComponents );
    342
    343        fireEvent( WikiEngineEvent.INITIALIZED ); // initialization complete
    344
    345        LOG.info( "WikiEngine configured." );
    346        m_isConfigured = true;
    347    }
    348
    349    void createAndFindWorkingDirectory( final Properties props ) throws WikiException {
    350        m_workDir = TextUtil.getStringProperty( props, PROP_WORKDIR, null );
    351
    352        final File f = new File( m_workDir );
    353        try {
    354            f.mkdirs();
    355        } catch( final SecurityException e ) {
    356            LOG.fatal( "Unable to find or create the working directory: {}", m_workDir, e );
    357            throw new WikiException( "Unable to find or create the working dir: " + m_workDir, e );
    358        }
    359
    360        //  A bunch of sanity checks
    361        checkWorkingDirectory( !f.exists(), "Work directory does not exist: " + m_workDir );
    362        checkWorkingDirectory( !f.canRead(), "No permission to read work directory: " + m_workDir );
    363        checkWorkingDirectory( !f.canWrite(), "No permission to write to work directory: " + m_workDir );
    364        checkWorkingDirectory( !f.isDirectory(), "jspwiki.workDir does not point to a directory: " + m_workDir );
    365
    366        LOG.info( "JSPWiki working directory is '{}'", m_workDir );
    367    }
    368
    369    void checkWorkingDirectory( final boolean condition, final String errMsg ) throws WikiException {
    370        if( condition ) {
    371            throw new WikiException( errMsg );
    372        }
    373    }
    374
    375    void initExtraComponents( final Map< String, String > extraComponents ) {
    376        for( final Map.Entry< String, String > extraComponent : extraComponents.entrySet() ) {
    377            try {
    378                LOG.info( "Registering on WikiEngine {} as {}", extraComponent.getKey(), extraComponent.getValue() );
    379                initComponent( extraComponent.getKey(), Class.forName( extraComponent.getValue() ) );
    380            } catch( final Exception e ) {
    381                LOG.error( "Unable to start {}", extraComponent.getKey(), e );
    382            }
    383        }
    384    }
    385
    386    < T > void initComponent( final Class< T > componentClass, final Object... initArgs ) throws Exception {
    387        initComponent( componentClass.getName(), componentClass, initArgs );
    388    }
    389
    390    < T > void initComponent( final String componentInitClass, final Class< T > componentClass, final Object... initArgs ) throws Exception {
    391        final T component;
    392        if( initArgs == null || initArgs.length == 0 ) {
    393            component = ClassUtil.getMappedObject( componentInitClass );
    394        } else {
    395            component = ClassUtil.getMappedObject( componentInitClass, initArgs );
    396        }
    397        managers.put( componentClass, component );
    398        if( Initializable.class.isAssignableFrom( component.getClass() ) ) {
    399            ( ( Initializable )component ).initialize( this, m_properties );
    400        }
    401    }
    402
    403    /** {@inheritDoc} */
    404    @Override
    405    @SuppressWarnings( "unchecked" )
    406    public < T > T getManager( final Class< T > manager ) {
    407        return ( T )managers.entrySet().stream()
    408                                       .filter( e -> manager.isAssignableFrom( e.getKey() ) )
    409                                       .map( Map.Entry::getValue )
    410                                       .findFirst().orElse( null );
    411    }
    412
    413    /** {@inheritDoc} */
    414    @Override
    415    @SuppressWarnings( "unchecked" )
    416    public < T > List< T > getManagers( final Class< T > manager ) {
    417        return ( List< T > )managers.entrySet().stream()
    418                                               .filter( e -> manager.isAssignableFrom( e.getKey() ) )
    419                                               .map( Map.Entry::getValue )
    420                                               .collect( Collectors.toList() );
    421    }
    422
    423    /** {@inheritDoc} */
    424    @Override
    425    public boolean isConfigured() {
    426        return m_isConfigured;
    427    }
    428
    429    /**
    430     * Checks if the template directory specified in the wiki's properties actually exists. If it doesn't, then {@code m_templateDir} is
    431     * set to {@link #DEFAULT_TEMPLATE_NAME}.
    432     * <p>
    433     * This checks the existence of the <tt>ViewTemplate.jsp</tt> file, which exists in every template using {@code m_servletContext.getRealPath("/")}.
    434     * <p>
    435     * {@code m_servletContext.getRealPath("/")} can return {@code null} on certain servers/conditions (f.ex, packed wars), an extra check
    436     * against {@code m_servletContext.getResource} is made.
    437     */
    438    void enforceValidTemplateDirectory() {
    439        if( m_servletContext != null ) {
    440            final String viewTemplate = "templates" + File.separator + getTemplateDir() + File.separator + "ViewTemplate.jsp";
    441            boolean exists = new File( m_servletContext.getRealPath( "/" ) + viewTemplate ).exists();
    442            if( !exists ) {
    443                try {
    444                    final URL url = m_servletContext.getResource( viewTemplate );
    445                    exists = url != null && StringUtils.isNotEmpty( url.getFile() );
    446                } catch( final MalformedURLException e ) {
    447                    LOG.warn( "template not found with viewTemplate {}", viewTemplate );
    448                }
    449            }
    450            if( !exists ) {
    451                LOG.warn( "{} template not found, updating WikiEngine's default template to {}", getTemplateDir(), DEFAULT_TEMPLATE_NAME );
    452                m_templateDir = DEFAULT_TEMPLATE_NAME;
    453            }
    454        }
    455    }
    456
    457    /**
    458     *  Initializes the reference manager. Scans all existing WikiPages for
    459     *  internal links and adds them to the ReferenceManager object.
    460     *
    461     *  @throws WikiException If the reference manager initialization fails.
    462     */
    463    public void initReferenceManager() throws WikiException {
    464        try {
    465            // Build a new manager with default key lists.
    466            if( getManager( ReferenceManager.class ) == null ) {
    467                final ArrayList< Page > pages = new ArrayList<>();
    468                pages.addAll( getManager( PageManager.class ).getAllPages() );
    469                pages.addAll( getManager( AttachmentManager.class ).getAllAttachments() );
    470                final String refMgrClassName = m_properties.getProperty( PROP_REF_MANAGER_IMPL, ClassUtil.getMappedClass( ReferenceManager.class.getName() ).getName() );
    471
    472                initComponent( refMgrClassName, ReferenceManager.class, this );
    473
    474                getManager( ReferenceManager.class ).initialize( pages );
    475            }
    476
    477        } catch( final ProviderException e ) {
    478            LOG.fatal( "PageProvider is unable to list pages: ", e );
    479        } catch( final Exception e ) {
    480            throw new WikiException( "Could not instantiate ReferenceManager: " + e.getMessage(), e );
    481        }
    482    }
    483
    484    /** {@inheritDoc} */
    485    @Override
    486    public Properties getWikiProperties() {
    487        return m_properties;
    488    }
    489
    490    /** {@inheritDoc} */
    491    @Override
    492    public String getWorkDir() {
    493        return m_workDir;
    494    }
    495
    496    /** {@inheritDoc} */
    497    @Override
    498    public String getTemplateDir() {
    499        return m_templateDir;
    500    }
    501
    502    /** {@inheritDoc} */
    503    @Override
    504    public Date getStartTime() {
    505        return ( Date )m_startTime.clone();
    506    }
    507
    508    /** {@inheritDoc} */
    509    @Override
    510    public String getBaseURL() {
    511        return m_servletContext.getContextPath();
    512    }
    513
    514    /** {@inheritDoc} */
    515    @Override
    516    public String getGlobalRSSURL() {
    517        final RSSGenerator rssGenerator = getManager( RSSGenerator.class );
    518        if( rssGenerator != null && rssGenerator.isEnabled() ) {
    519            return getBaseURL() + "/" + rssGenerator.getRssFile();
    520        }
    521
    522        return null;
    523    }
    524
    525    /** {@inheritDoc} */
    526    @Override
    527    public String getInterWikiURL( final String wikiName ) {
    528        return TextUtil.getStringProperty( m_properties,PROP_INTERWIKIREF + wikiName,null );
    529    }
    530
    531    /** {@inheritDoc} */
    532    @Override
    533    public String getURL( final String context, String pageName, final String params ) {
    534        if( pageName == null ) {
    535            pageName = getFrontPage();
    536        }
    537        final URLConstructor urlConstructor = getManager( URLConstructor.class );
    538        return urlConstructor.makeURL( context, pageName, params );
    539    }
    540
    541    /** {@inheritDoc} */
    542    @Override
    543    public String getFrontPage() {
    544        return m_frontPage;
    545    }
    546
    547    /** {@inheritDoc} */
    548    @Override
    549    public ServletContext getServletContext() {
    550        return m_servletContext;
    551    }
    552
    553    /** {@inheritDoc} */
    554    @Override
    555    public Collection< String > getAllInterWikiLinks() {
    556        final ArrayList< String > list = new ArrayList<>();
    557        for( final Enumeration< ? > i = m_properties.propertyNames(); i.hasMoreElements(); ) {
    558            final String prop = ( String )i.nextElement();
    559            if( prop.startsWith( PROP_INTERWIKIREF ) ) {
    560                list.add( prop.substring( prop.lastIndexOf( "." ) + 1 ) );
    561            }
    562        }
    563
    564        return list;
    565    }
    566
    567    /** {@inheritDoc} */
    568    @Override
    569    public Collection< String > getAllInlinedImagePatterns() {
    570        final ArrayList< String > ptrnlist = new ArrayList<>();
    571        for( final Enumeration< ? > e = m_properties.propertyNames(); e.hasMoreElements(); ) {
    572            final String name = ( String )e.nextElement();
    573            if( name.startsWith( PROP_INLINEIMAGEPTRN ) ) {
    574                ptrnlist.add( TextUtil.getStringProperty( m_properties, name, null ) );
    575            }
    576        }
    577
    578        if( ptrnlist.isEmpty() ) {
    579            ptrnlist.add( DEFAULT_INLINEPATTERN );
    580        }
    581
    582        return ptrnlist;
    583    }
    584
    585    /** {@inheritDoc} */
    586    @Override
    587    public String getSpecialPageReference( final String original ) {
    588        return getManager( CommandResolver.class ).getSpecialPageReference( original );
    589    }
    590
    591    /** {@inheritDoc} */
    592    @Override
    593    public String getApplicationName() {
    594        final String appName = TextUtil.getStringProperty( m_properties, PROP_APPNAME, Release.APPNAME );
    595        return TextUtil.cleanString( appName, TextUtil.PUNCTUATION_CHARS_ALLOWED );
    596    }
    597
    598    /** {@inheritDoc} */
    599    @Override
    600    public String getFinalPageName( final String page ) throws ProviderException {
    601        return getManager( CommandResolver.class ).getFinalPageName( page );
    602    }
    603
    604    /** {@inheritDoc} */
    605    @Override
    606    public String encodeName( final String pagename ) {
    607        try {
    608            return URLEncoder.encode( pagename, m_useUTF8 ? StandardCharsets.UTF_8.name() : StandardCharsets.ISO_8859_1.name() );
    609        } catch( final UnsupportedEncodingException e ) {
    610            throw new InternalWikiException( "ISO-8859-1 not a supported encoding!?!  Your platform is borked." , e);
    611        }
    612    }
    613
    614    /** {@inheritDoc} */
    615    @Override
    616    public String decodeName( final String pagerequest ) {
    617        try {
    618            return URLDecoder.decode( pagerequest, m_useUTF8 ? StandardCharsets.UTF_8.name() : StandardCharsets.ISO_8859_1.name() );
    619        } catch( final UnsupportedEncodingException e ) {
    620            throw new InternalWikiException("ISO-8859-1 not a supported encoding!?!  Your platform is borked.", e);
    621        }
    622    }
    623
    624    /** {@inheritDoc} */
    625    @Override
    626    public Charset getContentEncoding() {
    627        if( m_useUTF8 ) {
    628            return StandardCharsets.UTF_8;
    629        }
    630        return StandardCharsets.ISO_8859_1;
    631    }
    632
    633    /**
    634     * {@inheritDoc}
    635     * <p>It is called by {@link WikiServlet#destroy()}. When this method is called, it fires a "shutdown" WikiEngineEvent to
    636     * all registered listeners.
    637     */
    638    @Override
    639    public void shutdown() {
    640        fireEvent( WikiEngineEvent.SHUTDOWN );
    641        getManager( CachingManager.class ).shutdown();
    642        getManager( FilterManager.class ).destroy();
    643        WikiEventManager.shutdown();
    644    }
    645
    646    /**
    647     *  Returns the current TemplateManager.
    648     *
    649     *  @return A TemplateManager instance.
    650     * @deprecated use {@code getManager( TemplateManager.class )} instead.
    651     */
    652    @Deprecated
    653    public TemplateManager getTemplateManager() {
    654        return getManager( TemplateManager.class );
    655    }
    656
    657    /**
    658     * Returns the {@link org.apache.wiki.workflow.WorkflowManager} associated with this WikiEngine. If the WikiEngine has not been
    659     * initialized, this method will return <code>null</code>.
    660     *
    661     * @return the task queue
    662     * @deprecated use {@code getManager( WorkflowManager.class )} instead.
    663     */
    664    @Deprecated
    665    public WorkflowManager getWorkflowManager() {
    666        return getManager( WorkflowManager.class );
    667    }
    668
    669    /**
    670     * Returns this object's ReferenceManager.
    671     *
    672     * @return The current ReferenceManager instance.
    673     * @since 1.6.1
    674     * @deprecated use {@code getManager( ReferenceManager.class )} instead.
    675     */
    676    @Deprecated
    677    public ReferenceManager getReferenceManager() {
    678        return getManager( ReferenceManager.class );
    679    }
    680
    681    /**
    682     * Returns the current rendering manager for this wiki application.
    683     *
    684     * @since 2.3.27
    685     * @return A RenderingManager object.
    686     * @deprecated use {@code getManager( RenderingManager.class )} instead.
    687     */
    688    @Deprecated
    689    public RenderingManager getRenderingManager() {
    690        return getManager( RenderingManager.class );
    691    }
    692
    693    /**
    694     * Returns the current plugin manager.
    695     *
    696     * @since 1.6.1
    697     * @return The current PluginManager instance
    698     * @deprecated use {@code getManager( PluginManager.class )} instead.
    699     */
    700    @Deprecated
    701    public PluginManager getPluginManager() {
    702        return getManager( PluginManager.class );
    703    }
    704
    705    /**
    706     *  Returns the current variable manager.
    707     *
    708     *  @return The current VariableManager.
    709     * @deprecated use {@code getManager( VariableManager.class )} instead.
    710     */
    711    @Deprecated
    712    public VariableManager getVariableManager()  {
    713        return getManager( VariableManager.class );
    714    }
    715
    716    /**
    717     * Returns the current PageManager which is responsible for storing and managing WikiPages.
    718     *
    719     * @return The current PageManager instance.
    720     * @deprecated use {@code getManager( PageManager.class )} instead.
    721     */
    722    @Deprecated
    723    public PageManager getPageManager() {
    724        return getManager( PageManager.class );
    725    }
    726
    727    /**
    728     * Returns the CommandResolver for this wiki engine.
    729     *
    730     * @return the resolver
    731     * @deprecated use {@code getManager( CommandResolver.class )} instead.
    732     */
    733    @Deprecated
    734    public CommandResolver getCommandResolver() {
    735        return getManager( CommandResolver.class );
    736    }
    737
    738    /**
    739     * Returns the current AttachmentManager, which is responsible for storing and managing attachments.
    740     *
    741     * @since 1.9.31.
    742     * @return The current AttachmentManager instance
    743     * @deprecated use {@code getManager( AttachmentManager.class )} instead.
    744     */
    745    @Deprecated
    746    public AttachmentManager getAttachmentManager() {
    747        return getManager( AttachmentManager.class );
    748    }
    749
    750    /**
    751     * Returns the currently used authorization manager.
    752     *
    753     * @return The current AuthorizationManager instance.
    754     * @deprecated use {@code getManager( AuthorizationManager.class )} instead.
    755     */
    756    @Deprecated
    757    public AuthorizationManager getAuthorizationManager()  {
    758        return getManager( AuthorizationManager.class );
    759    }
    760
    761    /**
    762     * Returns the currently used authentication manager.
    763     *
    764     * @return The current AuthenticationManager instance.
    765     * @deprecated use {@code getManager( AuthenticationManager.class )} instead.
    766     */
    767    @Deprecated
    768    public AuthenticationManager getAuthenticationManager() {
    769        return getManager( AuthenticationManager.class );
    770    }
    771
    772    /**
    773     * Returns the manager responsible for the filters.
    774     *
    775     * @since 2.1.88
    776     * @return The current FilterManager instance.
    777     * @deprecated use {@code getManager( FilterManager.class )} instead.
    778     */
    779    @Deprecated
    780    public FilterManager getFilterManager() {
    781        return getManager( FilterManager.class );
    782    }
    783
    784    /**
    785     * Returns the manager responsible for searching the Wiki.
    786     *
    787     * @since 2.2.21
    788     * @return The current SearchManager instance.
    789     * @deprecated use {@code getManager( SearchManager.class )} instead.
    790     */
    791    @Deprecated
    792    public SearchManager getSearchManager() {
    793        return getManager( SearchManager.class );
    794    }
    795
    796    /**
    797     * Returns the progress manager we're using
    798     *
    799     * @return A ProgressManager.
    800     * @since 2.6
    801     * @deprecated use {@code getManager( ProgressManager.class )} instead.
    802     */
    803    @Deprecated
    804    public ProgressManager getProgressManager() {
    805        return getManager( ProgressManager.class );
    806    }
    807
    808    /** {@inheritDoc} */
    809    @Override
    810    public String getRootPath() {
    811        return m_rootPath;
    812    }
    813
    814    /**
    815     * @since 2.2.6
    816     * @return the URL constructor.
    817     * @deprecated use {@code getManager( URLConstructor.class )} instead.
    818     */
    819    @Deprecated
    820    public URLConstructor getURLConstructor() {
    821        return getManager( URLConstructor.class );
    822    }
    823
    824    /**
    825     * Returns the RSSGenerator. If the property <code>jspwiki.rss.generate</code> has not been set to <code>true</code>, this method
    826     * will return <code>null</code>, <em>and callers should check for this value.</em>
    827     *
    828     * @since 2.1.165
    829     * @return the RSS generator
    830     * @deprecated use {@code getManager( RSSGenerator.class )} instead.
    831     */
    832    @Deprecated
    833    public RSSGenerator getRSSGenerator() {
    834        return getManager( RSSGenerator.class );
    835    }
    836
    837    /**
    838     *  Returns the PageRenamer employed by this WikiEngine.
    839     *
    840     *  @since 2.5.141
    841     *  @return The current PageRenamer instance.
    842     * @deprecated use {@code getManager( PageRenamer.class )} instead.
    843     */
    844    @Deprecated
    845    public PageRenamer getPageRenamer() {
    846        return getManager( PageRenamer.class );
    847    }
    848
    849    /**
    850     *  Returns the UserManager employed by this WikiEngine.
    851     *
    852     *  @since 2.3
    853     *  @return The current UserManager instance.
    854     * @deprecated use {@code getManager( UserManager.class )} instead.
    855     */
    856    @Deprecated
    857    public UserManager getUserManager() {
    858        return getManager( UserManager.class );
    859    }
    860
    861    /**
    862     *  Returns the TasksManager employed by this WikiEngine.
    863     *
    864     *  @return The current TasksManager instance.
    865     * @deprecated use {@code getManager( TaskManager.class )} instead.
    866     */
    867    @Deprecated
    868    public TasksManager getTasksManager() {
    869        return getManager( TasksManager.class );
    870    }
    871
    872    /**
    873     *  Returns the GroupManager employed by this WikiEngine.
    874     *
    875     *  @since 2.3
    876     *  @return The current GroupManager instance.
    877     * @deprecated use {@code getManager( GroupManager.class )} instead.
    878     */
    879    @Deprecated
    880    public GroupManager getGroupManager() {
    881        return getManager( GroupManager.class );
    882    }
    883
    884    /**
    885     *  Returns the current {@link AdminBeanManager}.
    886     *
    887     *  @return The current {@link AdminBeanManager}.
    888     *  @since  2.6
    889     * @deprecated use {@code getManager( AdminBeanManager.class )} instead.
    890     */
    891    @Deprecated
    892    public AdminBeanManager getAdminBeanManager() {
    893        return getManager( AdminBeanManager.class );
    894    }
    895
    896    /**
    897     *  Returns the AclManager employed by this WikiEngine. The AclManager is lazily initialized.
    898     *  <p>
    899     *  The AclManager implementing class may be set by the System property {@link #PROP_ACL_MANAGER_IMPL}.
    900     *  </p>
    901     *
    902     * @since 2.3
    903     * @return The current AclManager.
    904     * @deprecated use {@code getManager( AclManager.class )} instead.
    905     */
    906    @Deprecated
    907    public AclManager getAclManager()  {
    908        return getManager( AclManager.class );
    909    }
    910
    911    /**
    912     *  Returns the DifferenceManager so that texts can be compared.
    913     *
    914     *  @return the difference manager.
    915     * @deprecated use {@code getManager( DifferenceManager.class )} instead.
    916     */
    917    @Deprecated
    918    public DifferenceManager getDifferenceManager() {
    919        return getManager( DifferenceManager.class );
    920    }
    921
    922    /**
    923     *  Returns the current EditorManager instance.
    924     *
    925     *  @return The current EditorManager.
    926     * @deprecated use {@code getManager( EditorManager.class )} instead.
    927     */
    928    @Deprecated
    929    public EditorManager getEditorManager() {
    930        return getManager( EditorManager.class );
    931    }
    932
    933    /**
    934     *  Returns the current i18n manager.
    935     *
    936     *  @return The current Intertan... Interante... Internatatializ... Whatever.
    937     * @deprecated use {@code getManager( InternationalizationManager.class )} instead.
    938     */
    939    @Deprecated
    940    public InternationalizationManager getInternationalizationManager() {
    941        return getManager( InternationalizationManager.class );
    942    }
    943
    944    /** {@inheritDoc} */
    945    @Override
    946    public final synchronized void addWikiEventListener( final WikiEventListener listener ) {
    947        WikiEventManager.addWikiEventListener( this, listener );
    948    }
    949
    950    /** {@inheritDoc} */
    951    @Override
    952    public final synchronized void removeWikiEventListener( final WikiEventListener listener ) {
    953        WikiEventManager.removeWikiEventListener( this, listener );
    954    }
    955
    956    /**
    957     * Fires a WikiEngineEvent to all registered listeners.
    958     *
    959     * @param type  the event type
    960     */
    961    protected final void fireEvent( final int type ) {
    962        if( WikiEventManager.isListening(this ) ) {
    963            WikiEventManager.fireEvent( this, new WikiEngineEvent(this, type ) );
    964        }
    965    }
    966
    967    /**
    968     * Fires a WikiPageEvent to all registered listeners.
    969     *
    970     * @param type  the event type
    971     */
    972    protected final void firePageEvent( final int type, final String pageName ) {
    973        if( WikiEventManager.isListening(this ) ) {
    974            WikiEventManager.fireEvent(this,new WikiPageEvent(this, type, pageName ) );
    975        }
    976    }
    977
    978    /** {@inheritDoc} */
    979    @Override
    980    public void setAttribute( final String key, final Object value ) {
    981        m_attributes.put( key, value );
    982    }
    983
    984    /** {@inheritDoc} */
    985    @Override
    986    @SuppressWarnings( "unchecked" )
    987    public < T > T getAttribute( final String key ) {
    988        return ( T )m_attributes.get( key );
    989    }
    990
    991    /** {@inheritDoc} */
    992    @Override
    993    @SuppressWarnings( "unchecked" )
    994    public < T > T removeAttribute( final String key ) {
    995        return ( T )m_attributes.remove( key );
    996    }
    997
    998}