Objectives#

The main goal of the public api is to have a stable codebase that developers can use to extend JSPWiki. Changes in this API would imply a major release.

Starting with 2.11.0.M7, the public API is reachable at the org.apache.jspwiki:jspwiki-api:LATEST_JSPWIKI maven coordinates. Its contents are covered in the following sections.

Core API#

It's compromised of the classes and interfaces in the org.apache.wiki.api.core, org.apache.wiki.api.engine, org.apache.wiki.api.exceptions and org.apache.wiki.api.spi packages, plus the org.apache.wiki.api.Release class. It models the core abstractions of JSPWiki:

  • Page, which models a wiki page, and has an associated Acl, which is composed of AclEntrys
  • Attachment, which models the attachments added to a wiki page. Conceptually, an Attachment is a kind of Page, and thus Attachment extends Page, so it also has associated Acls, author, creation date, etc.
  • Session, which models the session of a user in the wiki.
  • Context, which models the current action (view, edit, attach, etc.) happening in a given moment.
    • an action in JSPWiki is modeled with a Command, and that's why Context extends Command.
    • as the Context is modelling the current action, you can access from here the associated HTTP request, Session and Page, variables that you set on this scope, the associated user, etc.
    • The ContextEnum is a simple placeholder for all the kinds of Command identifiers understood by JSPWiki.
    • Contexts are usually created at JSP level and then passed on to the java code.
  • Engine, which is responsible of keeping the wiki running. As opposed to Context, the Engine focuses more on the "static" side of the application: configuration properties, urls (global, rss, etc.), the associated ServletContext, etc.
    • Perhaps the most important method here is getManager(Class<?> cls ) which returns the requested "manager" (or engine part, following the engine metaphor). Most of these managers are not part of the public API, so if a given extension relies on them it may stop working on a new minor release.
    • There is only one instance of an Engine for a given wiki.

The default implementation for these abstractions are:

  • Page : org.apache.wiki.WikiPage
  • Attachment : org.apache.wiki.attachment.Attachment
  • Acl : org.apache.wiki.auth.acl.AclImpl
  • AclEntry : org.apache.wiki.auth.acl.AclEntryImpl
  • Session : org.apache.wiki.WikiSession
  • Context : org.apache.wiki.WikiContext
  • Engine : org.apache.wiki.WikiEngine
warning note that these default implementations should never be instantiated/accessed directly or, generally speaking, appear in an extension code. The concrete implementation of the public API used by JSPWiki is selected through an SPI, so it is not impossible that a JSPWiki installation may be running with a different implementation.

To obtain instances from the public API you may use:

  • the JSPWiki API (public or not); for example, an Engine can be obtained by using Context#getEngine().
  • the org.apache.wiki.api.spi.Wiki class, a DSL which exposes different SPIs to obtain said instances; for example a Context can be created using one of the Wiki.context().create(..) methods, or the Engine instance can be obtained by using one of the Wiki.engine().find( .. ) methods, etc.

Providing custom core API implementations#

It is easy to provide custom implementations for the core API:

  • For a custom Engine, provide an implementation of o.a.w.api.spi.EngineSPI, and set the jspwiki.provider.impl.engine property on the jspwiki-[custom].properties file with the fully qualified name of the implementation.
  • For a custom Context, provide an implementation of o.a.w.api.spi.ContextSPI, and set the jspwiki.provider.impl.context property on the jspwiki-[custom].properties file with the fully qualified name of the implementation.
  • For a custom Session, provide an implementation of o.a.w.api.spi.SessionSPI, and set the jspwiki.provider.impl.session property on the jspwiki-[custom].properties file with the fully qualified name of the implementation.
  • For custom Page or Attachment, provide an implementation of o.a.w.api.spi.ContentsSPI, and set the jspwiki.provider.impl.contents property on the jspwiki-[custom].properties file with the fully qualified name of the implementation.
  • For custom Acl or AclEntry, provide an implementation of o.a.w.api.spi.AclsSPI, and set the jspwiki.provider.impl.acls property on the jspwiki-[custom].properties file with the fully qualified name of the implementation.

Registering custom managers in the WikiEngine#

It is possible to register custom "Engine parts" (managers or components or however they end up being called):

  • The default Engine implementation will look on classpath for an ini/classmappings-extra.xml file, with the same structure as ini/classmappings.xml.
  • If found, will register each requestedClass with its correspondent mappedClass.
  • These custom managers must have a no-arg constructor.
  • If there's a need to perform some initialization tasks querying the Engine, the custom manager should implement o.a.w.api.engine.Initializable and perform those tasks there.
  • Custom managers then will be accessible through Engine#getManager( CustomComponent.class ).

Please also note that

  • The classes don't get registered in any particular order, as there's an iteration of the contents of a Map.
  • As with "built-in" managers, if there are more than one requestedClass entries with the same mappedClass, the last getting registered overwrites the other(s).
  • Access to the custom manager is controlled through its access modifiers (although the Engine must be able to "see" it).

Plugins#

It's compromised of the classes and interfaces in the org.apache.wiki.api.plugins package.

This part of the public API is covered in detail in the How to write a Plugin page.

Filters#

It's compromised of the classes and interfaces in the org.apache.wiki.api.filter package.

This part of the public API is covered in detail in the How to write a Filter page.

Page and Attachment providers#

It's compromised of the classes and interfaces in the org.apache.wiki.api.providers and org.apache.wiki.api.search package.

This part of the public API is covered in detail in the How to write a Page Provider page.

Testing#

Most, if not all, of the API you'll be working with will be interfaces, so the appropiate way for unit testing an extension is to mock them with a mocking library, like for example Mockito.

Said that, there's a maven artifact, not part of the public API, which holds JSPWiki end-to-end testing support classes. It should be added as a dependency following this configuration:

<dependency>
  <groupId>org.apache.jspwiki</groupId>
  <artifactId>jspwiki-main</artifactId>
  <type>test-jar</type>
  <version>LATEST_JSPWIKI</version>
  <scope>test</scope>
</dependency>

The most important class in this artifact is org.apache.wiki.TestEngine which is an Engine implementation suitable to be used while testing. It allows to mimic JSPWiki running behaviour and has a number of static build(..) methods which make really easy to have a TestEngine up and running:

package com.myorg.jspwiki.extensions;

import org.apache.wiki.TestEngine;
import org.apache.wiki.api.providers.PageProvider;
import org.apache.wiki.api.spi.Wiki;
import org.apache.wiki.pages.PageManager;
[...]

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
[...]

import static org.apache.wiki.TestEngine.with;

public class MyAwesomeTest {

    static TestEngine e  ngine = TestEngine.build( with( "jspwiki.usePageCache", "false" ),
	    			        					   with( "jspwiki.pageProvider", "WikiPageAdapterProvider" ),
	    			        					   with( "jspwiki.attachmentProvider", "WikiAttachmentAdapterProvider" ),
		    		        					   with( "jspwiki.pageProvider.adapter.impl", "com.myorg.jspwiki.extensions.AwesomePageProvider" ),
		    			        				   with( "jspwiki.attachmentProvider.adapter.impl", "com.myorg.jspwiki.extensions.AwesomeAttachmentProvider" ) );

    @Test
    public void testPageProvider() throws Exception {
        final PageProvider pageProvider = engine.getManager( PageManager.class ).getProvider();
        Assertions.assertEquals( "com.myorg.jspwiki.extensions.AwesomePageProvider", pageProvider.getProviderInfo() );
        
        pageProvider.putPageText( Wiki.contents.page( engine, "page4" ), "some crazy text here" );
        Assertions.assertTrue( pageProvider.pageExists( "page4" ) );
    }
[...]	    							  
}
info note how TestEngine is defined with an static qualifier. This ensures the Engine is only initialized once throughout all the tests' executions.

My/An extension is not using the public API, what do I do?#

If you are the extension's author, please consider a new release upgrading your extension to use the public API.

If you are using an extension that does not use the public API, consider contacting the extension author so he/she considers a new release. In the meantime, JSPWiki includes an specific module, org.apache.jspwiki:jspwiki-210-adapters which provides backwards compatibility with extensions not using the public API.

Plugins and filters not using the public API don't need anything special, they should still keep working.

Page and attachment providers should modify its jspwiki-[custom].properties:

  • Page providers
    • jspwiki.pageProvider should be set to WikiPageAdapterProvider and then jspwiki.pageProvider.adapter.impl to the actual page provider.
  • Attachment providers
    • jspwiki.attachmentProvider should be set to WikiAttachmentAdapterProvider and then jspwiki.attachmentProvider.adapter.impl to the actual attachment provider.
warning note this module is subject to disappear at some point in the future, on a minor release, so extension authors should consider migrating to the public API.

warning provided compatibility is limited to the execution of extensions not using the public API. If, for whatever reason, an extension uses part of JSPWiki that has disappeared due to minor release versioning, then the extension will be loaded but it is bound to cause an exception when reaching that piece of code (this of course applies to extensions using the public API anyway).


Category.Documentation