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