Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

There are four ways of creating a BI Component: create a BI Component from scratch, convert an existing class into a BI Component, create a subclass of ComponentBase (org.pentaho.plugin.ComponentBase), or create a subclass of SimpleComponent (org.pentaho.plugin.core.SimpleComponent).

To become a BI Component, a Java object must implement the org.pentaho.core.component.IComponent interface. The IComponent interface extends two other interfaces: org.pentaho.core.audit.IAuditable and org.pentaho.util.logging.ILogger. Pentaho provides classes that implement these interfaces.

The hierarchy of the Pentaho classes looks like this:
org.pentaho.core.system.PentahoBase (implements ILogger and Serializable)
--> org.pentaho.core.system.PentahoMessenger
--> org.pentaho.plugin.ComponentBase (implements IComponent and IAuditable)
--> org.pentaho.plugin.core.SimpleComponent
--> other Pentaho-provided BI Components

You can create your BI Components in whatever package you like whether they are subclasses of Pentaho classes or not. There are no required methods that will be inaccessible if you use a non-Pentaho package.

Extend SimpleComponent

This is the easiest way to create a new component and the recommended place to start.

1.Create a new Java class that is a subclass of (extends) org.pentaho.plugin.core.SimpleComponent.

2.Implement an executeAction() method and a getLogger() method. See 'Component Methods' below for a description of these methods.

Extend ComponentBase

If your component needs to validate its inputs and/or system settings or needs to perform any initialization and/or cleanup, you should extend org.pentaho.plugin.ComponentBase.

1.Create a new Java class that is a subclass of (extends) org.pentaho.plugin.ComponentBase.

2.Add code to the init(), validateSystemSettings(), validateAction(), executeAction(), done(), and getLogger() methods as appropriate. See 'Component Methods' below for a description of these methods.

Convert a Java Object into a BI Component

If you have an existing object that you wish to convert into a BI Component there are two options.
Create a new object that subclasses your existing object and implements the required interfaces
Change your object to implement the required interfaces

Which of these options is the best for your particular object will depend on your specific circumstances; but, we recommend creating a subclass if only to keep your source file size manageable.

If you do need to use this approach most of the code you need to create your new class can be found in these classes: org.pentaho.core.system.PentahoBase, org.pentaho.core.system.PentahoMessenger, and org.pentaho.plugin.ComponentBase. To make this process easier we have created a class, org.pentaho.plugin.ComponentSubclassExample, that contains the code you need.

To convert a Java class to a BI Component:

1.Copy the imports, member variables, and methods from org.pentaho.plugin.ComponentSubclassExample into your Java class.

2.Add code to the init(), validateSystemSettings(), validateAction(), executeAction(), done(), and getLogger() methods as appropriate. See 'Component Methods' below for a description of these methods.

Components from Scratch

In order to create a component from scratch a new class that implements the interface org.pentaho.core.component.IComponent must be created. To implement all three interfaces requires about 30 methods to be implemented. It is not recommended to use this option and there should be no need to do this given the options above.

Component Methods

If using one of the three recommended methods for creating new components, the methods below are the BI Component methods that need to be customized to provide your needed functionality. The methods are listed in the order that they are typically called during normal processing. Depending on the option used to create your component, not all of these methods are required. See above sections for more details.

getLogger()

This method creates a Log object that will be use to log messages from your component. It has to be created by your component (instead of a superclass) so that the log correctly records the class name of your object.

This method is implemented with a single line.

public Log getLogger() {
    return LogFactory.getLog(this.class);
}

validateSystemSettings()

This method allows your component to verify that any its required system settings it needs in order to operate are set correctly. For examples, the Kettle component (org.pentaho.plugin.kettle.KettleComponent) checks it system settings to see if it is configured for a file-based or RBDMS-based repository, and the email component (org.pentaho.plugin.email.EmailComponent) uses its system settings to connect to your email server.

System-wide settings for components should be stored in XML documents in the 'system' folder in the Pentaho solution root folder. System settings are ones that will be the same for every instance of your component and that won't change while the server or application is running (unless manually changed by an administrator).

In the system folder you will see subdirectories such as 'kettle', 'quartz', and 'smtp-email'. If your component needs system settings you can create a directory to store your settings in and place an XML file in the directory. The XML file can be named anything that you like. Within the XML document you can arrange the settings in any format that you like. The settings file for the email component provides a good example of a settings file. Within the root solution folder the path to the email settings file is 'system/smtp-email/email_config.xml'.

<email-smtp>

   <!-- The values within <properties> are passed directly to the JavaMail API.
   For a list of valid properties see   
   http://java.sun.com/products/javamail/javadocs/index.html -->
   <properties>
      <!-- This is the address of your SMTP email server for sending email. e.g.              
           smtp.pentaho.org -->
      <mail.smtp.host></mail.smtp.host>

      <!-- This is the port of your SMTP email server. Usually this is 25.
           For GMail this is 587 -->   
      <mail.smtp.port>25</mail.smtp.port>
       
      <!-- The transport for accessing the email server. Usually this is smtp.
           For GMail this is smtps -->
      <mail.transport.protocol>smtp</mail.transport.protocol>
       
      <!- Usually this is 'false'. For GMail it is 'true' ->
      <mail.smtp.starttls.enable>false</mail.smtp.starttls.enable>
       
      <!- Set to true if the email server requires the sender to authenticate ->
      <mail.smtp.auth>true</mail.smtp.auth>
       
      <!-- This is true if the email server requires an SSL connection.
           Usually 'false'. For GMail this is 'true' -->
      <mail.smtp.ssl>false</mail.smtp.ssl>

      <!- Output debug information from the JavaMail API ->
      <mail.debug>false</mail.debug>
   </properties>

   <!-- This is the default 'from' address that emails from the Pentaho BI Suite
        will appear to come from e.g. joe.pentaho@pentaho.org -->
   <mail.from.default>joe.pentaho@pentaho.org</mail.from.default>

   <!-- This is the user id used to connect to the email server for sending email
        It is only required if email-authenticate is set to true
        This is never sent or shown to anyone -->
   <mail.userid></mail.userid>
   
   <!-- This is the password used to connect to the email server for sending email
        It is only required if email-authenticate is set to true
        This is never sent or shown to anyone -->
   <mail.password></mail.password>

</email-smtp>
 
The email component accesses these settings by calling PentahoSystem.getSystemSetting(). For example:

String mailhost = PentahoSystem.getSystemSetting("smtp-email/email_config.xml",
    "mail.smtp.host", null);

The getSystemSetting() method finds an entry in a system setting file and returns it to the component. The component does not need to know where the system settings files are located. See the 'Utility' section below for more information on the method PentahoSystem.getSystemSetting().

validateAction()

This method is called to give your component the chance to verify that it has the inputs and resources that it needs to complete successfully. If you return 'false' from this method execution of the action sequence will terminate.

This method should only be used to check that the inputs are available it should not check the values of inputs because they are not guaranteed to be available at this point during execution. If your component needs resources such as a template file referred to in the action sequence as "new-employee-greeting", you can check that the resource is available by calling isDefinedResource("new-employee-greeting").

If your component needs an input called 'employee-id', you can check that it has been defined in the action sequence by calling isDefinedInput("employee-id").

If your component needs an output called "employee-greeting-email-text", you can check that it has been defined in the action sequence by calling isDefinedOutput("employee-greeting-email-text").

Your validateAction() method would now look like this

public boolean validateAction() {
    if (!isDefinedResource("new-employee-greeting"))

Unknown macro: {        error( "A template called 'new-employee-greeting' has not been defined" );        return false;    }

    if (!isDefinedInput("employee-id"))

Unknown macro: {        error( "An employee id is needed" );        return false;    }

    if (!isDefinedOutput("employee-greeting-email-text") )

Unknown macro: {        error("An output called 'employee-greeting-email-text' has not been defined" );        return false;    }

    return true;
}

Notice that we are only checking to see if the input has been defined and we are not trying to get the input value.

The validateAction() of the EmailComponent looks like this:

public boolean validateAction() {
    // make sure that we can get a 'to' email address
    if (!isDefinedInput("to"))

Unknown macro: {        error(Messages.getErrorString("Email.ERROR_0001_TO_NOT_DEFINED",              getActionName()));        return false;    }

    // make sure that we can get a subject for the email
    if (!isDefinedInput("subject"))

Unknown macro: {        error(Messages.getErrorString("Email.ERROR_0002_SUBJECT_NOT_DEFINED",              getActionName()));        return false;    }

    // make sure that we have either a plain text or html message for the email
    if (!isDefinedInput("message-plain") && !isDefinedInput("message-html"))

Unknown macro: {        error(Messages.getErrorString("Email.ERROR_0003_BODY_NOT_DEFINED",              getActionName()));        return false;    }

    return true;
}

Notice that this method does not check for the optional parameters such as 'cc', 'bcc', or email attachments but only checks for the minimum parameters that are required for the component to execute successfully.

You do not have to call for each individual resource, input, or output. If you prefer, you can work with java.util.Set objects that contain the names of the available items. These sets can be obtained by calling getResourceNames(), getInputNames(), and getOutputNames(). For example

Set inputNames = getInputNames();
if( !inputNames.contains( "param1" ) && !inputNames.contains( "param2" ) ) {
    error( "I need both param1 and param2" );
    return false;
}

init()

This method is called to allow the component to perform any initialization operations that are required before the executeAction() is called.

executeAction()

This method is called to cause the component to perform its function. Within this method you can call other internal API methods to get the values of inputs, to get resources, to ask users for parameters, and to get output streams.

Typically, during the execute method a component will:
1.Gather input values. These might be parameters or component settings. If the input values are not complete, the component can stop executing or prompt the user for additional information.

2.Gather resources. These might be templates or definition files.

3.Get an output pipe of some kind (e.g. an output stream).

4.Create output contents.

5.Return the status of the execution.

See 'Internal API' below for descriptions of the methods available for the component to use.

We will use the executeAction() of the PrintComponent (org.pentaho.plugin.print.PrintComponent) as an example (minor modifications have been made for clarity).
 
protected boolean executeAction() {
    String printFileName = null;
    IActionResource printFileResource = null;

    // see if we are printing a file
    if (isDefinedInput("print-file"))

Unknown macro: {         // get the name of the file to print        printFileName = getInputStringValue("print-file");    }

    InputStream inStream = null;
    // Get the name of the printer to use
    if (isDefinedInput("printer-name"))

Unknown macro: {        printerName = getInputStringValue("printer-name");    }

    // try to find the requested printer
    PrintService printer = getPrinterInternal(printerName);
    if (printer == null) {
        // the requested printer is not available
        if (!feedbackAllowed())

Unknown macro: {            // we are not allowed to prompt the user for a printer, we have to fail                           error( "The requested printer "+printerName+" is not available" );            return false;        }

        // prompt the user for an available printer
        // get a list of available print services
        PrintService[] services = PrinterJob.lookupPrintServices();
        ArrayList values = new ArrayList();
        // add each print service to our list of printers
        for (int i = 0; i < services.length; i++)

Unknown macro: {            String value = services[i].getName();            values.add(value);        }

        // create a parameter for the user to select from
        createFeedbackParameter("printer-name",    
            "select a printer", "", null, values, null, "select");
            return null;
        }
        promptNeeded();
        return true;
    }

    // Get the number of copies
    int copies = 1;
    if (isDefinedInput("copies"))

Unknown macro: {        copies = Integer.valueOf(getInputStringValue("copies")).intValue();    }

    // Check for a valid printFileName or printFile Resource
    if (printFileName != null) {
        try

Unknown macro: {            inStream = new FileInputStream(printFileName);        }

catch (FileNotFoundException fnfe)

Unknown macro: {            error(fnfe.toString(), fnfe);            return false;        }

        // Set the input source for sending to the driver.
        InputSource source = new InputSource(inStream);
        try

Unknown macro: {            Driver driver = new Driver(source, null);            PrinterJob pj = PrinterJob.getPrinterJob();            pj.setPrintService(printer);            PrintRenderer renderer = new PrintRenderer(pj, copies);            driver.setRenderer(renderer);            driver.run();        }

catch (Exception ex)

Unknown macro: {         error( "Could not print the document", ex);            return false;        }

        return true;
    }

done()

This method is called to give the component the opportunity to perform any cleanup operations that are necessary.

  • No labels