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