Monday, August 11, 2008

BIRT: Launch a BIRT RCP Application through Java Web Start

Its been a while since I've written a good tutorial. So I was fortunate that I had a fellow ask me about writing launching BIRT from Java Web Start. After some investigation, I found that there is a bit of a problem with the OSGi startup in an application. So, I did a little digging into how I can launch an RCP application from Java Web Start. These are the steps I followed on how to be able to do that. The following example will show how to embed BIRT into an RCP application, and then set that RCP application up to run from Java Web Start. I will also store my BIRT report on a web server, load the BIRT report from the web server, and launch it.

I followed, for the most part, the instructions from the following sites:

http://www.onjava.com/pub/a/onjava/2006/07/26/deploying-birt.html?page=4

http://help.eclipse.org/help32/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/java_web_start.htm

I did have to modify this slightly since both of these articles were a little out of date.

First step is to create the RCP application with BIRT capable of running my report.

  1. Create a new Eclipse Plug-In project.

  2. Name the Eclipse project BIRTRCPViewer, and keep the default option.

  3. In the next screen, set that this project will make contributions to the UI, and that it is an RCP Application.

  4. Specify that this will be a RCP application with a view. You can click Finish at this point.

  5. In the Plugin Editor, go over to the Dependencies tab. Add in the org.eclipse.birt.core and org.eclipse.birt.report.engine packages to dependencies, and in the Imported Packages, add in the oda.jdbc, oda.sampledb, and emitter.html packages. If you plan to use other formats besides HTML for your final report output, add them here.

  6. Open up the View.java file and modify it to have a simple Browser element embedded in a fill layout. (Code will be displayed at the end of the next step)
  7. Now, we need to start the BIRT engine, and render a report. I have created a simple report called test.rptdesign, and stored it on my local Apache web server, located at localhost:8080/birt/test.rptdesign. I put all the BIRT startup and render code in the createPartControl method, and set it so that the results will be displayed in the Browser control. The finished code for the View.java is as follows:

    package rcptest;

    import java.io.ByteArrayOutputStream;
    import java.io.InputStream;
    import java.net.URL;
    import java.util.HashMap;

    import org.eclipse.birt.core.framework.Platform;
    import org.eclipse.birt.report.engine.api.EngineConfig;
    import org.eclipse.birt.report.engine.api.EngineConstants;
    import org.eclipse.birt.report.engine.api.EngineException;
    import org.eclipse.birt.report.engine.api.HTMLRenderContext;
    import org.eclipse.birt.report.engine.api.HTMLRenderOption;
    import org.eclipse.birt.report.engine.api.IRenderOption;
    import org.eclipse.birt.report.engine.api.IReportEngine;
    import org.eclipse.birt.report.engine.api.IReportEngineFactory;
    import org.eclipse.birt.report.engine.api.IReportRunnable;
    import org.eclipse.birt.report.engine.api.IRunAndRenderTask;
    import org.eclipse.swt.SWT;
    import org.eclipse.swt.browser.Browser;
    import org.eclipse.swt.widgets.Composite;
    import org.eclipse.ui.part.ViewPart;

    public class View extends ViewPart {
    public static final String ID = "RCPTest.view";
    private Browser browser;

    /**
    * This is a callback that will allow us to create the viewer and initialize
    * it.
    */
    public void createPartControl(Composite parent) {
    browser = new Browser(parent, SWT.NONE);

    try {
    this.previewReport();
    } catch (EngineException e) {
    e.printStackTrace();
    }
    }

    private void previewReport()
    throws EngineException {
    EngineConfig config = new EngineConfig();
    // Create the report engine
    IReportEngineFactory factory =
    (IReportEngineFactory) Platform
    .createFactoryObject(
    IReportEngineFactory.
    EXTENSION_REPORT_ENGINE_FACTORY );
    IReportEngine engine = factory.
    createReportEngine( config );
    IReportRunnable design = null;

    try {
    // Open a report design -
    // use design to
    // modify design, retrieve
    // embedded images etc.
    String report = "http://localhost:8080/birt/test.rptdesign";
    URL url = new URL(report);

    InputStream fs = url.openStream();
    design = engine.openReportDesign(fs);
    IRunAndRenderTask task = engine.
    createRunAndRenderTask(design);

    // Set Render context to handle url
    // and image locations
    HTMLRenderContext renderContext =
    new HTMLRenderContext();

    renderContext.setImageDirectory(
    "c:/test/image");
    HashMap< String, HTMLRenderContext >
    contextMap = new
    HashMap< String, HTMLRenderContext >();
    contextMap.put( EngineConstants.
    APPCONTEXT_HTML_RENDER_CONTEXT,
    renderContext );
    task.setAppContext(contextMap);

    // Set rendering options -
    // such as file or stream output,
    // output format, whether it is
    // embeddable, etc
    // Render report to
    // Byte Array
    IRenderOption options;
    options = new HTMLRenderOption( );
    ByteArrayOutputStream bos =
    new ByteArrayOutputStream();

    options.setOutputStream(bos);
    options.setOutputFormat("html");

    task.setRenderOption(options);

    // run the report and destroy the engine

    task.run();
    task.close();

    //set Browser text accordingly
    browser.setText(bos.toString());
    engine.destroy();
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    /**
    * Passing the focus request to the viewer's control.
    */
    public void setFocus() {
    //do nothing
    }
    }


  8. From the Plug-In editor, under the Overview tab, select Launch Eclipse application to confirm that the application works. As you can see from the screenshot, we now have a simple application with a brower with our BIRT results.

  9. Right-mouse click on the BIRTRCPViewer project, go to Other, and under the Plug-In projects, select Product Configuration.

  10. Name your product configuration BIRTRCPViewer.product, or whatever you named your project, and base it off of the launch configuration that we just created. In my screenshot below, I based it off of RCPTest.product launch configuration.
  11. In the Product Configuration Dialog, make sure you have the Product Name and Product ID filled in. Otherwise, when you go to launch the application in Web Start, OSGi will complain about not being able to find a Product ID.

Now, feel free to test launch with the Product Configuration to make sure your application works. Once completed, we will now need to make a “wrapper” for the Java Web Start launch. For the most part, I followed the instructions in the above link. But to be absolute, here are the steps I followed to create the Web Start application.

  1. Create a new Feature Project

  2. Name your Feature Project, and be sure to put in a Feature Provider.

  3. On the next screen, set to initialize from Launch Configuration.

  4. Create a folder under the new Feature product called Startup.

  5. Copy C:\eclipse\plugins\org.eclipse.equinox.launcher_.jar to your projects Startup folder, and rename it startup.jar. This took me a few steps to figure out since in newer version of Eclipse, they did away with the startup.jar in favor of the Equinox launcher.

  6. Under the Overview tab of the Feature product, click on the Export Wizard link.
  7. Specify your output folder.

  8. Under the Options tab, check Package as individual JAR archives (required for JNLP and update sites.

  9. Under the Jar signing tab, enter the relevant information. For better instructions than I can provide on how to set up the Java Keystore, check here: http://www.exampledepot.com/egs/java.security.cert/CreateCert.html

  10. In the Java Web Start tab, put in the Site URL where you will store your application and the JRE version.

  11. Now, export your wrapper. If all went according to plan, you should have a structure similar to below

  12. We are missing 1 thing in this file structure, the JNLP file that needs to launch the whole thing. Below is my final JNLP based off of the example in the web start link at the top of the article.
    <?xml version="1.0" encoding="UTF-8"?>
    <jnlp
    spec="1.0+"
    codebase="http://localhost:8080/birt"
    href="/birt/startup.jnlp"> <!-- URL to the site containing the jnlp application. It should match the value used on export. Href, the name of this file -->
    <information>
    <!-- user readable name of the application -->
    <title> BIRT RCP Viewer </title>
    <!-- vendor name -->
    <vendor>digiassn.blogspot.com</vendor>
    <!-- vendor homepage -->
    <homepage href="My company website" />
    <!-- product description -->
    <description>This is a RCP BIRT Viewer</description>
    <icon kind="splash" href="splash.gif"/>
    <offline-allowed/>
    </information>

    <!--request all permissions from the application. This does not change-->
    <security>
    <all-permissions/>
    </security>

    <!-- The name of the main class to execute. This does not change-->
    <application-desc main-class="org.eclipse.equinox.launcher.WebStartMain">
    <argument>-nosplash</argument>
    </application-desc>

    <resources>
    <!-- Reference to the startup.jar. This does not change -->
    <jar href="startup.jar"/>

    <!-- Reference to all the plugins and features constituting the application -->
    <!-- Here we are referring to the wrapper feature since it transitively refers to all the other plug-ins necessary -->
    <extension
    name="Wrapper feature"
    href="/birt/features/MyRCP_1.0.0.jnlp"/>

    <!-- Information usually specified in the config.ini -->
    <property
    name="osgi.instance.area"
    value="@user.home/Application Data/birt"/>
    <property
    name="osgi.configuration.area"
    value="@user.home/Application Data/birt"/>

    <!-- The id of the product to run, like found in the overview page of the product editor -->
    <property
    name="eclipse.product"
    value="RCPTest.product"/>
    </resources>

    <!-- Indicate on a platform basis which JRE to use -->
    <resources os="Mac">
    <j2se version="1.5+" java-vm-args="-XstartOnFirstThread"/>
    </resources>
    <resources os="Windows">
    <j2se version="1.4+"/>
    </resources>
    <resources os="Linux">
    <j2se version="1.4+"/>
    </resources>
    </jnlp>


  13. Copy everything to the /birt folder on Apache Tomcat.

And that’s it, now, I can go to my web server at http://localhost:8080/birt/startup.jnlp and launch my new RCP application via Java Web Start.