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}