Die BIRT Report Engine bietet Report-Rendering-Erweiterungen, die einen Business-Intelligence-Bericht in HTML, PDF, XLS, PostScript, Microsoft Word und PowerPoint ausgeben. In BIRT 2.2.1 unterstützt die BIRT-Report-Rendering-Erweiterungs-API das Rendern eines Reports in einem anwenderspezifischen Format, wie es XML ermöglicht. Im Folgenden wird die beispielhafte Implementierung einer maßgeschneiderten XML-Report-Rendering-Erweiterung: org.eclipse.birt.report.engine.emitter.xml beschrieben. Mit dem Code können Anwender ein Plug-in entwickeln, das die Daten eines Berichts in einem bestimmten Dateiformat aufbereitet. Eine Rendering-Erweiterung ergänzt das BIRT Report Engine Framework um einen Emitter, indem ein so genannter Extension Point implementiert wird: org.eclipse.birt.report.engine.emitters. Die XML-Schema-Datei org.eclipse.birt.report.engine/schema/emitters.exsd beschreibt diesen Extension Point. Dadurch kann ein neues Ausgabeformat in der Präsentations-Engine unterstützt werden. Das BIRT-Plug-in-Registry nutzt den Extension Point, um alle unterstützten Ausgabeformate zu erkennen, die für die Report-Engine-Umgebung definiert wurden.
XML-Report-Rendering-Erweiterung
Die Muster-XML-Report-Rendering-Erweiterung ist ein Plug-in, das BIRT-Berichtdaten in ein XML-Format exportieren kann (Abb. 1). Reportentwickler rendern BIRT-Berichtdaten in XML, damit sie die Inhalte in anderen Anwendungen wieder verwenden und mit anderen Nutzern austauschen können. Die Muster-XML-Report-Rendering-Erweiterung hat die folgenden Funktionen:- Sie exportiert BIRT-Reportdaten in ein XML-Format: Das XML-Report-Rendering-Plug-in rendert jedes Berichtelement und schreibt es in die Ausgabedatei: report_name>.xml.
- Sie definiert eine Public API, damit die BIRT-Berichte im XML-Format gerendert werden können: Das Plug-in erweitert die im org.eclipse.birt.report.engine.emitter Extension Point bestimmte Funktionalität mit org.eclipse.birt.report.engine.
- Nutzer können ein XML-Schema für eine Formatierungsausgabe bestimmen: Während des Rendering-Prozesses verarbeitet das Muster-Plug-in alle Elemente im Berichtdesign, indem es XML-Properties und damit verbundene Daten in die Ausgabedatei exportiert. Das Plug-in unterstützt die Anordnung der Reportelemente in einem XML-Schema. Damit erhalten Entwickler zusätzliche Formatierungsmöglichkeiten für ihre Ausgabe. Das Plug-in definiert diese Mappings in der Property-Datei <report_name>.xmlemitter. Das Plug-in liest die Property-Datei während der Laufzeit und lädt die Custom-Tags.
XML-Report-Rendering-Erweiterung
Ein neues Plug-in-Projekt für die XML-Report-Rendering-Erweiterung wird mit der Eclipse PDE erstellt. Folgende Aufgaben müssen abgearbeitet werden, um ein XML-Report-Rendering-Plug-in-Projekt zu entwickeln:- Aus dem Eclipse-PDE-Menü File | New | Project auswählen
- Unter New Project das Plug-in Project auswählen und Next anklicken
- Unter Plug-in Project die Einstellungen wie in Tabelle 1 verändern
- Im Feld Plug-in Content die Einstellungen wie in Tabelle 2 verändern
| Ebene | Auswahl | Wert |
|---|---|---|
| Plug-in Project | Project Name | Org.eclipse.birt.report.engine.emitter.xml |
| Use Default Location | Selected | |
| Location | Nicht verfügbar, wenn die Default Location ausgewählt wurde | |
| Project Settings | Create a Java Project | Selected |
| Source Folder | src | |
| Output Folder | bin | |
| Target Platform | Eclipse Version | 3.3 |
| An OSGi Framework | Not selected |
| Ebene | Auswahl | Wert |
|---|---|---|
| Plug-in Properties | Plug-in ID | org.eclipse.birt.report.engine.emitter.xml |
| Plug-in Version | 1.0.0 | |
| Plug-in Name | BIRT XML Emitter | |
| Plug-in Provider | Unternehmens-URL eingeben oder freilassen | |
| Classpath | xmlEmitter.jar eingeben oder freilassen | |
| Plug-in Options | Generate an activator, a Java class that controls the plug-in’s life cycle. | Selected |
| Activator | org.eclipse.birt.report.engine.emitter.xml .XmlPlugin |
|
| This plug-in will make contributions to the UI | Not selected | |
| Rich Client Application | Would you like to create a rich client application? | No |
Tab. 2: Werte in Plug-in-Projektfeldern verändern
Definition von Abhängigkeiten
Entwickler müssen das org.eclipse.birt.report.engine-Plug-in bestimmen, um das XML-Report-Rendering-Beispiel zusammenzustellen und durchzuspielen. Das Plug-in muss im Classpath der XML-Rendering-Erweiterung vorhanden sein.
Benennung des Emitter-Erweiterungspunkts
Um die XML-Report-Rendering-Erweiterung zu implementieren, werden der org.eclipse.birt.report.engine.emitters-Erweiterungspunkt bestimmt und die Details der Erweiterungselemente ergänzt. Schritte zur Bestimmung des Erweiterungspunkts (Tab. 3):
- Im PDE Manifest Editor Extensions auswählen
- In All Extensions Add anklicken. Daraufhin erscheint unter New Extension die Extension Point Selection.
- Bei den verfügbaren Erweiterungspunkten das folgende Plug-in auswählen: org.eclipse.birt.report.engine.emitters. Danach Finish anklicken.
- In der Auswahl All Extensions den Erweiterungspunkt org.eclipse.birt.report.engine.emitters anklicken und das Erweiterungselement Emitter auswählen.
- Unter Extension Element Details die Eigenschaften für das Emitter-Erweiterungselement bestimmen. In Tabelle 3 wird dies genauer beschrieben.
| Merkmal | Wert |
|---|---|
| Class | Org.eclipse.birt.report.engine.emitter.xml.XMLReportEmitter |
| Format | XML |
| MimeType | XML |
| ID | Org.eclipse.birt.report.engine.emitter.xml |
Tab. 3: Property-Werte für das Emitter-Erweiterungselement bestimmen
Die Muster-XML-Report-Rendering-Erweiterung verstehen
Die XML-Report-Rendering-Erweiterung erweitert die Report-Emitter-Schnittstellen und XML-Writer in org.eclipse.birt.report.engine.emitter. Das Extension-Muster erstellt die XML-Ausgabedatei in dem gleichen Ordner, in dem sich auch der exportierte Bericht befindet. Der Dateiname für die Ausgabe gleicht der Bezeichnung für den Bericht mit einer XML-Erweiterung. Die Fehlerüberprüfung beim Erweiterungsbeispiel ist jedoch nur eingeschränkt möglich. Der folgende Abschnitt beschreibt die codebasierte Erweiterung allgemein. Sie erklärt Entwicklern, wie eine XML-Report-Rendering-Erweiterung entwickelt werden muss, nachdem sie das Plug-in-Framework in der Eclipse PDE definiert haben.
Erklärung des XML-Report-Rendering-Erweiterungspakets
Das Implementierungspaket für das XML-Report-Rendering-Extension-Beispiel – org.eclipse.birt.report.engine.emitter.xml – enthält die folgenden Klassen:
- XMLPlugin: Plug-in-Runtime-Klasse für das beschriebene Beispiel.
- XMLReportEmitter: Steuert Anfang und Ende des Prozesses, der den Reportcontainer rendert.
- XMLRenderOption: Integriert das Plug-in in die BIRT Report Engine und bestimmt die Informationen für die Konfiguration, unter anderem das XML-Ausgabeformat.
- XMLTags.java: Definiert die Controls und verknüpfte Property-Listen, die für das Schreiben einer XML-Datei genutzt werden.
- XMLFileWriter: Schreibt die XML-Version, Text, Abbildung, Daten, Bezeichnung und Report-Tag-Inhalt des Berichts und gibt sie an die XML-Ausgabedatei weiter.
- LoadExportSchema: Lädt die XML-Schema-Datei – wenn vorhanden – und ersetzt damit die vorgegebenen Werte. Diese Werte bestimmen die XML-Version, Text, Abbildungen, Daten, Bezeichnungen und Report-Tags. Eine Accessor-Methode für jeden Tag sendet den Wert an XMLReportEmitter für die Ausgabe in der Exportdatei zurück.
XMLReportEmitters
Der XMLReportEmitter schreibt die Berichtsinhalte in eine XML-Datei und erzeugt die Writer- und Emitter-Objekte. Außerdem steuert der XMLReportEmitter den Start und das Ende des Prozesses, bei dem der Reportcontainer gerendert wird. Danach exportiert er die XML-Version sowie -Text, -Abbildungen, -Daten, -Bezeichnung und -Report-Tag-Inhalt des Berichts in die XML-Ausgabedatei. Der XMLReportEmitter implementiert folgende Methoden:
- Der XMLReportEmitter erzeugt die XML-Report-Emitter-Klasse als org.eclipse.birt.report.engine.presentation. Das ContentEmitterVisitor-Objekt übernimmt die Emitter-Abläufe.
- Der initialize()-Befehl startet die folgenden Abläufe, die notwendig sind, um einen Output-Datenstrom zu erstellen, der die Berichtsinhalte in die XML-Datei schreibt (Listing 1): Erweiterung der IEmitterServices-Schnittstelle um eine Referenz, Erzeugung der Datei- und Output-Datenstrom-Objekte und Nutzen der dafür definierten Einstellungen sowie Erzeugung des XMLWriter-Objekts.
- Der start()-Befehl führt folgenden Aufgaben aus: Es wird eine Referenz für die IReportContent-Schnittstelle erstellt. Sie enthält Accessor-Methoden, die die Schnittstellen zu den Emittern für die Reportinhalte transportiert. Das Start-Emitter-Logging-Level wird bestimmt und diese Informationen an die Logdatei gesendet. Wenn es eine optionale XML-Schema-Datei gibt, wird diese für den Bericht lokalisiert, außerdem wird ein LoadExportSchema-Objekt erzeugt, um die XML-Schema-Datei zu lesen. Und der Befehl öffnet die Ausgabedatei und definiert das Kodierungsschema UTF-8.
- Startet den XML-Writer.
- Schreibt den Start-Tag, der wiederum den <xml>-Tag für die Ausgabedatei genauer bestimmt. Dazu gehören auch die Version und das Kodierungsschema.
- Schreibt den <report>-Tag, der Reportname und andere Eigenschaften in der Report-Property-Liste der Ausgabedatei spezifiziert.
- Der end()-Befehl löst die folgenden Prozesse aus und gleicht damit der CSV-Rendering-Erweiterung: Er bestimmt den endgültigen Report-Logging-Level und schreibt an die Logdatei. Er beendet den Schreibprozess, schließt den XML-Writer und die Ausgabedatei.
String fileName =
report.getDesign().getReportDesign().getFileName();
int pos = fileName.indexOf("/");
String fn = fileName.substring(pos+1,fileName.length());
fileName = fn;
if (fileName.length() > 0) {
pos = fileName.lastIndexOf(".");
if ( pos > 0 )
fileName = fileName.substring(0, pos);
fileName = fileName + ".xmlemitter";
pos = fileName.lastIndexOf("/");
String propFileName =
fileName.substring( pos+1 , fileName.length() );
String resourceFolder =
report.getDesign().getReportDesign()
.getResourceFolder();
if ( fileExists(resourceFolder + "/"
+ propFileName))
exportSchema = new LoadExportSchema
resourceFolder + "/" + propFileName );
else
if ( fileExists(fileName))
exportSchema =
new LoadExportSchema( fileName );
else exportSchema = new LoadExportSchema( "" );
}
this.report = report;
writer.open( out, "UTF-8" );
writer.startWriter( );
writer.closeTag( exportSchema.getExportStartTag());
writer.closeTag( XMLTags.TAG_CR );
String rp = exportSchema.getExportReportTag();
for (int i = 0;i < XMLTags.rPropList.length;i++)
{
if (exportSchema.isPropertyRequired(
XMLTags.rPropList[i], rp))
{
String propValue = getReportPropValue(i,report);
rp = replaceTag( rp, "??"
+XMLTags.rPropList[i], propValue );
}
}
writer.writeCode( rp );
writer.closeTag( XMLTags.TAG_CR );
XMLReportEmitter-Methoden
Die XMLReportEmitter-Klasse definiert folgende zusätzliche Methode, die in unterschiedlichen Phasen der Reportgenerierung aufgerufen werden. Sie ermöglichen den Zugriff auf den Reportcontainer, Seiten, Tabellen, Zeilen, Zellen, Text, Bezeichnungen, Daten, Abbildungen, Hyperlinks und andere Inhalte. Das folgende Beispiel zeigt, wie ein Label entwickelt und bearbeitet wird.
- startLabel( ) (Listing 2) initiiert folgende Prozesse: Der Aufruf des LoadExportSchema.getExportLabelTag() erzeugt das Muster für den <label>-Tag, das in der <report_name>.xmlemitter-Property-Datei genauer bestimmt wird. Existiert keine Property-Datei, nutzt das Plug-in das folgende vorgegebene Muster, das in der LoadExportSchema-Klassen bestimmt wird: <label>??value</label>. startLabel( ) wiederholt den Vorgang durch die folgende Liste von Label-Eigenschaften, die in den XML-Tags definiert wird. Damit werden die für den Bericht notwendigen Eigenschaften festgelegt:
static String[] lPropList = {"Bookmark","Height","Hyperlink","InlineStyle", "Name","TOC","Width","X","Y" };Des Weiteren wird getLabelPropValue() aufgerufen, um jeden notwendigen Eigenschaftswert abzufragen und den Wert im <label>-Tag-Ausdruck zu ersetzen. Sodann wird startText() und XMLFileWriter.closeTag( ) aufgerufen, um den <label>-Tag in die Ausgabedatei zu übertragen.
public void startLabel( ILabelContent label ) { String lbl = exportSchema.getExportLabelTag( ); int len = XMLTags.lPropList.length; for (int i = 0;i < XMLTags.lPropList.length;i++) { if (exportSchema.isPropertyRequired( XMLTags.lPropList[i], lbl)) { String propValue = getLabelPropValue(i,label); lbl = replaceTag( lbl, "??"+XMLTags.lPropList[i], propValue ); } } startText( label, lbl ); writer.closeTag( XMLTags.TAG_CR ); } - startText( ) (Listing 3) initiiert folgende Abläufe: Bestimmt wird das Start-Text-Logging-Level und die Daten werden an die Log-Datei weitergeleitet. ITextContent.getText() wird genutzt, um den Label-Bezeichner abzurufen. Zudem wird der <label>-Tag in die Ausgabedatei geschrieben.
public void startText( ITextContent text, String exportTag ) { logger.log( Level.FINE, "[XMLReportEmitter] Start text" ); String textValue = text.getText( ); writer.writeCode( replaceTag( exportTag, XMLTags.valueTag, textValue ) ); } - getLabelPropValue() (Listing 4) löst die folgenden Prozesse aus: Aufruf der geeigneten IContentAccessor-Methode, um den Property-Wert abzurufen sowie Rücksendung des Werts, um startLabel( ) aufzurufen und damit den Wert innerhalb des <label>-Tags zu ersetzen. Zudem wird die XML-Ausgabedatei erstellt.
private String getLabelPropValue( int property, ILabelContent label) { String propValue; switch (property) { case 0: // "Bookmark": propValue = label.getBookmark( ); break; case 1: // "Height": if ( label.getHeight( ) != null ) propValue = label.getHeight().toString( ); else propValue = ""; break; case 2: //"Hyperlink": if ( label.getHyperlinkAction( ) != null ) propValue = label.getHyperlinkAction( ).getHyperlink( ); else propValue = ""; break; ... case 8: //"Y": if ( label.getY( ) != null ) propValue = label.getY( ).toString( ); else propValue = ""; break; default: propValue = ""; break; } if ( propValue == null ) propValue = ""; return propValue; }
XMLTags
Die XMLTags-Klasse definiert die Controls und die damit verbundenen Property-Listen, die für die Analyse der Reportinhalte genutzt werden. Gleichzeitig werden die Tag-Werte ersetzt und der Tag in der XML-Ausgabedatei erstellt. Listing 5 zeigt den Code für die Entwicklung der XMLTags-Klasse.
public class XMLTags
{
public static final String TAG_CR = "\n" ;
static String valueTag = "??value";
static String labelControl = "label";
static String textControl = "text";
static String imageControl = "image";
static String dataControl = "data";
static String reportControl = "report";
static String startControl = "start";
static String endControl = "end";
static String[] iPropList =
{"Bookmark","Height","Hyperlink","ImageMap",
"InlineStyle","MIMEType","Name","Style","TOC","URI",
"Width","X","Y"};
static String[] dPropList =
{"Bookmark","Height","Hyperlink","InlineStyle","Name",
"Style","TOC","Width","X","Y"};
static String[] lPropList =
{"Bookmark","Height","Hyperlink","InlineStyle","Name",
"TOC","Width","X","Y" };
static String[] tPropList =
{"Bookmark","Height","Hyperlink","InlineStyle","Name",
"Style","Text","TOC","Width","X","Y"}
static String[] rPropList =
{"TotalPages", "TOCTree", "Name"};
}
XMLFileWriter
Die XMLFileWriter-Klasse erstellt den abschließenden Tag. Die Entwicklung dieser Klasse zeigt Listing 6.
package org.eclipse.birt.report.engine.emitter.xml;
import org.eclipse.birt.report.engine.emitter.XMLWriter;
public class XMLFileWriter extends XMLWriter {
public XMLFileWriter( )
{
}
public void writeCode( String code )
{
super.printWriter.print( code );
}
public void startWriter( )
{
}
public void closeTag( String tagName )
{
super.printWriter.print( tagName );
}
}
XMLRenderOption
Die org.eclipse.birt.report.engine.emitter.xml.XMLRenderOption-Klasse ergänzt die BIRT Report Engine Runtime um die XML-Rendering-Option (Listing 7).
package org.eclipse.birt.report.engine.emitter.xml;
import org.eclipse.birt.report.engine.api.RenderOption;
public class XMLRenderOption extends RenderOption{
public static final String XML = "XML";
protected String configPath= "";
public XMLRenderOption( ) {
}
public void setExportConfigFile( String config )
{
this.configPath = config;
}
public String getExportConfigFile()
{
return configPath;
}
}
LoadExportSchema
Die org.eclipse.birt.report.engine.emitter.xml.LoadExportSchema-Klasse lädt optional ein XML-Schema. Dafür führt die Klasse folgende Schritte durch (Listing 8): Sie bestimmt die vorgegebenen Ersatzmuster für die XML-Tags, ruft die readSchemaFile()-Methode auf und ordnet jedem Tag eine Accessor-Methode zu, die einen Wert an den XMLReportEmitter zurücksendet, um diesen in der Exportdatei auszugeben.
package org.eclipse.birt.report.engine.emitter.xml;
...
public class LoadExportSchema{
protected String fileName = "";
protected String startTag = "";
protected String textTag = "??value ";
protected String imageTag = "??value ";
protected String dataTag = "??value";
protected String labelTag = "";
protected String endTag = "";
protected String reportTag = "";
public LoadExportSchema(String fileName)
{
if ( fileName.length() > 0 )
{
this.fileName = fileName;
readSchemaFile();
}
}
Die readSchemaFile()-Methode (Listing 9) liest die XML-Schema-Datei. Dabei geht sie jeweils Zeile für Zeile durch und ersetzt die vorgegebenen Werte für die Muster einer XML-Version sowie von Text, Abbildung, Datei, Label und Report-Tags durch die Werte, die in der XML-Schema-Datei dafür vorgesehen sind.
private void readSchemaFile()
{
BufferedReader input = null;
try
{
input = new BufferedReader(
new FileReader(fileName) );
String line = null; //not declared within while loop
while (( line = input.readLine()) != null){
int pos = line.indexOf("=");
if ( pos > 0 )
{
String index = line.substring(0, pos );
String indexTag = line.substring(pos + 1,
line.length());
if ( index.equalsIgnoreCase(
XMLTags.labelControl ) )
{
labelTag = indexTag;
}
if ( index.equalsIgnoreCase( XMLTags.imageControl ) )
{
imageTag = indexTag;
}
if ( index.equalsIgnoreCase( XMLTags.dataControl ) )
{
dataTag = indexTag;
}
if ( index.equalsIgnoreCase( XMLTags.startControl ) )
{
startTag = indexTag;
}
if ( index.equalsIgnoreCase( XMLTags.endControl ) )
{
endTag = indexTag;
}
if ( index.equalsIgnoreCase( XMLTags.reportControl ) )
{
reportTag = indexTag;
}
}
catch (FileNotFoundException ex)
{
ex.printStackTrace( );
}
catch (IOException ex)
{
ex.printStackTrace( );
}
finally
{
try
{
if (input!= null)
{
input.close( );
}
}
catch (IOException ex)
{
ex.printStackTrace( );
}
}
}
Listing 10 zeigt die Pattern-Werte für die XML-Version, Text, Abbildung, Daten, Label und Report-Tags, die für die XML-Schema-Datei definiert sind.
start=<?xml version="1.0" encoding="UTF-8"?> report=<report name=??name> label=<label name=??name hyperlink=??hyperlink>??value</label> text=<text name=??name>??value</text> image=<image name=??name>??val0ue</image> data=<data>??value</data> end=</report>
Test des XML-Report-Rendering-Plug-ins
Das mit dem dargestellten Code entwickelte XML-Report-Rendering-Plug-in kann in wenigen Schritten getestet werden. Dafür muss eine Java-Applikation erstellt werden, die ein Reportdesign in einer Installation der BIRT Runtime Engine ausführt. Dieser Prozess ähnelt dem Anwendungsbeispiel, bei dem eine CSV-Report-Rendering-Anwendung erstellt wurde. Für den Test eines XML-Report-Rendering-Plug-ins sind folgende Aufgaben zu erfüllen:
- Entwicklung des org.eclipse.birt.report.engine.emitter.xml-Plug-ins
- Deployment des Plug-Ins in der BIRT Runtime Engine Directory
- Launch einer Runtime-Instanz der Eclipse PDE
- Erstellung einer Java-Anwendung, die das Reportdesign ausführt und die Berichtdaten an eine XML-Datei sendet
- Erstellung eines Berichtdesigns mit einer Tabelle, die Inhalte einer Datenquelle und eines Datensatzes abbildet
- Ausführung der Anwendung und Kontrolle der XML-Ausgabedatei
Abbildung 2 zeigt das Reportdesign, das mit dem XML-Report-Rendering-Beispiel entwickelt wurde.
Listing 11 gibt einen Überblick über die Inhalte der XML-Ausgabedatei. Dazu gehören XML-Version, Text, Abbildungen, Daten, Label und Report-Tags in einem ausgeführten Bericht.
<?xml version="1.0" encoding="UTF-8"?> <report name= C:/IANA/2007/runtime-XMLEmitter/ExecuteXMLReport/reports/ xmlReport.rptdesign> <image name=> /9j/4AAQSkZJRgABAgEBLAEsAAD /4RVaRXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUA ... 7PMv9I9nVo5cj8b7MV9zB/gh8cf/2Q== </image> <label name= hyperlink=http://www.actuate.com>Company Name </label> <label name= hyperlink=>Report</label> <label name= hyperlink=>PRODUCTNAME</label> <label name= hyperlink=>QUANTITYINSTOCK</label> <label name= hyperlink=>MSRP</label> <data>1969 Harley Davidson Ultimate Chopper</data> <data>7933</data> <data>95.7</data> <data>1952 Alpine Renault 1300</data> <data>7305</data> <data>214.3</data> <data>1996 Moto Guzzi 1100i</data> <data>6625</data> <data>118.94</data> <data>2003 Harley-Davidson Eagle Drag Bike</data> <data>5582</data> <data>193.66</data> ... <data>American Airlines: MD-11S</data> <data>8820</data> <data>74.03</data> <data>Boeing X-32A JSF</data> <data>4857</data> <data>49.66</data> <data>Pont Yacht</data> <data>414</data> <data>54.6</data> </report>
In dem hier generierten Bericht beziehen sich die Inhalte beispielsweise auf klassische Automodelle und Oldtimer. Das in diesem Beitrag dargestellte Beispiel für die Entwicklung einer Eclipse-BIRT-XML-Report-Rendering-Erweiterung zeigt, dass Anwender durch die modulare Struktur von BIRT in nur wenigen Schritten ein individuelles Reportdesign erstellen können.
Weitere Informationen und Ressourcen erhalten Anwender unter [1]. Diese Community-Seite für Entwickler bietet zudem Downloads, Onlinedokumentationen, Foren und weitere Tools, mit denen Nutzer technische Tipps, Code und Ideen austauschen können.
Hamid A. Borna ist Leiter der Professional Services bei Actuate und mit seinem Team für alle kundenbezogenen Softwareimplementierungsprojekte bzw. Schulungsmaßnahmen im deutschsprachigen Raum verantwortlich. Borna verfügt über 11 Jahre Erfahrung in der IT (unter anderem bei Oracle) mit Datenbanken und Management Information Systems sowie im Projektmanagement.
Frank Michahelles ist Professional Services Consultant bei Actuate. Er verfügt über fundiertes Know-how in Implementierungs- und Customizing-Projekten und hat insbesondere große Erfahrung in den Bereichen Reporting und Performancemanagement.




