    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.api.core;
    020
    021import org.apache.logging.log4j.LogManager;
    022import org.apache.wiki.api.engine.EngineLifecycleExtension;
    023import org.apache.wiki.api.events.CustomWikiEventListener;
    024import org.apache.wiki.api.exceptions.ProviderException;
    025import org.apache.wiki.api.exceptions.WikiException;
    026import org.apache.wiki.event.WikiEventListener;
    027import org.apache.wiki.event.WikiEventManager;
    028import org.apache.wiki.util.TextUtil;
    029
    030import javax.servlet.ServletContext;
    031import java.io.File;
    032import java.io.FileNotFoundException;
    033import java.io.IOException;
    034import java.io.InputStream;
    035import java.io.OutputStream;
    036import java.net.MalformedURLException;
    037import java.net.URL;
    038import java.nio.charset.Charset;
    039import java.nio.file.Files;
    040import java.util.Collection;
    041import java.util.Date;
    042import java.util.List;
    043import java.util.Properties;
    044import java.util.ServiceLoader;
    045
    046
    047/**
    048 *  Provides Wiki services to the JSP page.
    049 *
    050 *  <P>
    051 *  This is the main interface through which everything should go.
    052 *
    053 *  <p>
    054 *  There's basically only a single Engine for each web application, and you should always get it using either the
    055 *  {@code Context#getEngine()} method or through {@code Wiki.engine().find(..)} DSL methods.
    056 */
    057public interface Engine {
    058
    059    /** The default inlining pattern.  Currently "*.png" */
    060    String DEFAULT_INLINEPATTERN = "*.png";
    061
    062    /** The name used for the default template. The value is {@value}. */
    063    String DEFAULT_TEMPLATE_NAME = "default";
    064
    065    /** Property for application name */
    066    String PROP_APPNAME = "jspwiki.applicationName";
    067
    068    /** This property defines the inline image pattern.  It's current value is {@value} */
    069    String PROP_INLINEIMAGEPTRN = "jspwiki.translatorReader.inlinePattern";
    070
    071    /** Property start for any interwiki reference. */
    072    String PROP_INTERWIKIREF = "jspwiki.interWikiRef.";
    073
    074    /** The property name defining which packages will be searched for plugin classes. */
    075    String PROP_SEARCHPATH = "jspwiki.plugin.searchPath";
    076
    077    /** If true, then the user name will be stored with the page data.*/
    078    String PROP_STOREUSERNAME= "jspwiki.storeUserName";
    079
    080    /** Define the used encoding.  Currently supported are ISO-8859-1 and UTF-8 */
    081    String PROP_ENCODING = "jspwiki.encoding";
    082
    083    /** Do not use encoding in WikiJSPFilter, default is false for most servers.
    084     Double negative, cause for most servers you don't need the property */
    085    String PROP_NO_FILTER_ENCODING = "jspwiki.nofilterencoding";
    086
    087    /** Property name for where the jspwiki work directory should be.
    088     If not specified, reverts to ${java.tmpdir}. */
    089    String PROP_WORKDIR = "jspwiki.workDir";
    090
    091    /** The name of the cookie that gets stored to the user browser. */
    092    String PREFS_COOKIE_NAME = "JSPWikiUserProfile";
    093
    094    /** Property name for the "match english plurals" -hack. */
    095    String PROP_MATCHPLURALS = "jspwiki.translatorReader.matchEnglishPlurals";
    096
    097    /** Property name for the template that is used. */
    098    String PROP_TEMPLATEDIR = "jspwiki.templateDir";
    099
    100    /** Property name for the default front page. */
    101    String PROP_FRONTPAGE = "jspwiki.frontPage";
    102
    103    /** Property name for setting the url generator instance */
    104    String PROP_URLCONSTRUCTOR = "jspwiki.urlConstructor";
    105
    106    /** The name of the property containing the ACLManager implementing class. The value is {@value}. */
    107    String PROP_ACL_MANAGER_IMPL = "jspwiki.aclManager";
    108
    109    /** The name of the property containing the ReferenceManager implementing class. The value is {@value}. */
    110    String PROP_REF_MANAGER_IMPL = "jspwiki.refManager";
    111
    112    /** If this property is set to false, we don't allow the creation of empty pages */
    113    String PROP_ALLOW_CREATION_OF_EMPTY_PAGES = "jspwiki.allowCreationOfEmptyPages";
    114
    115    /**
    116     * Adapt Engine to a concrete type.
    117     *
    118     * @param cls class denoting the type to adapt to.
    119     * @param <E> type to adapt to.
    120     * @return engine instance adapted to the requested type. Might throw an unchecked exception if the instance cannot be adapted to requested type!
    121     */
    122    @SuppressWarnings( "unchecked" )
    123    default < E extends Engine > E adapt( final Class< E > cls ) {
    124        return ( E )this;
    125    }
    126
    127    /**
    128     * Retrieves the object instantiated by the Engine matching the requested type.
    129     *
    130     * @param manager requested object instantiated by the Engine.
    131     * @param <T> type of the requested object.
    132     * @return requested object instantiated by the Engine, {@code null} if not available.
    133     */
    134    < T > T getManager( Class< T > manager );
    135
    136    /**
    137     * Retrieves the objects instantiated by the Engine that can be assigned to the requested type.
    138     *
    139     * @param manager requested objectx instantiated by the Engine.
    140     * @param <T> type of the requested object.
    141     * @return collection of requested objects instantiated by the Engine, {@code empty} list if none available.
    142     */
    143    < T > List< T > getManagers( Class< T > manager );
    144
    145    /**
    146     * check if the Engine has been configured.
    147     *
    148     * @return {@code true} if it has, {@code false} otherwise.
    149     */
    150    boolean isConfigured();
    151
    152    /**
    153     *  Returns the set of properties that the Engine was initialized with.  Note that this method returns a direct reference, so it's
    154     *  possible to manipulate the properties.  However, this is not advised unless you really know what you're doing.
    155     *
    156     *  @return The wiki properties
    157     */
    158    Properties getWikiProperties();
    159
    160    /**
    161     *  Returns the JSPWiki working directory set with "jspwiki.workDir".
    162     *
    163     *  @since 2.1.100
    164     *  @return The working directory.
    165     */
    166    String getWorkDir();
    167
    168    /**
    169     *  Returns the current template directory.
    170     *
    171     *  @since 1.9.20
    172     *  @return The template directory as initialized by the engine.
    173     */
    174    String getTemplateDir();
    175
    176    /**
    177     * Returns plugins' search path.
    178     *
    179     * @return plugins' search path.
    180     */
    181    default String getPluginSearchPath() {
    182        return TextUtil.getStringProperty( getWikiProperties(), PROP_SEARCHPATH, null );
    183    }
    184
    185    /**
    186     *  Returns the moment when this engine was started.
    187     *
    188     *  @since 2.0.15.
    189     *  @return The start time of this wiki.
    190     */
    191    Date getStartTime();
    192
    193    /**
    194     *  Returns the base URL, telling where this Wiki actually lives.
    195     *
    196     *  @since 1.6.1
    197     *  @return The Base URL.
    198     */
    199    String getBaseURL();
    200
    201    /**
    202     *  Returns the URL of the global RSS file.  May be null, if the RSS file generation is not operational.
    203     *
    204     *  @since 1.7.10
    205     *  @return The global RSS url
    206     */
    207    String getGlobalRSSURL();
    208
    209    /**
    210     *  Returns an URL to some other Wiki that we know.
    211     *
    212     *  @param  wikiName The name of the other wiki.
    213     *  @return null, if no such reference was found.
    214     */
    215    String getInterWikiURL( String wikiName );
    216
    217    /**
    218     *  Returns an URL if a WikiContext is not available.
    219     *
    220     *  @param context The WikiContext (VIEW, EDIT, etc...)
    221     *  @param pageName Name of the page, as usual
    222     *  @param params List of parameters. May be null, if no parameters.
    223     *  @return An URL (absolute or relative).
    224     */
    225    String getURL( String context, String pageName, String params );
    226
    227    /**
    228     *  Returns the default front page, if no page is used.
    229     *
    230     *  @return The front page name.
    231     */
    232    String getFrontPage();
    233
    234    /**
    235     *  Returns the ServletContext that this particular Engine was initialized with. <strong>It may return {@code null}</strong>,
    236     *  if the Engine is not running inside a servlet container!
    237     *
    238     *  @since 1.7.10
    239     *  @return ServletContext of the Engine, or {@code null}.
    240     */
    241    ServletContext getServletContext();
    242
    243    /**
    244     * Looks up and obtains a configuration file inside the WEB-INF folder of a wiki webapp.
    245     *
    246     * @param name the file to obtain, <em>e.g.</em>, <code>jspwiki.policy</code>
    247     * @return the URL to the file
    248     */
    249    default URL findConfigFile( final String name ) {
    250        LogManager.getLogger( Engine.class ).info( "looking for " + name + " inside WEB-INF " );
    251        // Try creating an absolute path first
    252        File defaultFile = null;
    253        if( getRootPath() != null ) {
    254            defaultFile = new File( getRootPath() + "/WEB-INF/" + name );
    255        }
    256        if ( defaultFile != null && defaultFile.exists() ) {
    257            try {
    258                return defaultFile.toURI().toURL();
    259            } catch ( final MalformedURLException e ) {
    260                // Shouldn't happen, but log it if it does
    261                LogManager.getLogger( Engine.class ).warn( "Malformed URL: " + e.getMessage() );
    262            }
    263        }
    264
    265        // Ok, the absolute path didn't work; try other methods
    266        URL path = null;
    267
    268        if( getServletContext() != null ) {
    269            final File tmpFile;
    270            try {
    271                tmpFile = File.createTempFile( "temp." + name, "" );
    272            } catch( final IOException e ) {
    273                LogManager.getLogger( Engine.class ).error( "unable to create a temp file to load onto the policy", e );
    274                return null;
    275            }
    276            tmpFile.deleteOnExit();
    277            LogManager.getLogger( Engine.class ).info( "looking for /" + name + " on classpath" );
    278            //  create a tmp file of the policy loaded as an InputStream and return the URL to it
    279            try( final InputStream is = Engine.class.getResourceAsStream( "/" + name );
    280                final OutputStream os = Files.newOutputStream( tmpFile.toPath() ) ) {
    281                if( is == null ) {
    282                    throw new FileNotFoundException( name + " not found" );
    283                }
    284                final URL url = getServletContext().getResource( "/WEB-INF/" + name );
    285                if( url != null ) {
    286                    return url;
    287                }
    288
    289                final byte[] buff = new byte[1024];
    290                int bytes;
    291                while( ( bytes = is.read( buff ) ) != -1 ) {
    292                    os.write( buff, 0, bytes );
    293                }
    294
    295                path = tmpFile.toURI().toURL();
    296            } catch( final MalformedURLException e ) {
    297                // This should never happen unless I screw up
    298                LogManager.getLogger( Engine.class ).fatal( "Your code is b0rked.  You are a bad person.", e );
    299            } catch( final IOException e ) {
    300                LogManager.getLogger( Engine.class ).error( "failed to load security policy from file " + name + ",stacktrace follows", e );
    301            }
    302        }
    303        return path;
    304    }
    305
    306    /**
    307     *  Returns a collection of all supported InterWiki links.
    308     *
    309     *  @return A Collection of Strings.
    310     */
    311    Collection< String > getAllInterWikiLinks();
    312
    313    /**
    314     *  Returns a collection of all image types that get inlined.
    315     *
    316     *  @return A Collection of Strings with a regexp pattern.
    317     */
    318    Collection< String > getAllInlinedImagePatterns();
    319
    320    /**
    321     *  <p>If the page is a special page, then returns a direct URL to that page. Otherwise returns <code>null</code>.
    322     *  This method delegates requests to {@link org.apache.wiki.ui.CommandResolver#getSpecialPageReference(String)}.</p>
    323     *  <p>Special pages are defined in jspwiki.properties using the jspwiki.specialPage setting. They're typically used to give Wiki page
    324     *  names to e.g. custom JSP pages.</p>
    325     *
    326     *  @param original The page to check
    327     *  @return A reference to the page, or null, if there's no special page.
    328     */
    329    String getSpecialPageReference( String original );
    330
    331    /**
    332     *  Returns the name of the application.
    333     *
    334     *  @return A string describing the name of this application.
    335     */
    336    String getApplicationName();
    337
    338    /**
    339     *  Returns the root path.  The root path is where the Engine is located in the file system.
    340     *
    341     *  @since 2.2
    342     *  @return A path to where the Wiki is installed in the local filesystem.
    343     */
    344    String getRootPath();
    345
    346    /**
    347     *  Returns the correct page name, or null, if no such page can be found.  Aliases are considered. This method simply delegates to
    348     *  {@link org.apache.wiki.ui.CommandResolver#getFinalPageName(String)}.
    349     *
    350     *  @since 2.0
    351     *  @param page Page name.
    352     *  @return The rewritten page name, or null, if the page does not exist.
    353     *  @throws ProviderException If something goes wrong in the backend.
    354     */
    355    String getFinalPageName( String page ) throws ProviderException;
    356
    357    /**
    358     *  Turns a WikiName into something that can be called through using an URL.
    359     *
    360     *  @since 1.4.1
    361     *  @param pagename A name. Can be actually any string.
    362     *  @return A properly encoded name.
    363     *  @see #decodeName(String)
    364     */
    365    String encodeName( String pagename );
    366
    367    /**
    368     *  Decodes a URL-encoded request back to regular life.  This properly heeds the encoding as defined in the settings file.
    369     *
    370     *  @param pagerequest The URL-encoded string to decode
    371     *  @return A decoded string.
    372     *  @see #encodeName(String)
    373     */
    374    String decodeName( String pagerequest );
    375
    376    /**
    377     *  Returns the IANA name of the character set encoding we're supposed to be using right now.
    378     *
    379     *  @since 1.5.3
    380     *  @return The content encoding (either UTF-8 or ISO-8859-1).
    381     */
    382    Charset getContentEncoding();
    383
    384    /**
    385     * Registers a WikiEventListener with this instance.
    386     *
    387     * @param listener the event listener
    388     */
    389    void addWikiEventListener( WikiEventListener listener );
    390
    391    /**
    392     * Un-registers a WikiEventListener with this instance.
    393     *
    394     * @param listener the event listener
    395     */
    396    void removeWikiEventListener( WikiEventListener listener );
    397
    398    /**
    399     * Adds an attribute to the engine for the duration of this engine.  The value is not persisted.
    400     *
    401     * @since 2.4.91
    402     * @param key the attribute name
    403     * @param value the value
    404     */
    405    void setAttribute( String key, Object value );
    406
    407    /**
    408     *  Gets an attribute from the engine.
    409     *
    410     *  @param key the attribute name
    411     *  @return the value
    412     */
    413    < T > T getAttribute( String key );
    414
    415    /**
    416     *  Removes an attribute.
    417     *
    418     *  @param key The key of the attribute to remove.
    419     *  @return The previous attribute, if it existed.
    420     */
    421    < T > T removeAttribute( String key );
    422
    423    /**
    424     * Initializes the {@code Engine}, notifying all the {@link EngineLifecycleExtension}s.
    425     *
    426     * @param properties Wiki configuration properties.
    427     * @throws WikiException if something happens while setting up the {@code Engine}.
    428     */
    429    default void start( final Properties properties ) throws WikiException {
    430        final var loader = ServiceLoader.load( EngineLifecycleExtension.class );
    431        for( final var extension : loader ) {
    432            extension.onInit( properties );
    433        }
    434        initialize( properties );
    435        for( final var extension : loader ) {
    436            extension.onStart( this, properties );
    437        }
    438        final var events = ServiceLoader.load( CustomWikiEventListener.class );
    439        for( final var event : events ) {
    440            CustomWikiEventListener.LISTENERS.add( event );
    441            event.initialize( this, getWikiProperties() );
    442            WikiEventManager.addWikiEventListener( event.client(), event );
    443        }
    444    }
    445
    446    /**
    447     * Shuts down the {@code Engine}, notifying all the {@link EngineLifecycleExtension}s.
    448     */
    449    default void stop() {
    450        final ServiceLoader< EngineLifecycleExtension > loader = ServiceLoader.load( EngineLifecycleExtension.class );
    451        for( final EngineLifecycleExtension extension : loader ) {
    452            extension.onShutdown( this, getWikiProperties() );
    453        }
    454        shutdown();
    455    }
    456
    457    /**
    458     * Sets up the application's running {@code Engine}.
    459     *
    460     * @param properties Wiki configuration properties.
    461     * @throws WikiException if something happens while setting up the {@code Engine}.
    462     */
    463    void initialize( Properties properties ) throws WikiException;
    464
    465    /**
    466     * Signals that the {@code Engine} will be shut down by the servlet container.
    467     */
    468    void shutdown();
    469
    470}