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.

Saturday, July 12, 2008

BIRT: Reading Chart From Library and Adding to Report Design

I recently came back from a client engagement that was pretty exciting. They are integrating BIRT into their product using a “report wizard” to dynamically build reports. I was pretty stoked by this. There are different ways to accomplish what they wanted internal to the BIRT report scripting and Report Engine environment, and the way they are utilizing it is pretty darn cool.

One of the ideas we came up with was to read chart prototypes from a library and to add them using the Report Engine API to their final report design. There are several advantages to this. First on, of course, is that this gives them the ability to graphically build the visual attributes of their charts using the BIRT Report Designer, and the ability to rapidly change elements and have them propagate to any report consuming them. This will keep chart look and feel consistent throughout their site. But with any approach, pros and cons need to be taken into consideration. In this case, there were a few hurdles to overcome. First being that since their data is build dynamically through their report wizard, data binding to the chart cannot be done in the report library and would need to be done at run-time, as would any chart series formatting. This differs from my previous example in that it actually adds to a report, not just rendering a stand-alone chart.

The following example of how to read a chart from a Library design, and add the data binding to it. There are a few things to notate about this example. First, if you look at my previous Chart Engine API example, you will notice that data is bound differently. When you work with the Chart Engine API in a standalone fashion, you have to manually create what is called a Data Set (not to be confused with a BIRT Report Data Set). A Data Set in the Chart Engine API is basically an array of values bound to an axis, there are no report binding elements involved. When adding to a report, you have to create a dataset binding to the ExtendedReportItem, not the chart itself.

package com.digiassn.blogspot.birt.chartBuilder;

import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;

import org.eclipse.birt.chart.model.ChartWithoutAxes;
import org.eclipse.birt.chart.model.component.Series;
import org.eclipse.birt.chart.model.component.impl.SeriesImpl;
import org.eclipse.birt.chart.model.data.Query;
import org.eclipse.birt.chart.model.data.SeriesDefinition;
import org.eclipse.birt.chart.model.data.impl.QueryImpl;
import org.eclipse.birt.chart.model.data.impl.SeriesDefinitionImpl;
import org.eclipse.birt.chart.model.type.PieSeries;
import org.eclipse.birt.chart.model.type.impl.PieSeriesImpl;
import org.eclipse.birt.core.exception.BirtException;
import org.eclipse.birt.core.framework.Platform;
import org.eclipse.birt.core.framework.PlatformConfig;
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.IRenderTask;
import org.eclipse.birt.report.engine.api.IReportDocument;
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.IRunTask;
import org.eclipse.birt.report.model.api.DataSetHandle;
import org.eclipse.birt.report.model.api.DataSourceHandle;
import org.eclipse.birt.report.model.api.DesignConfig;
import org.eclipse.birt.report.model.api.DesignElementHandle;
import org.eclipse.birt.report.model.api.DesignFileException;
import org.eclipse.birt.report.model.api.ElementFactory;
import org.eclipse.birt.report.model.api.ExtendedItemHandle;
import org.eclipse.birt.report.model.api.IDesignEngine;
import org.eclipse.birt.report.model.api.IDesignEngineFactory;
import org.eclipse.birt.report.model.api.LabelHandle;
import org.eclipse.birt.report.model.api.LibraryHandle;
import org.eclipse.birt.report.model.api.PropertyHandle;
import org.eclipse.birt.report.model.api.ReportDesignHandle;
import org.eclipse.birt.report.model.api.SessionHandle;
import org.eclipse.birt.report.model.api.activity.SemanticException;
import org.eclipse.birt.report.model.api.command.ContentException;
import org.eclipse.birt.report.model.api.command.NameException;
import org.eclipse.birt.report.model.api.elements.structures.ComputedColumn;

import com.ibm.icu.util.ULocale;

public class ChartBuilder7 {
private static String BIRT_HOME = "C:/birt-runtime-2_2_1_1/ReportEngine";

public static void main(String[] args) {
try {
final ChartBuilder7 cb = new ChartBuilder7();

cb.buildReport();
cb.runReport();
cb.renderReport();
//cb.renderReportlet();

cb.shutdown();
} catch (final ContentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (final NameException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (final SemanticException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (final DesignFileException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (final BirtException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (final IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

//factories and engines used in program
IDesignEngine dengine;
IDesignEngineFactory designFactory;
IReportEngine rengine; // stores the single instance of the report engine

SessionHandle session;

public ChartBuilder7() throws BirtException {
final PlatformConfig platformConfig = new PlatformConfig();
platformConfig.setBIRTHome(BIRT_HOME);
Platform.startup(platformConfig);

// create a new report engine factory
final IReportEngineFactory reportFactory = (IReportEngineFactory) Platform
.createFactoryObject(IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY);

// create a new report engine
final EngineConfig engineConfig = new EngineConfig();
engineConfig.setBIRTHome(BIRT_HOME); // will replace with
// configuration file
rengine = reportFactory.createReportEngine(engineConfig);

final DesignConfig dconfig = new DesignConfig();
dconfig.setBIRTHome(BIRT_HOME);

// try to start up the eclipse platform
designFactory = (IDesignEngineFactory) Platform
.createFactoryObject(IDesignEngineFactory.EXTENSION_DESIGN_ENGINE_FACTORY);
dengine = designFactory.createDesignEngine(dconfig);

// create a new session, open the library, and retrieve the first data
// source since it is uniform in our library
session = dengine.newSessionHandle(ULocale.ENGLISH);

}

/**
* Adds data binding information to chart retrieved from library
*
* @param eih
*/

public void addPieChartFromLibrary(ExtendedItemHandle eih) {
try {
// create chart and set standard options
ChartWithoutAxes cwaPie = (ChartWithoutAxes)eih.getReportItem().getProperty("chart.instance");

//clear any existing definitions from the chart
cwaPie.getSeriesDefinitions().clear();

//create actual series data. This will not be in the library chart
Series seCategory = SeriesImpl.create( );

//This is where things differ from standalone charts. A Query is an EList
//that contains an expression for data. Keep in mind we need to set out
//data set below, otherwise setting these expressions are pointless
Query query = QueryImpl.create( "row[\"OrganizationName\"]");//$NON-NLS-1$
// When creating a series, getDataDefinition() is expecting the EList,
// and the Query is an instance of that
seCategory.getDataDefinition( ).add( query );

SeriesDefinition series = SeriesDefinitionImpl.create( );
series.getSeries( ).add( seCategory );
cwaPie.getSeriesDefinitions( ).add( series );
series.getSeriesPalette().shift(1); // to change the color of the next slice

// Orthogonal Series
PieSeries sePie = (PieSeries) PieSeriesImpl.create( );

Query query2 = QueryImpl.create( "row[\"CandidatesImage\"]");//$NON-NLS-1$
sePie.getDataDefinition().add(query2);
sePie.setExplosion( 5 );

SeriesDefinition sdEmpCount = SeriesDefinitionImpl.create( );
sdEmpCount.getSeriesPalette().shift(2);
series.getSeriesDefinitions( ).add( sdEmpCount );
sdEmpCount.getSeries( ).add( sePie );

//add to report
// eih.loadExtendedElement(); // this may or may not need to
//be called - it's typically used for reading (if already in the rpt design file)
// eih.getReportItem().setProperty("chart.instance", cwaPie);
eih.setProperty("outputFormat", "PNG");
eih.setProperty("dataSet", "GridXMLDataSet"); // tell the chart which dataset to use to bind the chart to the report

//do the bindings to the extended element handle
PropertyHandle computedSet = eih.getColumnBindings( );
ComputedColumn cs1 = new ComputedColumn();
cs1.setExpression( "dataSetRow[\"OrganizationName\"]");//$NON-NLS-1$
cs1.setName( "OrgName" );
cs1.setDataType( "string" );
computedSet.addItem( cs1 );

ComputedColumn cs2 = new ComputedColumn();
cs2.setExpression( "dataSetRow[\"CandidatesImage\"]");//$NON-NLS-1$
cs2.setName( "Candidates" );
cs2.setDataType( "float" );
computedSet.addItem( cs2 );
}
catch ( Exception E ) {
E.printStackTrace();
}
}


/**
* Build report
* @throws ContentException
* @throws NameException
* @throws SemanticException
* @throws DesignFileException
* @throws IOException
*/
public void buildReport() throws ContentException, NameException,
SemanticException, DesignFileException, IOException {
// create a new report
final ReportDesignHandle reportDesign = session.createDesign();

final ElementFactory ef = reportDesign.getElementFactory();

// add new master page element
final DesignElementHandle element = ef
.newSimpleMasterPage("Page Master");
reportDesign.getMasterPages().add(element);

// get the session, open the library
final SessionHandle session = dengine.newSessionHandle(ULocale.ENGLISH);
final LibraryHandle library = session
.openLibrary("C:\\contracts\\CompanyA\\ChartBuild\\chart_library.rptlibrary");

// get the data source and date set from library
final DataSourceHandle dataSourceHandle = (DataSourceHandle) library
.getDataSources().get(0);
final DataSetHandle dataSetHandle = (DataSetHandle) library
.getDataSets().get(0);

// add the data sources and data sets to report
reportDesign.getDataSources().add(dataSourceHandle);
reportDesign.getDataSets().add(dataSetHandle);

// create a new extended item handle and add. This is read in the library
//so we do not need to manually create visual elements. Then add to the body of our
//new report design
final ExtendedItemHandle eih = (ExtendedItemHandle)library.findElement("PieChart");
reportDesign.getBody().add(eih);

//modify the chart with data bindings so that it is bound to our new report design
this.addPieChartFromLibrary(eih);

// add some sample labels
final LabelHandle lh = ef.newLabel("New Label");
lh.setText("Can you see me?");
reportDesign.getBody().add(lh);

// set my initial properties for the new report
final String reportName = "Example Chart";
reportDesign.setDisplayName(reportName);
reportDesign.setDescription(reportName);
reportDesign.setIconFile("/templates/blank_report.gif");
reportDesign.setFileName("C:/Temp/" + reportName + ".rptdesign");
reportDesign.setDefaultUnits("in");
reportDesign.setProperty("comments", reportName);
reportDesign.setProperty(IReportRunnable.TITLE, reportName);

// save report design
reportDesign.saveAs("C:/Temp/" + reportName + ".rptdesign");
}

/**
* Render full report
*/
public void renderReport() {
try {
final IReportDocument rptdoc = rengine
.openReportDocument("C:/temp/tempDoc.rptDocument");

// Create Render Task
final IRenderTask rtask = rengine.createRenderTask(rptdoc);

// Set Render context to handle url and image locataions
final HTMLRenderContext renderContext = new HTMLRenderContext();
// Set the Base URL for all actions
renderContext.setBaseURL("http://localhost/");
// Tell the Engine to prepend all images with this URL - Note this
// requires using the HTMLServerImageHandler
renderContext.setBaseImageURL("http://localhost/myimages");
// Tell the Engine where to write the images to
renderContext.setImageDirectory("C:/xampplite/htdocs/myimages");
// Tell the Engine what image formats are supported. Note you must
// have SVG in the string
// to render charts in SVG.
renderContext.setSupportedImageFormats("JPG;PNG;BMP;SVG");
final HashMap<String, HTMLRenderContext> contextMap = new HashMap<String, HTMLRenderContext>();
contextMap.put(EngineConstants.APPCONTEXT_HTML_RENDER_CONTEXT,
renderContext);
rtask.setAppContext(contextMap);

// Set rendering options - such as file or stream output,
// output format, whether it is embeddable, etc
final HTMLRenderOption options = new HTMLRenderOption();

// Remove HTML and Body tags
// options.setEmbeddable(true);

// Set ouptut location
options.setOutputFileName("C:/temp/outputReport.html");

// Set output format
options.setOutputFormat("html");
rtask.setRenderOption(options);

rtask.render();

// render the report and destroy the engine
// Note - If the program stays resident do not shutdown the Platform
// or the Engine
rtask.close();
} catch (final EngineException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

/**
* render just a reportlet
*/
public void renderReportlet() {
try {
final IReportDocument rptdoc = rengine
.openReportDocument("C:/temp/tempDoc.rptDocument");

// Create Render Task
final IRenderTask rtask = rengine.createRenderTask(rptdoc);

// Set Render context to handle url and image locataions
final HTMLRenderContext renderContext = new HTMLRenderContext();
// Set the Base URL for all actions
renderContext.setBaseURL("http://localhost/");
// Tell the Engine to prepend all images with this URL - Note this
// requires using the HTMLServerImageHandler
renderContext.setBaseImageURL("http://localhost/myimages");
// Tell the Engine where to write the images to
renderContext.setImageDirectory("C:/xampplite/htdocs/myimages");
// Tell the Engine what image formats are supported. Note you must
// have SVG in the string
// to render charts in SVG.
renderContext.setSupportedImageFormats("JPG;PNG;BMP;SVG");
final HashMap<String, HTMLRenderContext> contextMap = new HashMap<String, HTMLRenderContext>();
contextMap.put(EngineConstants.APPCONTEXT_HTML_RENDER_CONTEXT,
renderContext);
rtask.setAppContext(contextMap);

// Set rendering options - such as file or stream output,
// output format, whether it is embeddable, etc
final HTMLRenderOption options = new HTMLRenderOption();

// Remove HTML and Body tags
// options.setEmbeddable(true);

// Set ouptut location
options.setOutputFileName("C:/temp/outputReportlet.html");

// Set output format
options.setOutputFormat("html");
rtask.setRenderOption(options);

rtask.setReportlet("ChartToRender");
rtask.render();

// render the report and destroy the engine
// Note - If the program stays resident do not shutdown the Platform
// or the Engine
rtask.close();
} catch (final EngineException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

/**
* Run report
*/
public void runReport() {
try {
final IReportRunnable design = rengine.openReportDesign("C:/Temp/"
+ "Example Chart" + ".rptdesign");
final IRunTask runTask = rengine.createRunTask(design);

// use the default locale
runTask.setLocale(Locale.getDefault());

runTask.run("C:/temp/tempDoc.rptDocument");
runTask.close();
} catch (final EngineException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public void shutdown() {
Platform.shutdown();
}
}


Sunday, June 29, 2008

BIRT: BIRT 2.3 is out

The next release of BIRT, 2.3, is out. Check it out: http://www.eclipse.org/birt/phoenix/

Thursday, June 26, 2008

Phone: Verizon XV6900

So, I had an unfortunate situation with my old Motorola Razr, where the speaker stopped working and I couldn’t hear anything, so it was time for a new phone. My requirements were simple, I needed to be able to tether, sync with Outlook to retire my existing PDA, and something that I could play Chess on.

What I ended up with was the HTC XV8900 from Verizon. I refused to leave Verizon, although I am still tempted to get an iPhone, I just can’t stomach AT&T’s coverage. So far it is a decision I have been very happy with. I won’t review it since there are plenty off reviews about this phone out there, so I’ll just share my thoughts on it. First off, it uses a touch interface for everything, so no slideout QWERTY keyboard or anything of that nature for quick texting, which is fine because I seem to be of a generation before texting became popular. Besides, that’s one of the things that makes the iPhone so stylish, and I agree with the “no buttons” philosophy to an extent. I have a portable Bluetooth foldout keyboard, so I’d rather have a fullsize keyboard for any serious typing than the crappy keyboards they have on phones. I did have to hack the keyboard a little since it was a HP Foldout Bluetooth keyboard and used some sort of proprietary driver, but thanks to a nice fellow from Africa, I have a workable solution.

After a few modifications, I have a really nice “Slide to Unlock” option that mimics the iPhones, with the Caller ID mimicking it as well. Outside of that, the other features, such as web browsing, media, camera, are pretty much there, so anything an iPhone can do, I can do on this thing since Windows Mobile devices have been doing these things since before I got my WM 2003 device.

Outlook syncing hasn’t changes since my WM2003 device, except now I have it on my phone, and I can check my email when I am out and about. The only issue is Activesync considers my “Outlook Email” separate from my two email accounts, so when I sync up, it wont sync mail that is checked already on the phone. A little annoying, so I have it configured not to delete messages on the phone when checked. If I ran an Exchange server, I could use the Push option, which I’m sure Blackberry users are all too familiar with, but I’m lazy and don’t want to configure an Exchange server just for this purpose.

While stylish, and features that do just about everything I need, there are a few gripes about this device. 1st, since it is a Windows Mobile device, the ever present X does not close applications annoyance is there. While HTC includes a quick link in the top right corner to kill all applications, its still annoying. Also annoying is the fact that TouchFlo is always present. I wanted to give Spb mobile shell a try, but I don’t want to run that AND touchflo at the same time. Even after I kill all the TouchFlo apps, it still manages to start itself if I do a finger gesture or pen swipe from the bottom to the top of the screen. Also, the paltry 512 flashram is laughable compared to the 16 Gb you can get on other phones. I had to purchase a separate MicroSD card to compensate. Also annoying is the fact that the memory usage is not configurable. On my older WM2003 device, I could allocate memory for storage or applications running, on this device that doesn’t seem to e an option. With the MicroSD card, why would I want to use the precious flashram to store apps when I don’t have to? Also, I had a hard time finding a case to fit the device. Verizon only offered a silicone slip cover for the phone, so I had to put on a screen protector, and find a 3rd part case. So the case I have is slightly larger than the phone itself, and it’s a little gangly. I’ll have to keep my eye out for a more easily accessible case for this phone.

Overall, I am happy with the phone though. It does everything my old hacked Razr did, plus more. No more crappy, hard to read, mobile web, I have the same games, tethering capability, plus a suite of mobile office applications to use with my Bluetooth keyboard, a media device, movies, and games.

Tuesday, April 22, 2008

BIRT: Another BIRT Book

I was surprised to find this when looking around on Amazon earlier. To join the ranks of my book and the books from AW about BIRT, an author out of Germany is publishing a book called "Eclipse BIRT" from XPert Press. I would like to have an opinion about it, unfortunately the language barrier is a bit of an issue for me since I don't speak German. Regardless, it is good to see another book about BIRT out there, showing the project is growing and demand is out there.

Friday, March 28, 2008

Games: Ninja Gaiden Dragon Sword

I was really skeptical about this game at first. I had read plenty of pre-reviews about this game and using the stylus to "slash" the opponents on the screen. I just imagined some geeky kid on an airplane waving his hands widley, shopping at the tiny DS screen with a katana shapped stylus, poking the passengers around him in the eyes. And given the fact that Ninja Gaiden on the XBox is second only the GTA in the need for an ESRB rating, I was a little worried about the delivery on family friendly Nintendos consoles.

Now that I've gotten a hands on with it, I can say those fears are completely unfounded. NG:DS is a completely engrossing handheld experience right from the get go. Gone are the days and stories of Ryu chasing Jaquio around looking for statues and saving Irine from the clutches of certain death. NG:DS takes up from the original Xbox story line, some 6 months after the "Dark Dragon Blade" incident. While I haven't gotten enough into the story, I did run around quite a bit to make sure the control scheme wasn't nearly as wonky as I had feared. Holding the DS at 90 degrees, book style, is a bit awkward, but only because I've spent the past 20 years of my life holding controllers ALA the original NES control scheme. Moving the character around is done via the stylus, as is the attacks. How is this done? Why, by pointing where you want to move to, writing/slashing the stylus over the opponent to attack, tapping the opponent to throw ninja stars, and slashing up to jump. So the attacks are actually pretty intuitive, despite my original fears.

There are a few hiccups however. There re times where to want to jump, and the chacter runs right into the coming onslaught of enemies instead. Thats kind of annoying, but I can chalk that up to my own ineptness with the control scheme. Blocking is a bit of a pain. Any button will cause you to block. But since I am a righty, I hold the stylus with my right-hand, the DS is in a right-hand orientation, which puts the buttons on the right-screen. So, as you can imagine, having to use my left hand to push a button in a right-handed orientation leaves me a little short-circuited due to my lack of ambidextrous abilities. Fortunately, what I lack in coordination I more than make up for in hand size, so reaching over to push buttons to block isn't as bad as I make it seem.

While I haven't done enough story line to really pass judgment on it, I did find the game bordering on "not able to put down" fun from running around and slashing. While this game isn't quite suitable for my game time during meals while traveling due to it's more interactive nature, it is definitely suitable for the evenings at the hotel while traveling and on long plane rides so that I can be the uncoordinated geeky kid poking peoples eyes out.