Developing Plugins

Doc Update Coming Soon

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.

Sample Plugin Available

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":

  • Service another application (through web-services)
  • Visualize data in a new way (generate new content and send it to the browser)
  • Register new control files with the engine and with the user console (allows you to store configuration and parameters that control the rendering of a Visualize extension)
  • Integrate the BIServer with a third-party engine, application, or service of your choosing during a scheduled execution or an on-demand user action (i.e. inserting a brand new step in an action sequence)
  • Tweak the Pentaho User Console (add new menu options or toolbar buttons, new Perspectives and Admin console screens)

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

  • title - the human readable title of the plugin
  • name - the unique id of the plugin within the system
  • loader - DEFAULT or OVERRIDING - this tells the system whether to use the default class loader or the overriding classloader.  The overriding classloader allows you to override jar files that are included in the system within your plugin's lib folder.  Due to the complexity of the overriding classloader, we do not recommend this approach except for special circumstances.

Extension Points Reference

A BIServer Plugin provides the following extension points in particular:

  1. Overlays
    • Customize existing page content
    • Customize Menu System (add / remove items)
  2. Content Types
    • Define new types of content in the solution repository, e.g. 'xaction'
    • Define operations for these new types of content
  3. Content Generators
    • Serve new UI pages for any general purpose or to render something about your new content types
  4. BI Components (as of 3.0)
    • Introduce a new BI Component to use in an action sequence
  5. Web Services (as of 3.5)
    • Expose a Plain Old Java Object (POJO) class in your plugin as a genuine web service
  6. GWT RPC Services (as of 3.5)
    • Your plugin's GWT client code can talk to a POJO inside your plugin without having to be a servlet deployed in the web application

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:

  • url - the url "address" to a particular static content folder within your plugin.  To understand how the url is used, consider the the example above; if a request came in for the resource http://<machine>/pentaho/content/reporting/resources/smiley.png, the BI Server would decompose the request to find the relative url.  The relative url is the entire request url minus the base url (http://<machine>/pentaho/content) which leaves us with the relative url "/reporting/resources/smiley.png".  The BI Server then searches all plugins to see if any plugins can serve content from the directory portion of this url "/reporting/resources".
  • localFolder - the relative path to the actual directory that will serve the static content.  This must be a path to a directory relative to the root of your plugin.  If the BI Server determines that your plugin can serve the requested content, then this folder will be searched for the requested resource.  Again, following the example above, a BI Server request http://<machine>/pentaho/content/reporting/resources/smiley.png will essentially lead to a lookup of smiley.png in your plugin's "resources" folder.

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:

  • <cache/> - can be 'true' or 'false'.  Settings this to true enables server-side caching of the static content
  • <max-age/> - directly corresponds to the HTTP response header max-age property, i.e. the number of seconds this content should live before being requested again.
    Below is an example settings.xml file. Note: this file must live at the root of your plugin, and the cache and max-age must be children of a <settings> element.
    <?xml version="1.0" encoding="UTF-8"?>
    <settings>
    	<cache>false</cache>
    	<max-age>3600</max-age>
    </settings>
    

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:

  • Launch page (launch.jsp)

    Note: At this time, the overlay is parsed in the launch.jsp, and only adding a fourth button is supported.
    The id of the overlay needs to be "launch"!!

    <overlay id="launch" resourcebundle="content/plugin/resources/plugin.properties">
    		<button id="patButton" image="content/plugin/resources/menu-icon.gif" command="openURL('Plugin','Plugin','content/plugin')" label="${plugin.tooltip}" />
    
  • PUC Menu System
    The Menu system in PUC has been transitioned to XUL, enabling the use of overlays. The following example removes a pre-defined menu item and adds a new one
    <overlays>
      <overlay id="startup.myPlugin" resourcebundle="content/my-plugin/resources/messages" priority="1">
        <!-- Add one -->
        <menubar id="newmenu"> <!-- target menu -->
          <!-- All children will be added to the target -->
          <menuitem id="myMenuItem1" label="${lblKey1}" command="COMMAND" />
          <menuitem id="myMenuItem2" label="${lblKey2}" command="COMMAND" insertbefore|insertafter="otherId" />
          <menuitem id="myMenuItem3" label="${lblKey3}" command="COMMAND" pos="2" />
        </menubar>
        <!-- remove one -->
        <menuitem id="idToRemove" removeelement="true"/>
      </overlay>
    </overlays>
    

    Where COMMAND is one of the following options:

    • mantleXulHandler.openUrl('TABNAME','TABNAME','URL')
    • mantleXulHandler.executeMantleCommand("COMMAND_NAME")
    • mantleXulHandler.executeMantleCall("some.javaScript()")

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:

  • type: what identifies this content type - a file extension
  • mime-type: the mime-type represented by the content
  • title: a human-readable interpretation of the file extension, aka type. Note: title is not currently rendered anywhere in the user console
  • description: short description of this content type.   Note: description is not currently rendered anywhere in the user console
  • icon-url: an image that represents this content type in the solution browser.  icon-url can either be an absolute url such as http://myfreeicons/smiley.png or a relative url.  An icon with a relative url can be served by your plugin in one of two ways:
  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).
  • meta-provider (as of 3.5): The class that will be used to return meta information about specific files of this type that are found in the solution repository to the user console.  Your implementing class should implement org.pentaho.platform.api.engine.ISolutionFileMetaProvider.  Due to backwards compatibility, we recommend that your implementation class extend org.pentaho.platform.api.engine.SolutionFileMetaAdapter to hide the deprecated behavior that you need not concern yourself with.  FYI: The meta-provider element supercedes the fileinfo-generated element in the content-generator section.
  • operations: defines user operations for this content type. These will appear in contextual menus when this type of content is in view.
    • operation - id: the name of this operation that appears in in the contextual menu.  RUN, NEWWINDOW, SCHEDULE_NEW and EDIT are the only supported operations at this time.
    • operation - command: a URL to which a GET request will be issued when the operation is invoked (handled by a content generator)

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:

  • id: A unique id for the object. The id is important because it is the lookup key by which user console requests are routed to your content generator. Using our example above, the user console request http://<machine>/pentaho/content/myplugin will be handled by MyPluginContentGenerator (url based on the default configuration of web.xml)
  • type: A type identifier. This can be used to register multiple content generators for a single type of content. For example multiple content generators could specify a type of 'xaction'. This will allow (eventually) the user to choose from a list of options when they select a file in the solution repository. Supporting multiple generators per type is not yet fully operational.
  • class (or classname): The class name of the content generator. The specified class must implement org.pentaho.platform.api.engine.IContentGenerator or subclass org.pentaho.platform.engine.services.solution.BaseContentGenerator (this is the easier option). For an example of a content generator see:
    • org.pentaho.platform.engine.services.solution.ActionSequenceContentGenerator in the bi-platform-engine-services project
    • org.pentaho.platform.uifoundation.contentgen.AdminContentGenerator in the bi-platform-ui-foundation project
    • org.pentaho.platform.uifoundation.contentgen.NavigationContentGenerator in the bi-platform-ui-foundation project
  • title: a human-readable name for this content generator.  In future releases title may be presented to the user in the user console to disambiguate when there are multiple content generators that service a particular content type.
  • fileinfo-classname (deprecated as of 3.5): You can supply some metadata about your new content type to the navigation user interface by implementing org.pentaho.platform.api.engine.ISolutionFileMetaProvider and setting fileinfo-classname to the name of you implementing class.  Note: if you are developing against a 3.5 or later, please use the meta-provider element within the content-type section of the plugin.xml instead of defining the fileinfo-classname here. Support for fileinfo-classname will go away within a few releases.

    Warning

    Prior to 3.6, do not give the same string for id and type in a content generator. The plugin manager will not be able to uniquely identify your content generator.  This limitation was removed in 3.6.

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.

Actions superceed PojoComponents in Sugar

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 /wiki/spaces/PMOPEN/pages/1249389595

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"/>
  • id (optional) the id of the service.  Id (along with the service type) is used to uniquely identify a service.  Id is the key by which you make requests to the service.  The service URL will essentially be <webapp context>/<dispatch context>/<serviceId>, where <dispatch context> = "content/ws-run" to execute xml web services, and "gwt-rpc" for executing gwt services.  If id is not set, the simple name of the class is used, referring to the example above, it would be set to "EchoService".
  • type (required) the type of service to expose.  More than one type may be listed by comma-delimiter, e.g. type="xml,gwt".  Options currently are either "xml" or "gwt".  Setting type to "xml" will employ Apache Axis to expose your class as a SOAP or raw xml over http (depending on the Axis configuration supplied in the BI Server).  Setting type to "gwt" will allow the GwtRpcPluginProxyServlet to dispatch GWT RPC requests to your bean.  (More info below in this)
  • title (optional) a natural language name for the service used when services are listed visually to the user, such as in PUC's Tools->Web Services menu option.
  • description (optional) a description of the service used when services are listed visually to the user, such as in PUC's Tools->Web Services menu option.
  • class (required) the POJO class you wish to expose as a service

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.

Accessing Pentaho API Implementations

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