This wiki will be reformatted shortly to organize the concepts more logically. Doc on the latest features such as HttpServlets, REST extensions, and the ability to configure a plugin with Spring will be added as well. Though these new features are missing from this doc, they are currently demonstrated in the EchoPlugin mentioned below.

A sample plugin is now available, see Echo Plugin - a sample plugin for the BIServer. It is useful for demonstrating the features of the plugin architecture as well as providing a starting point for your own plugin.

Introduction

The plugin framework provides developers with the ability to extend the Pentaho BI Server in various ways. I like to think in terms of function, i.e. what can I do with a plugin to the BIServer?  These are the plugin "features":

Plugin Developer Guide

In this section, we'll go into more detail about the kinds of things you can do with a plugin and how to know which of the many extension points you should be concerned with, given your specific need. Then we will point you to the salient sections in the plugin developer reference

Service feature

The Pentaho BIServer plugin framework provides a plugin contributor with what I think is kind-of a strange and perhaps unique ability. That is, you can write a plugin that extends the core services offered in the BIServer. It's a bit of an atypical use case in the enterprise world to be extending the back-end capabilities of a product with your own services, but neverthelesss, we make this possible. It is perhaps due to the complex nature of BI Solutions, and the inability for Pentaho to predict all the possible points of integration or uses of our product that we provide a way to extend our service offerings. If you are a developer, you may be thinking "why is he making such a big deal about service extensions, isn't this just the same as writing a new Java Servlet which is nothing special in J2EE land?". The answer to that is, yes, it's just about the same thing, the difference is we allow you to insert a servlet-like Java class and extend an existing web application without modifying core web-app files, such as web.xml. So, enough talking about what a service plugin is, you probably want to know how you do it.  Well, you have 2 choices:

  1. see Defining a web service (as of 3.5), or if that option is not flexible enough for you, then
  2. see #Defining a Content Generator which will provide you pretty much the full power of the Java HttpServlet

Visualize feature

..more to come, but for the mean-time, see #Defining a Content Generator and Defining Static Content (as of 3.5)

Register feature

..more to come, but for the mean-time, see #Defining a Content Type

Integrate feature

If you are privileged enough to develop against our current trunk codeline which is slated for the "Sugar" release, then see Creating Actions, the lightweight alternative to Components for a good overview. Otherwise, follow the comments about using PojoComponents in Defining an Action (a component executable in an action sequence) (as of 3.0 or Sugar).

Tweak feature

See #Defining a Menu Item and #Defining an Overlay for ways to extend the Pentaho User Console (PUC)

Plugin Developer Reference

Creating a Plugin project

The subfloor Ant-based build framework is used internally by Pentaho to create plugin projects. See How to Create a New Project for information on how to run a script that will auto-generate a skeleton project for you. Typically you will want to run the script with the following options:

groovy ~/bin/projcreator.groovy myproject -i -o acme -p -d /path/to/biserver-ee

or if you wish to not employ IVY dependency management, then run

groovy ~/bin/projcreator.groovy myproject -o acme -p -d /path/to/biserver-ee

Plugin Composition

Each plugin is contained in a folder in the system folder. The standard contents of a plugin folder are a plugin.xml file and a 'lib' folder.

The 'lib' Folder

The plugin folder can contain a folder called 'lib' in which any binary (JAR) files needed by the plugin can be placed. This means that any libraries required by the plugin do not have to be deployed to the server. You can move plugin libraries out of the plugin lib folder and place them in one of the servers lib folders as needed.

The 'plugin.xml' File

The plugin.xml file defines the extension points used by the plugin. The plugin.xml file of the sample plugin looks like this (the content has been scraped out so you can see the structure of the file):

<?xml version="1.0" encoding="UTF-8"?>

<plugin title="My Plugin">
<!-- this is not a functional section of plugin.xml, it is here to illustrate the
     structure of the various elements
    <lifecycle-listener/>

    <static-paths/>

    <overlays>
        <overlay/>
    </overlays>

    <perspective/>

    <content-types>
        <content-type/>
    </content-types>

    <content-generator/>

    <bean/>
-->
</plugin>

Each of the various elements of the plugin.xml file are described in the following section on extension points.

Plugin Attributes

Extension Points Reference

A BIServer Plugin provides the following extension points in particular:

  1. Overlays
  2. Content Types
  3. Content Generators
  4. BI Components (as of 3.0)
  5. Web Services (as of 3.5)
  6. GWT RPC Services (as of 3.5)

Defining a Lifecycle Listener (as of 3.0)

You will want to define a lifecycle-listener class in your plugin.xml if you want your plugin to respond to plugin lifecycle events. These events are:

  1. init - fired just prior to the plugin being registered with the BI Server
  2. loaded - fired after the plugin has been registered with the BI Server
  3. unLoaded - fired when the plugin is about to be unregistered

In order to hook into these events, you will need to create an implementation of IPluginLifecycleListener and configure it in your plugin.xml like this:

<lifecycle-listener class="my.plugin.MyLifecycleLister"/>

Defining External Resources (as of 4.0)

Your plugin can include Javascript and CSS files dynamically into content served up from the Pentaho BI Server. For example:

  <external-resources>
    <file context="mantle">content/dashboards/resources/gwt/chartDesigner/chartDesigner.nocache.js</file>
  </external-resources>

To have the above referenced js file included in the content pages, the page must have a reference to the webcontext.js file, with a parameter of context and
the value matching the context value defined in the external-resource node attribute. The following example demonstrates.

 <script type=”text/javascript” src=”webContext.js?context=mantle”></script>

The value add here is the ability to add as many .js and .css files that you may need to any context available in the Pentaho BI Server to provide your custom functionality to any new or existing plugin.

Defining Static Content (as of 3.5)

Your plugin can serve static content to a request by setting up static path mappings in your plugin.xml.  For example:

<static-paths>
     <static-path url="/reporting/resources" localFolder="resources"/>
</static-paths>  

 You can define as many <static-path/> elements as you wish.  Each element defines:

Note: prior to 3.6, you may experience the BISERVER-4280. Since 3.6 there is logic to correctly match a request path to a plugin resource. Prior to this change the logic was sloppy regarding plugins that "look like" others in that they had the same first few characters in their servicing urls.

A Note on Caching of static content

You can control the caching of these static resources by setting 2 elements in you settings.xml (the file read by the plugin system to control various behaviors of your plugin).  The two elements are:

Defining an Overlay

An overlay is a bit of XUL (XML) whose purpose is to modify the content and/or layout of an existing XUL file. For more information on XUL overlays, see the Mozilla doc Xul Overlays.

The Pentaho User Console currently supports XUL overlays in the following places:

Defining a Content Type

By defining a new content type, you are making a particular file type visible to the solution repository. Additionally, a content-type entry in plugin.xml allows you to specify what user operations are available for this content type which will be accessible in the Pentaho User Console.

An example:

<content-type type="myc" mime-type="text/html">
<title>My Content</title>
<description>MyContent File</description>
<icon-url>content/myplugin/images/mycontentFileType.png</icon-url>
<meta-provider>my.plugin.MyContentMetaProvider</meta-provider>
<operations>
    <operation>
    <id>EDIT</id>
    <command>content/myplugin?solution={solution}&amp;path={path}&amp;action={name}</command>
    </operation>
</operations>
</content-type>

The parts of the content-type entry are:

  1. statically by mapping it in a static-paths section in your plugin.xml
    In this case, you should set icon-url to content/<static-path url>/<your-icon>, using the example given in the Static Content section above, the icon-url would be content/reporting/resources/smiley.png
  2. dynamically from a content generator
    If you are serving the icon from a content generator, your icon-url will be content/<content-generator id>/<your-icon> (See the content generator section below for details on content generator id attribute).

Defining a Content Generator

A content generator is a Java class file that serves up custom content for users. Think of it like a little embedded Java Servlet. There are no restrictions on the kind of content that may be generated.

Examples of content generator definitions can be found in the sample plugin described above and also in biserver-ce/pentaho-solutions/system/ui/plugin.xml.

An example:
As of 3.5, you can define your content generator in a single element like this:

<content-generator
  id="myplugin"
  title="My Content Generator"
  type="myContentType"
  class="my.plugin.MyPluginContentGenerator"/>

..otherwise, this is the method supported in 2.1:

<content-generator id="myplugin" type="myContentType">
    <title>My Plugin</title>
    <classname>my.plugin.MyPluginContentGenerator</classname>
</content-generator>

The parts of the content-generator entry are:

Defining an HTTP File Uploader (as of 3.6)

<bean id="myPluginUploader" class="my.plugin.MyFileUploader"/>

In some cases, your plugin may require users to upload files to the BIServer that your plugin may need to operate on.  To this end, we provide the IUploadFileServletPlugin Java interface (in the bi-platform-web-servlet source project).  Implement one of these and register it with the BIServer by defining it as a "bean" in the plugin.xml as show above.  The plugin framework will route any HTTP requests to http://<machine>/pentaho/upload/<myuploaderid> to the corresponding uploader bean, using the bean id as the last element of the URL, e.g. http://<machine>/pentaho/upload/myPluginUploader

Defining an Action (a component executable in an action sequence) (as of 3.0 or Sugar)

#actions
To define a new BI Component to be used in action sequences, you need to configure a plugin bean in your plugin.xml like this (plugin beans have other uses besides just serving up Actions).

<bean id="MyAction" class="my.plugin.MyAction"/>

If your bean is an Action, just reference the bean id in the component-name element of your action definition. If your plugin bean is a (now deprecated) Pojo Component definition, then you will reference the bean id (not the fully qualified class) in the class input to the Pojo component.

Pojo Components, which were supported in 3.0, will be deprecated as of the Sugar release, they have been succeeded by Actions, which are very similar in form but more robust. You can think of Actions as POJOv2. Look for more documentation here. For a working example, see the sample Echo plugin, which now provides an Action (EchoAction) as a plugin-in.

Plugin Scheduling and Background Execution

Please read the article Plugin Scheduling and Background Execution

Defining a web service (as of 3.5)

You can expose a POJO in your plugin as various types of services with just a little XML markup in your plugin.xml.  The plugin architecture currently allows you to promote your class as either an XML service (WSDL-based SOAP or raw xml-over-http) or a GWT RPC service.  Other service types are planned, such as JSON.  Your class need not extend or implement anything special, the service endpoint configuration systems will inspect your object through reflection and expose your public methods as service endpoints.

Here is an example of how to expose the EchoService class as an xml web service.   Behind the scenes the Axis libraries are using introspection on your Java class to make service endpoints available for your public methods:

<webservice
    id="echoService"
    type="xml"
    title="Echo Service"
    description="A sample webservice that echos an input"
    class="org.pentaho.samples.EchoService"/>

More on GWT RPC service (as of 3.5)

Let's say your plugin is comprised of a GWT application. If your application is more than just client code, you are normally required to create a remote service class to handle the back-end work. This service class will have to extend GWT's base servlet, RemoteServiceServlet. Your new servlet must then be added to the web application deployment descriptor (web.xml) so it will be able to server http POST requests.

One of the promises of BI Server plugins is that they will not require any modification to system configuration, so touching web.xml is out of the question, not to mention placing the jar that hosts your new servlet into WEB-INF/lib.

To solve this problem, the plugin framework now allow you to register a class in your plugin as a GWT RPC endpoint. The only thing you have to do is:
a) Instead of extending RemoteServiceServlet, just have your service class implement the remote interface you have defined for the service. The remote interface is shared by the client and server and declares the contract between the two parts.
b) register your service class as a plugin "webservice"
c) Have your client code make it's requests to <webapp context>/gwtrpc/myService
gwtrpc is the name of the proxy servlet deployed in the BI Server, so that is required. "myService" is the id of your service in your plugin.xml.

An example of how to define your GWT service (for breakdown of the webservice element, see above):

<webservice id="myService" class="org.pentaho.samples.myplugin.server.MyServiceImpl"/>

A quick word on how this works - there is a GWT RPC proxy servlet deployed in the BI Server that will decide the correct target object to which it should route the payload of the request (method invocations). There is also special logic in that proxy servlet (GwtRpcProxyServlet) that can locate serialization white-list files (*.gwt.rpc) generated during your GWT compile phase.

 There is an example plugin called GWT Echo that shows how you would construct a client/server GWT application as a plugin.  See the Plugin Depot for the source location and a latest build of the project.  If you choose to build it yourself, see the README file at the root of the project source.

The 'plugin.spring.xml' File

Each plugin can optionally have a Spring xml file for it's internal configuration as well as for exposing beans to the PentahoSystem. see Pentaho ObjectFactory and Spring Enhancements

Defining beans to handle ContentType actions

If you've defined a content-type in your plugin and configured actions for it, you can define the bean to respond to these actions (or perspectives as they're sometimes called) in the plugin.spring.xml

For a content-type "xcontent" setup as follows:

<content-types>
    <content-type type="xcontent" mime-type="text/html">
      ...
      <operations>
        <operation>
            <id>RUN</id>
            <perspective>editor</perspective>
        </operation>
        <operation>
            <id>PARAMETER</id>
            <perspective>parameter</perspective>
        </operation>

You can define a bean to respond to these in plugin.spring.xml as follows:

<bean id="xcontent.editor" class="com.foo.MyContentGenerator" scope="prototype" />
<bean id="xcontent.parameter" class="com.foo.MyParameterContentGenerator" scope="prototype" />

JAX-WS services

JAX-WS webservices can be declared within the plugin.spring.xml. For information on how to write a JAX-WS service consult the Oracle documentation: http://docs.oracle.com/javaee/6/tutorial/doc/bnayl.html

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ws="http://jax-ws.dev.java.net/spring/core"
       xmlns:wss="http://jax-ws.dev.java.net/spring/servlet"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
                           http://jax-ws.dev.java.net/spring/core http://jax-ws.dev.java.net/spring/core.xsd
                           http://jax-ws.dev.java.net/spring/servlet http://jax-ws.dev.java.net/spring/servlet.xsd">

  <context:annotation-config/>

  <!-- JAX-WS bindings -->
  <wss:binding url="/webservices/myServiceEndPoint">
    <wss:service>
      <ws:service impl="com.foo.MyService" />
    </wss:service>
  </wss:binding>

REST Services

Please see the article How to create and register a new REST service from a plugin

Defining a plugin permission (action based security)

Please see the article How to register a new action based security (ABS) permission from a plugin

PUC Perspectives

Plugins can supply perspectives to be added to PUC. The documentation for this feature is located here: Providing a PUC Perspective from Plugins

Plugin System Overview

Here is a brief description of the main classes of the plugin architecture. (All classes reside in org.pentaho.platform.plugin.services.pluginmgr):

PlatformPlugin
The default implementation of IPlatformPlugin, this class groups together extensions into a logical collection. It is the main vehicle for the specification of a plugin.

PluginAdapter
This class causes the plugin settings to be read when the system starts and re-read when the Plugin Manager option on the Admin page is used.

PluginManager
The default implementation of IPluginManager, this class provides a plugin registry as well as many convenience methods from extracting plugin extensions.

SystemPathXmlPluginProvider
The default implementation of IPluginProvider, this class traverses the sub-folders of the system folder looking for plugin.xml files creates serves up IPlatformPlugins.

PluginResourceLoader
The default implementation of IPluginResourceLoader, this class provides a convenient abstraction layer for obtaining resources related to your plugin from within your plugin without having to know anything about the outside world in which your plugin lives.

As with most of the Pentaho API, you can get a reference to an implementation object (such as the ones metioned above) by using the service-locator-like PentahoSystem, e.g. PentahoSystem.get(IPluginResourceLoader.class, session). This call will return the designated implemention for the IPluginResourceLoader interface. In the sample Pentaho solution, the designation is declared in pentahoObjects.spring.xml, so you can always replace the default impls with your own.

Plugin Development FAQ

Is there a way to load resources from within my plugin without hard-coding paths?

Yes, as a matter of fact, we discourage hard-coding paths to resources for the reason that neither solutions paths nor the installation path of your plugin are constant. Let's say your plugin has a class Foo.java that needs to read in a properties file resources/config/foo.properties which is also located within your plugin. The recommended way to do this is as follows:

public class Foo {

  public static Properties loadFooProperties() {
    IPluginResourceLoader resLoader = PentahoSystem.get(IPluginResourceLoader.class, null);
    InputStream in = resLoader.getResourceAsStream(Foo.class, "resources/config/foo.properties");
    Properties props = new Properties();
    props.load(in);
    return props;
  }
}

The PluginResourceLoader is your friend here. Let's break this down a little: first, we are getting a handle to the PluginResourceLoader (we pass null for the session since we will always get the same instance regardless of the current session - it shoud be defined as a singleton). Next we use PluginResourceLoader to access the properties file. Note that we are passing in a Class. The reason for this is the PluginResourceLoader needs to determine which plugin to search to find the resource in question. It determines the plugin by finding which plugin loaded the class that you provide, so make sure you provide a Class that lives exclusively within your plugin. IPluginResourceLoader has many other methods that support retrieval of resources in other formats other than InputStream can also use it to load resources from within jar files in your plugin's lib directory.

Feedback

Please post any feedback you have to the BI server forum: http://forums.pentaho.org/forumdisplay.php?f=73