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