The way that I typically look at Emitters is that they are a mechanism for getting BIRT report output to some output mechanism. That output can be a file, a stream, and an IPC listener, whatever. This gives BIRT the ability to serve as more than just a Report Engine, but possibly as a middleware also. Why would you do this? Despite the added bloat, it allows you take advantage of the BIRT internal mechanisms for sorting, aggregating, formatting, and a filtering data. Of course, more often than not, you will just use an Emitter to output file formats.
So how do emitters work? An emitter is an Eclipse Plug-in, and when writing one, you need to set up as an Eclipse Plug-In project. Since it is an Eclipse Plug-in, it requires that you set up the proper entries in the plug-in.xml and Manifest.MF files. This can be a bit tedious to do, and in past experiences required a bit of trial and error on my part.
Since it is an Eclipse Plugin, there are two classes that need to be created. The Activator, which is usually automatically created when you create a new plug-in project, and the actual emitter. The Activator extends the org.eclipse.core.runtime.Plugin class. The code for this is automatically generated upon project creation. You will only need to be sure that the plug-in.xml file is pointing to the correct Activator.
The Emitter class itself is an extension of the org.eclipse.birt.report.engine.emitter.ContentEmitterAdapter class. This is where all the magic happens. The emitter class will simply implement certain methods based on the requirements of the emitter. In the following example emitter code, I wrote an emitter that will generate a very generic XML file using JaxB.
package com. .birt.emitter;
import java.io.OutputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import org.eclipse.birt.report.engine.content.IBandContent;
import org.eclipse.birt.report.engine.content.IReportContent;
import org.eclipse.birt.report.engine.content.IRowContent;
import org.eclipse.birt.report.engine.content.ITextContent;
import org.eclipse.birt.report.engine.emitter.ContentEmitterAdapter;
import org.eclipse.birt.report.engine.emitter.IEmitterServices;
import com. .birt.emitter.xml.ObjectFactory;
import com. .birt.emitter.xml.Root;
import com. .birt.emitter.xml.Root.Office;
import com. .birt.emitter.xml.Root.Office.Employees;
public class XMLEmitter extends ContentEmitterAdapter {
private ObjectFactory xmlObjectFactory;
private Root xml;
private Office currentOffice;
private OutputStream reportOutputStream;
@Override
public void initialize(IEmitterServices service) {
super.initialize(service);
//initialize my object factory for the XML file and create a root element in the XML file
this.xmlObjectFactory = new ObjectFactory();
this.xml = xmlObjectFactory.createRoot();
//create an OutputStream to output to the console
reportOutputStream = service.getRenderOption().getOutputStream();
}
@Override
public void startRow(IRowContent row) {
super.startRow(row);
//When we encounter a new row, and it is a HEADER for a group, we need
//to create a new office element
if (row.getBand().getBandType() == IBandContent.BAND_GROUP_HEADER)
{
this.currentOffice = xmlObjectFactory.createRootOffice();
}
}
@Override
public void endRow(IRowContent row) {
super.endRow(row);
//Once we encounter the end row and this is a HEADER row, we need to add
//this to our XML structure under the Office sections
if (row.getBand().getBandType() == IBandContent.BAND_GROUP_HEADER)
{
xml.getOffice().add(currentOffice);
}
}
@Override
public void end(IReportContent report) {
super.end(report);
//At the end of our report generation, create a new JaxB Marshaller object, and output the
//formatted output to the console
try {
JAXBContext jaxContext = JAXBContext.newInstance("com. .birt.emitter.xml", Root.class.getClassLoader()) ;
Marshaller xmlOutputWriter = jaxContext.createMarshaller();
xmlOutputWriter.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
xmlOutputWriter.marshal(xml, reportOutputStream);
} catch (PropertyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void startText(ITextContent text) {
super.startText(text);
//If this is a new text element (data in a row), then we need
//to go ahead and add this as our current office name if this is a header.
//If this is not a header, then we know it is employee information, and we
//need to add this to our list of office employees
IRowContent row = (IRowContent)text.getParent().getParent();
if (row.getBand().getBandType() == IBandContent.BAND_GROUP_HEADER)
{
currentOffice.setName(text.getText());
}
else
{
Employees currentEmployee = xmlObjectFactory.createRootOfficeEmployees();
currentEmployee.setName(text.getText());
currentOffice.getEmployees().add(currentEmployee);
}
}
}
You will notice it uses a very SAX type of processing, where each element gets a Start and End method. Each of these element equates to a type of designer element. In the above example, we are only looking for new rows, and new text elements. This emitter makes the following assumptions:
-That the report design file is a single table
-That the table is grouped by an Office ID
-That the data element in the Detail row only contains the Employees name.
So in other words, this particular emitter is not a general purpose emitter, it is designed with a specific report design file in mind.
Setting up the Emitter is another task in itself. I used the Eclipse 3.3 Plug-In configuration editor to set mine up, however, you can manually edit yours by hand. The first thing I did was to configure the general purpose things, such as name and ID, and maek sure the activator was correct.
Figure 1. Emitter configuration
Next I need to configure the Dependencies. In this case, the BIRT Report Engine and the Eclipse Core.
Figure 2. Dependencies
Next, I specified the packages to export during build needed for Runtime. I specified the 3 packages I need to export in my Emitter, the package with the Activator, the package with the emitter itself, and the package with the JaxB generated classes. In the classpath, I specify the jars I need for JaxB to work properly.
Figure 3. Runtime Exported Classes and Jars
Next, I specify how to configure my extensions. I create a new emitter extension, specify the class to use, the format, which is important when I specify the output format for BIRT to use, I will use this string. I specify a generic MIME type to use, and specify an ID, in which I used my package name. I also specify no-pagination, which is important if you are building a BIRT emitter that will support multiple pages, such as the PDF emitter. This will influence the behavior of the document generator inside of BIRT, and will add more legwork to the emitter.
Figure 4. Extension configuration
That’s pretty much it. Now, when I want to test this, I right-mouse click on my project and specify Export, and Deployable Plug-Ins and Fragments. I usually export to my BIRT Runtime folder for testing, and will write a few Unit Tests to test the execution of the emitter. Below is an example of the output I get from my emitter.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Root xmlns="http://www.example.org/OfficeLayout">
<Office>
<name>1</name>
<Employees>
<name>Murphy, Diane</name>
</Employees>
<Employees>
<name>Patterson, Mary</name>
</Employees>
<Employees>
<name>Firrelli, Jeff</name>
</Employees>
<Employees>
<name>Bow, Anthony</name>
</Employees>
<Employees>
<name>Jennings, Leslie</name>
</Employees>
<Employees>
<name>Thompson, Leslie</name>
</Employees>
</Office>
<Office>
<name>2</name>
<Employees>
<name>Firrelli, Julie</name>
</Employees>
<Employees>
<name>Patterson, Steve</name>
</Employees>
</Office>
<Office>
<name>3</name>
<Employees>
<name>Tseng, Foon Yue</name>
</Employees>
<Employees>
<name>Vanauf, George</name>
</Employees>
</Office>
<Office>
<name>4</name>
<Employees>
<name>Bondur, Gerard</name>
</Employees>
<Employees>
<name>Bondur, Loui</name>
</Employees>
<Employees>
<name>Hernandez, Gerard</name>
</Employees>
<Employees>
<name>Castillo, Pamela</name>
</Employees>
<Employees>
<name>Gerard, Martin</name>
</Employees>
</Office>
<Office>
<name>5</name>
<Employees>
<name>Nishi, Mami</name>
</Employees>
<Employees>
<name>Kato, Yoshimi</name>
</Employees>
</Office>
<Office>
<name>6</name>
<Employees>
<name>Patterson, William</name>
</Employees>
<Employees>
<name>Fixter, Andy</name>
</Employees>
<Employees>
<name>Marsh, Peter</name>
</Employees>
<Employees>
<name>King, Tom</name>
</Employees>
</Office>
<Office>
<name>7</name>
<Employees>
<name>Bott, Larry</name>
</Employees>
<Employees>
<name>Jones, Barry</name>
</Employees>
</Office>
</Root>