Thursday, November 05, 2009

Calling Spring Objects from BIRT Expressions and Event Handlers

Several examples are already available on the web, which demonstrate calling the BIRT engine from a Spring MVC application.

Integrating BIRT with Spring in a Web application
and
Eclipse BIRT in Spring web applications
are a couple of examples.

There is not much information on how to include Spring Beans within a report design. This post details an example of injecting the Spring ApplicationContext into BIRT’s AppContext object which will allow you to call your Spring Beans in BIRT expressions or event handlers. A link for the source is listed at the bottom. A readme file containing instructions for building the example is included in the download.

You can include the BIRT runtime by following the post above or examining the download. The Example implements a Spring Controller with the following code.


package org.eclipse.birt.spring;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.birt.report.engine.api.HTMLRenderOption;
import org.eclipse.birt.report.engine.api.HTMLServerImageHandler;
import org.eclipse.birt.report.engine.api.IReportEngine;
import org.eclipse.birt.report.engine.api.IReportRunnable;
import org.eclipse.birt.report.engine.api.IRunAndRenderTask;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

public class BirtController extends AbstractController {

private IReportEngine reportEngine;

protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {

String reportName = request.getParameter("ReportName");
ServletContext sc = request.getSession().getServletContext();
reportEngine = BirtEngine.getBirtEngine(sc);
IReportRunnable runnable = null;
runnable = reportEngine.openReportDesign( sc.getRealPath("/Reports")+"/"+reportName );
IRunAndRenderTask runAndRenderTask = reportEngine.createRunAndRenderTask(runnable);

HTMLRenderOption options = new HTMLRenderOption();
options.setOutputFormat("html");
options.setOutputStream(response.getOutputStream());
options.setImageHandler(new HTMLServerImageHandler());
options.setBaseImageURL(request.getContextPath()+"/images");
options.setImageDirectory(sc.getRealPath("/images"));
runAndRenderTask.setRenderOption(options);
runAndRenderTask.run();

return null;
}

}


Note that no error checking is implemented in this example. This controller class just extends the Spring AbstractController class passes the response object to the BIRT engine to output the report. The report name is retrieved from the request. Before running the report the BirtEngine class is used to retrieve the BIRT engine. This is virtually the same BirtEngine class used in the servlet example available in the BIRT wiki with some exceptions that are noted here.


package org.eclipse.birt.spring;

import java.io.InputStream;
import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;

import org.eclipse.birt.report.engine.api.EngineConfig;
import org.eclipse.birt.report.engine.api.IReportEngine;
import javax.servlet.*;
import org.eclipse.birt.core.framework.PlatformServletContext;
import org.eclipse.birt.core.framework.IPlatformContext;
import org.eclipse.birt.core.framework.Platform;
import org.eclipse.birt.core.exception.BirtException;
import org.eclipse.birt.report.engine.api.IReportEngineFactory;
import org.springframework.context.ApplicationContext;

public class BirtEngine {

private static IReportEngine birtEngine = null;

private static Properties configProps = new Properties();

private final static String configFile = "BirtConfig.properties";

public static synchronized void initBirtConfig() {
loadEngineProps();
}
public static synchronized IReportEngine getBirtEngine(ServletContext sc) {
if (birtEngine == null)
{
//optionally load engine props
//loadEngineProps();
EngineConfig config = new EngineConfig();
if( configProps != null){
String logLevel = configProps.getProperty("logLevel");
Level level = Level.OFF;
if ("SEVERE".equalsIgnoreCase(logLevel))
{
level = Level.SEVERE;
} else if ("WARNING".equalsIgnoreCase(logLevel))
{
level = Level.WARNING;
} else if ("INFO".equalsIgnoreCase(logLevel))
{
level = Level.INFO;
} else if ("CONFIG".equalsIgnoreCase(logLevel))
{
level = Level.CONFIG;
} else if ("FINE".equalsIgnoreCase(logLevel))
{
level = Level.FINE;
} else if ("FINER".equalsIgnoreCase(logLevel))
{
level = Level.FINER;
} else if ("FINEST".equalsIgnoreCase(logLevel))
{
level = Level.FINEST;
} else if ("OFF".equalsIgnoreCase(logLevel))
{
level = Level.OFF;
}

config.setLogConfig(configProps.getProperty("logDirectory"), level);
}
config.setEngineHome("");

IPlatformContext context = new PlatformServletContext( sc );
config.setPlatformContext( context );
config.getAppContext().put("PARENT_CLASSLOADER", BirtEngine.class.getClassLoader());
ApplicationContext sprCtx = ContextAccess.getApplicationContext();
config.getAppContext().put("spring",sprCtx);
try
{
Platform.startup( config );
}
catch ( BirtException e )
{
e.printStackTrace( );
}

IReportEngineFactory factory = (IReportEngineFactory) Platform
.createFactoryObject( IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY );
birtEngine = factory.createReportEngine( config );
}
return birtEngine;
}
public static synchronized void destroyBirtEngine() {
if (birtEngine == null) {
return;
}
birtEngine.destroy();
Platform.shutdown();
birtEngine = null;
}
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
private static void loadEngineProps() {
try {
//Config File must be in classpath
ClassLoader cl = Thread.currentThread ().getContextClassLoader();
InputStream in = null;
in = cl.getResourceAsStream (configFile);
configProps.load(in);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

This class just wraps access to the BIRT engine in a singleton.
The notable changes are in these lines of code deal with the EngineConfig class.

config.setEngineHome("");

IPlatformContext context = new PlatformServletContext( sc );
config.setPlatformContext( context );
config.getAppContext().put("PARENT_CLASSLOADER", BirtEngine.class.getClassLoader());
ApplicationContext sprCtx = ContextAccess.getApplicationContext();
config.getAppContext().put("spring",sprCtx);


The setEngineHome is passed a blank value and setting the PlatformContext to use a PlatformServletContext class will by default look for the BIRT plugins in the WEB-INF/Platform/plugins directory. Next we set the parent classloader for the report engine plugin so that classes available to the project will also be available to the BIRT engine. The final line gets the Spring ApplicationContext instance and loads it into the BIRT AppContext object and names it “spring”. The method used for retrieving the Spring context is described in a great post here.

The ContextAccess class is defined as follows.


package org.eclipse.birt.spring;
import org.springframework.context.ApplicationContext;
public class ContextAccess {
private static ApplicationContext ctx;
public static void setApplicationContext(ApplicationContext applicationContext) {
ctx = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return ctx;
}
}


This Class just contains methods to set and get the static variable ctx, which will contain the Spring ApplicationContext with the addition of one more class.



package org.eclipse.birt.spring;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;


public class SpringContextProvider implements ApplicationContextAware {
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
ContextAccess.setApplicationContext(ctx);
}
}



This Class implements the ApplicationContextAware interface which causes the Spring framework to callback this class with the Spring context, when it is created. The Spring configuration file for this example looks like the following.

<beans>
<bean id="springContextProvider" class="org.eclipse.birt.spring.SpringContextProvider">
</bean>
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/runbirt.htm">birtController</prop>
</props>
</property>
</bean>
<bean id="birtEngine"
class="org.eclipse.birt.spring.BirtEngine"
destroy-method="destroyBirtEngine">
</bean>
<bean id="birtController"
class="org.eclipse.birt.spring.BirtController">
</bean>
<bean id="carPojo"
class="org.eclipse.birt.spring.CarPojo">
</bean>

</beans>

We have a carPojo bean that is available. To access this from a BIRT expression, all we have to do is use the following syntax.


var mypojo = spring.getBean("carPojo");


You can then call the methods on the object. Eg mypojo.getYear();



Output for the example is presented below.




Caveats
If you preview the report in the designer the report will not work. This is because the Spring context is not available to the designer. The report has to be deployed to test it. There are many ways this example could be extended to circumvent this issue. BIRT provides an extension point to enhance the expression builder which could be used with Spring Remoting to access the Spring context. BIRT also provides an extension to implement a BIRT application context object within the designer that could be used in this same fashion. I will try to implement one of these methods in the future to illustrate this concept.

The example can be downloaded from Birt-Exchange.

Tuesday, October 20, 2009

Multiple Hyperlinks on BIRT Charts

BIRT 2.5.1 was released a couple of weeks ago and with its release BIRT Charts now support multiple hyperlinks. To use this feature select any of the interactivity locations within the chart builder. Next select the add button under the hyperlinks list box.



This will launch the hyperlink editor.



Enter the name you wish to appear in the context menu. Next select the edit base URL button, which will display the Hyperlink Options editor.



Here you have the option to link to a URI, a bookmark within the same report or to a bookmark in another report. Values from the current chart can be passed through to the target. For example the value of a slice could be passed to a google query. To do this select the URI radial and enter an expression similar to the one shown in the image below.



When running the report containing the chart, the multiple hyperlinks menu will popup on whatever event you defined in the editor. In the above example this was defined on a mouse click event. So clicking on a slice of the pie will cause the multiple hyperlinks context menu to appear.



This sample report is available at Birt-Exchange.

Monday, September 28, 2009

Calling BIRT reports from Wicket using Actuate’s JSAPI

I have written several posts on how to use the Actuate JSAPI to integrate with various frameworks. In this post I will detail integrating with Wicket. For more information on the JSAPI see this post:

Showing BIRT Reports using the Actuate JSAPI

Wicket can call BIRT reports with the JSAPI in a similar fashion as other front end frameworks that allow AJAX based APIs, where JavaScript is just embedded into the HTML. An HTML file based on the Hello World Wicket example may look similar to the following.

<html>
<head>
<title>Actuate JSAPI Wicket Example</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Viewer creation example</title>
</head>
<body>
<b>
<span wicket:id="message">message will be here</span>
</b>

<div id="acviewer" />

<div id="jsapi_example_container"><script type="text/javascript"
src="http://localhost:8080/ActuateJavaComponent/jsapi"></script> <script
type="text/javascript" language="JavaScript">

actuate.load("viewer");
actuate.initialize("http://localhost:8080/ActuateJavaComponent/",
null,
null,
null,
initViewer);
var viewer;
function initViewer()
{
viewer = new actuate.Viewer("acviewer");
var viewerwidth = 800;
var viewerheight = 620;
viewer.setWidth(viewerwidth);
viewer.setHeight(viewerheight);
run();
}
Using a Wicket Behavior to write out this code
function run()
{
viewer.setParameters({"Customer":"CAF Imports"});
viewer.setReportName("/Public/BIRT and BIRT Report Studio Examples/Customer Order History.rptdesign");
viewer.submit();
}

</script></div>
</body>
</html>

This example will look very familiar to other examples in the JSAPI overview post. The only real difference is the addition of the wicket message, which is generated by my instance of the Wicket Web Page class. You will notice that the run JavaScript function is hard coding the report name and parameters. This may be something you want to handle in the Java code, to make it more dynamic. One way of handling this is to use Wicket Behaviors to create custom components. You could use a Behavior to write out all the above JavaScript, but we will keep it simple and just write out the run script. So the first thing to do is to create a class that extends the AbstractBehavior class.


package jsapi.wicket.sample;

import org.apache.wicket.Component;
import org.apache.wicket.Response;
import org.apache.wicket.behavior.AbstractBehavior;
import org.apache.wicket.util.string.JavascriptUtils;

public class ReportComponent extends AbstractBehavior{

private static final long serialVersionUID = 1L;

public void onRendered(Component component) {
Response response = component.getResponse();
response.write(JavascriptUtils.SCRIPT_OPEN_TAG);
response.write("function run(){");
response.write("viewer.setParameters({\"Customer\":\"CAF Imports\"});");
response.write("viewer.setReportName(\"/Public/BIRT and BIRT Report Studio Examples/Customer Order History.rptdesign\");");
response.write("viewer.submit();}");
response.write(JavascriptUtils.SCRIPT_CLOSE_TAG);
}

}


This class overides the onRendered method to write out the entire run script from the HTML above. I can now modify my extended WebPage class to add an instance of the ReportComponent class.


package jsapi.wicket.sample;

import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;

public class ReportParameter extends WebPage {

private static final long serialVersionUID = 1L;

public ReportParameter(final PageParameters parameters) {

// Add the simplest type of label
Label myLabel = new Label("message", "Actuate JSAPI Wicket Example");
add(myLabel);
ReportComponent rc = new ReportComponent();
myLabel.add(rc);
}
}


The script is written below the Label component.
Finally the HTML should have the run JavaScript function commented out. The output from this example is as follows.


The generated HTML looks like:



Note that you can use the built in view time functions like selecting new parameters and rerunning the report without any additional code.



The example files can be downloaded from Birt-Exchange.

Thursday, September 03, 2009

Calling BIRT reports from Flex using Actuate’s JSAPI

Continuing on a series of posts I have written around Acuate’s JSAPI, this post details integrating BIRT reports with Flex. Previous posts detailed:

Showing BIRT Reports using the Actuate JSAPI
See this link for more details on what is available in the JSAPI.

Calling BIRT Reports from PHP using Actuate’s JSAPI

Calling BIRT Reports from ASP.NET using Actuate’s JSAPI

BIRT Reports can contain Flash components using the Text element. Actuate has also extended the BIRT report designer to include Flash charts. As stated in previous post the JSAPI can be used to include BIRT content in just about any front end. BIRT currently does not support a Flash emitter, but a Flex component can call the JSAPI to include and modify BIRT content within the same HTML page. This post details how this can be achieved.

The Flex SDK provides a Flex Ajax bridge that allows a Flex application to call and interact with Ajax based APIs. To illustrate how this can be used with Actuate’s JSAPI, I am going to build a Flex application that list a couple of reports. The application will also contain a button to execute the report. The output for the report will be written to another DIV element within the HTML wrapper.

The mxml file is pretty simple and contains a DataGrid that calls an action script to load the names and paths of two reports.


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
width="640" height="300" layout="vertical" backgroundColor="white">
<fab:FABridge xmlns:fab="bridge.*" />

<mx:Panel id="pnlMain" x="10" y="10" width="560" height="260"
layout="absolute" title="BIRT Reports From Flex">
<mx:DataGrid id="dgReports" x="10" y="10" initialize="initReports()" width="530" height="130">
<mx:columns>
<mx:DataGridColumn headerText="Report Name" width="160" dataField="name"/>
<mx:DataGridColumn headerText="Report Path" dataField="path"/>
</mx:columns>
</mx:DataGrid>
<mx:Button x="10" y="156" label="Run Report From Flex" id="flashButton" />
</mx:Panel>
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;

public function initReports():void
{
var reports:Array = new Array();
reports.push({name: "Monthly Revenue Analysis", path: "/Public/BIRT and BIRT Report Studio Examples/Monthly Revenue Analysis.rptdesign"});
reports.push({name: "Sales by Territory", path: "/Public/BIRT and BIRT Report Studio Examples/Sales by Territory.rptdesign"});
var reportCollection:ArrayCollection = new ArrayCollection(reports);
dgReports.dataProvider = reportCollection;
dgReports.selectedIndex = 0;
}
]]>
</mx:Script>
</mx:Application>


The fab:FABridge xmlns:fab=”bridge.*” tag includes the Flex Ajax bridge library. In this example I just added a bridge directory to my application, which contained the FABridge.as and FABridge.js files that handle the bridge. These files are available in the Flex SDK. This application also contains a button (flashButton) that will be used by JavaScript later in the example.

My wrapper HTML file contains a div element to display the application which is named BirtFlex. The flashvars parameter is used to set a unique bridge name for this application. The bridge name is used by JavaScript to get/set values within the Flex application.


<div id="fply">
<script language='javascript' charset='utf-8'>
document.write("<object id='flexApp' classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,5,0,0' type='application/x-shockwave-flash' height='300' width='640'>");
document.write("<param name='flashvars' value='bridgeName=birtexample'/>");
document.write("<param name='src' value='BirtFlex.swf'/>");
document.write("<embed name='BirtFlexDemo' pluginspage='http://www.macromedia.com/go/getflashplayer' src='BirtFlex.swf' height='300' width='640' flashvars='bridgeName=birtexample'/>");
document.write("</object>");
</script>
</div>


For brevity, I have foregone error checking and player checks.
To use the JSAPI the following code is added to the wrapper HTML file.


<div id="jsapi_example_container">
<script type="text/javascript" src="http://localhost:8080/ActuateJavaComponent/jsapi"></script>
<script type="text/javascript" language="JavaScript">

actuate.load("viewer");
actuate.initialize("http://localhost:8080/ActuateJavaComponent/",
null,
null,
null,
initViewer);
var viewer;
function initViewer()
{
document.getElementById("run").disabled = true;
viewer = new actuate.Viewer("acviewer");
var viewerwidth = 500;
var viewerheight = 620;
viewer.setWidth(viewerwidth);
viewer.setHeight(viewerheight);
setupFlashCallBack();
}
function run()
{
var flexApp = FABridge.birtexample.root();
var appValue = flexApp.getDgReports().getSelectedItem().path;
viewer.setReportName(appValue);
viewer.submit();
}
function setupFlashCallBack()
{
var initCallback = function()
{
//alert("Enabling");
document.getElementById("run").disabled = false;
}
var btnCallback = function()
{
var flexApp = FABridge.birtexample.root();
flexApp.getFlashButton().addEventListener("click",flashButtonClicked);
}
FABridge.addInitializationCallback("birtexample",initCallback);
FABridge.addInitializationCallback("birtexample",btnCallback);
}
function flashButtonClicked(event)
{
run();
}
</script>
</div>



This code is very similar to previous examples with a few notable exceptions. When the JSAPI is initialized the call back function initViewer is set.


actuate.initialize("http://localhost:8080/ActuateJavaComponent/",
null,
null,
null,
initViewer);



Once the API is initialized, the initViewer JavaScript function is called.


function initViewer()
{
document.getElementById("run").disabled = true;
viewer = new actuate.Viewer("acviewer");
var viewerwidth = 500;
var viewerheight = 620;
viewer.setWidth(viewerwidth);
viewer.setHeight(viewerheight);
setupFlashCallBack();
}



In addition to a button in the Flex application, the HTML wrapper also contains one to run the report. Not that two buttons are needed, but for illustrative purposes, this example contains two. In this example we disable the button until both the JSAPI and the Flex component are initialized. The Viewer JSAPI component is created as usual and then the setupFlashCallBack() function is called.


function setupFlashCallBack()
{
var initCallback = function()
{
document.getElementById("run").disabled = false;
}
var btnCallback = function()
{
var flexApp = FABridge.birtexample.root();
flexApp.getFlashButton().addEventListener("click",flashButtonClicked);
}
FABridge.addInitializationCallback("birtexample",initCallback);
FABridge.addInitializationCallback("birtexample",btnCallback);
}



In this function two call back functions are registered. The first one (initCallback) is setup to let us know when the Flex application is loaded, which then enables the run button. The second one (btnCallback) registers an event handler that the bridge will call when the Flex button is pressed. This event handler JavaScript function simply calls the run JavaScript function.


function flashButtonClicked(event)
{
run();
}



The JavaScript button has the run function setup on the onclick event.

<input type="button" id="run" value="Run Report From JavaScript" onclick="run()" >

So the report can be run from either button. The run function sets the report name and executes the API to run the report.

function run()
{
var flexApp = FABridge.birtexample.root();
var appValue = flexApp.getDgReports().getSelectedItem().path;
viewer.setReportName(appValue);
viewer.submit();
}



The report path is retrieved using the getDgReports().getSelectedItem().path command. The DataGrid in the mxml file is named dgReports

<mx:DataGrid id="dgReports" x="10" y="10" initialize="initReports()" width="530" height="130">


The bridge provides getter and setter methods for the items in the Flex application. The DataGrid contains two columns (name and path). The path contains the location of the report, so this is the field that is retrieved from the Flex application. See the Flex developers guide for more details on the bridge calls.



This example can be downloaded at Birt-Exchange.

Wednesday, August 26, 2009

Using Script to Modify a BIRT Chart

When designing BIRT reports that contain charts, the developer has many ways a chart can be customized. One way is to implement a script event handler either in JavaScript or Java. The chart engine currently supports over thirty hooks for implementing an event handler. These events are fired during the generation and rendering of the chart. Many items can be customized. For example, the data for a chart can be changed in the afterDatasetFilled script, or the entire chart model can be changed in the beforeGeneration script.


In addition to changing the chart using the chart scripting hooks, the chart model can also be changed using the primary BIRT report script hooks. When making changes to the report, most developers will use the beforeFactory event script. This event is fired prior to running the report and offers a location to make simple or complex changes to the report. This includes modifying any charts that exist within the report. Simple chart properties can be changed using the Chart Simple API. An example of this approach is described here.

For more complex changes, the Chart API can be called within the script. As an example, changing a specific chart series type is possible. BIRT 2.5 provides study charts which allow multiple Y-axes to be stacked, each with a different series type. Turning this feature on or off can also be done using script. These two changes can be done with the following script:

First import the API packages that will be needed.

importPackage(Packages.org.eclipse.birt.chart.model.data.impl);
importPackage(Packages.org.eclipse.birt.chart.model.type.impl);
importPackage(Packages.org.eclipse.birt.chart.model.attribute.impl);


Retrieve the report design handle and find the chart. Note that the chart needs to be named. This can be done in the general properties for the chart.

//Chart must be named
cht = reportContext.getDesignHandle().findElement("MyChart");
//get the chart model;
mychart = cht.getReportItem().getProperty( "chart.instance" );



Next retrieve the axes. In this example the chart has two Y-Axes. The second one contains the series to be modified.

xAxis =mychart.getAxes().get(0);
yAxis2 = xAxis.getAssociatedAxes().get(1);

Clear all series definitions defined for the second Y-axis.

yAxis2.getSeriesDefinitions().clear();

The example report contains a combo parameter that allows the user to select the series type for the second Y-Axis. The parameter is checked and an appropriate series is created. Note that if an area series is created, the background is set to translucent.

if( params["SeriesType"].value == "Line"){
var ns = LineSeriesImpl.create();
}else if( params["SeriesType"].value == "Bar"){
var ns = BarSeriesImpl.create();
}else if( params["SeriesType"].value == "Area"){
var ns = AreaSeriesImpl.create();
ns.setTranslucent(true);
ns.getLineAttributes().setColor(ColorDefinitionImpl.TRANSPARENT())


}

Create a new series definition and set the appropriate query and series type.

var sdNew = SeriesDefinitionImpl.create();
sdNew.getSeriesPalette( ).shift( 0 );
ns.getLabel().setVisible(true);
var qry = QueryImpl.create("row[\"QUANTITYORDERED\"]" );
ns.getDataDefinition().add(qry)
sdNew.getSeries().add( ns );

Assign the new series definition to the second Y-Axis.

yAxis2.getSeriesDefinitions().add( sdNew );

The example report also has a Boolean parameter that determines if the chart should be displayed as a study chart.


if( params["StudyChart"].value == true ){
mychart.setStudyLayout(true);
}else{
mychart.setStudyLayout(false);
}

Some of the output options for this example report are displayed below.


This report is available at Birt-Exchange.

Thursday, August 13, 2009

Calling BIRT reports from PHP using Actuate’s JSAPI

I have written a couple of post on using Actuate’s JSAPI to include BIRT content within your system. This API is AJAX based and allows content to be embedded within most web applications.

These post can read here:
Calling BIRT Reports from ASP.NET
and
Actuate's JSAPI

In addition take a look at calling the BIRT Engine from PHP, which I blogged about a while back.

Calling the JSAPI from PHP is not much different than any other front end. For example, including the interactive viewer can be done using the following code.



<?php
$JSAPI_Location='http://localhost:8080/ActuateJavaComponent/jsapi';
$ReportFile='/Public/BIRT and BIRT Report Studio Examples/Customer Dashboard.rptdesign';
?>
<HTML>
<head>
</head>
<body>

<div id="jsapi_example_container">
<script type="text/javascript" src="<?php echo $JSAPI_Location ?>"></script>
<script type="text/javascript" language="JavaScript">
<!-- Load the viewer component of the Actuate object -->
actuate.load("viewer");
actuate.initialize("<?php echo substr($JSAPI_Location, 0, -5) ?>",
null,
null,
null,
initViewer);
var viewer;
function initViewer()
{
viewer = new actuate.Viewer("acviewer");
<!-- register an event handler to catch exceptions -->
viewer.registerEventHandler(actuate.viewer.EventConstants.ON_EXCEPTION, errorHandler);
run();
}
function run()
{

viewer.setReportName("<?php echo $ReportFile ?>");
viewer.submit();
}
function errorHandler(viewInstance, exception)
{
alert(exception.getMessage());
}
</script>
<div id="acviewer"></div>
</div>
</body>
</HTML>


In this example I am setting the JSAPI location and report file to run using a couple of PHP variables. The JSAPI is initialized and the callback function initViewer is called which creates the viewer within the acviewer div element. The initViewer function then calls the run method which sets the report to run and executes it. The report is then displayed within the acviewer div element.



If the report has parameters you can use the parameter component of the JSAPI or set them using PHP variables.

Change the code above to add the parameter values.

<?PHP
$JSAPI_Location='http://localhost:8080/ActuateJavaComponent/jsapi';
$ReportFile='/Public/BIRT and BIRT Report Studio Examples/Sales By Territory.rptdesign';
$reportTerritory='NA';
$reportYear=2004;
?>

Next, within the run method set the parameters using code similar to the following.

function run()
{
viewer.setParameters(
{
"Territory": "<?php echo $reportTerritory ?>",
"currentYear": <?php echo $reportYear ?>
}
);
viewer.setReportName("<?php echo $ReportFile ?>");
viewer.submit();
}

To use the parameter component use code similar to the following.

<html>
<body>
<?php
$JSAPI_Location='http://localhost:8080/ActuateJavaComponent/jsapi';
$ReportFile='/Public/BIRT and BIRT Report Studio Examples/DateTest.rptdesign';
?>


<div id="jsapi_example_container">
<script type="text/javascript" src="<?php echo $JSAPI_Location ?>"></script>
<script type="text/javascript" language="JavaScript">
var paramObj;
var viewer;
actuate.load("viewer");
actuate.initialize("<?php echo substr($JSAPI_Location, 0, -5) ?>",
null,
null,
null,
init);

function init()
{
<!-- parameter -->
paramObj = new actuate.Parameter("parampane");
<!-- register an event handler to catch exceptions -->
paramObj.registerEventHandler(actuate.parameter.EventConstants.ON_EXCEPTION, parameterErrorHandler);
<!-- viewer -->
viewer = new actuate.Viewer("viewerpane");
var viewerwidth = 800;
var viewerheight = 600;
<!-- register an event handler to catch exceptions -->
viewer.registerEventHandler(actuate.viewer.EventConstants.ON_EXCEPTION, errorHandler);
viewer.setWidth(viewerwidth);
viewer.setHeight(viewerheight);
runParam();
}

function runParam()
{
paramObj.setReportName("<?php echo $ReportFile ?>");
paramObj.submit(function () {document.getElementById("run").style.visibility = 'visible';});
}

function run()
{
paramObj.downloadParameterValues(runNext);
}

function runNext( pvs )
{
viewer.setParameterValues(pvs);
viewer.setReportName("<?php echo $ReportFile ?>");
viewer.submit();
}

function errorHandler(viewInstance, exception)
{
alert(exception.getMessage());
}

function parameterErrorHandler(exception)
{
alert(exception.getMessage());
}

</script>
<table border=1>
<tr>
<td><div id="parampane"></div></td>
<td><input type="button" class="btn" id="run" value="Run Report" onclick="run()" style="visibility: hidden"></td>
</tr>
</table>
<div id="viewerpane"></div>
</div>
</body>
</html>


The run function downloads the parameters that were set using the parameter component. Next the setParameterValues method is used to set the parameter values for the run of the report. Notice this example has a date parameter, and the JSAPI provides a date picker for this type of parameter.





If you are already using some PHP class to implement a date picker, such as the one available at the Zebra PHP Components Framework blog, you can modify your code to retrieve the date from the id of the element the date is stored in. The above example would have the following modifications:

<?php
// include the class
require "class.datepicker.php";
// instantiate the object
$dp=new datepicker();
$dp->dateFormat="m/d/Y";

$JSAPI_Location='http://localhost:8080/ActuateJavaComponent/jsapi';
$ReportFile='/Public/BIRT and BIRT Report Studio Examples/DateTest.rptdesign';
?>

function runNext( pvs )
{
//viewer.setParameterValues(pvs);
viewer.setParameters({"currentYear":2003, "currentMonth":12, "DateParameter":document.getElementById("date").value});

viewer.setReportName("");
viewer.submit();
}


<table border=1>
<tr>
<td><input type="text" id="date"></td>
<td><input type="button" value="Select Date" onclick="<?=$dp->show("date")?>"></td>
</tr>

<tr>
<td><div id="parampane"></div></td>
<td><input type="button" class="btn" id="run" value="Run Report" onclick="run()" style="visibility: hidden"></td>
</tr>
</table>
<div id="viewerpane"></div>



Thursday, July 09, 2009

The Heavy Hand

Today I received a foundation email with this little nugget in it:

Pursuant to the Eclipse Foundation Bylaws, the Eclipse Foundation recently amended the Eclipse Foundation Bylaws and Membership Agreement. As required by the Eclipse Foundation Membership Agreement, we require your formal acceptance of these changes which became effective July 23, 2008 and are more fully described at: http://www.eclipse.org/membership/vote2008/.

Formal acceptance is required by virtue of the Membership Agreement. It should be noted that these changes are not optional and apply to all Eclipse Members upon Membership renewal from July 23, 2008 onwards.  (emphasis added)
Now I have no problem with the changes to the by laws.  In fact, they are great for my small company.  What I don't understand is why I need to agree in writing to these non-optional requirements?

If the decision has all ready been made, why do I need to agree in writing?  Maybe it is just me, but it feels heavy handed (and pointless) to insist that I validate a decision after it has all ready been made.

After all, if I don't like the decision I can just choose not to renew.