Have any questions?
+44 1234 567 890
Upgrade Guide for Bitnami ReportServer Instances
In this guide we describe the upgrade process from Bitnami ReportServer instances to a newer Bitnami ReportServer stack.
Overview
-
The following upgrade installation gives a step by step description of how to upgrade the Bitnami ReportServer Stack to either ReportServer 3.7.1 Community Edition or ReportServer 3.7.1 Enterprise Edition. Another versions can be upgraded in an analogous way. The upgrade consists of the following steps.
- Obtain the Bitnami ReportServer Enterprise (or Community) stack
- Stop the old ReportServer
- Create a complete backup of Reportserver data and a DB backup
- Install the new Bitnami ReportServer stack
- Delete the internal db of your ReportServer stack
- Import the backup data
- Restart ReportServer
- Check the logs
- Update the password of the new database
Before you start with upgrading the ReportServer Bitnami Stack, please make sure to have the following requirement met.
- You need to have a Bitnami Installation of Reportserver of version 3.0.3 or higher for this to work. In this example we upgrade 3.1.0 to version 3.7.1. Another versions can be upgraded in an analogous way.
Obtaining the Bitnami ReportServer stack
The first step is to download the updated Bitnami ReportServer Stack. You can find download links on our download page. Note that you can upgrade both to ReportServer Enterprise or to ReportServer Community. Once you have downloaded the installer, keep the file in a safe location and proceed with the next steps without starting the installation process. We'll do so in just a moment, but before that we need to make sure that the following precautions have been made for a succesful transfer of data.
- A complete DB-backup of your current bitnami reportserver metadata database including all relevant tables and data. Be sure you can return to the previous state in case something goes wrong.
- All relevant files of your current installation (any drivers you may use, reportserver.properties, web.xml if you changed the default one, etc). In the bitnami installation you can find the external configuration directory here:
- E.G. for standard installation: C:\Bitnami\reportserverenterprise-3.7.1.6048-1\apps\reportserver\reportserver-conf
The easiest and most complete backup is a complete SNAPSHOT of your current installation. Be sure you can return to the previous state in case something goes wrong.
Please make sure that you have access to a terminal (Linux) or cli (Windows command prompt) and that you have a user with administration rights.
Stop the old ReportServer
Once that we have exported all relevant data from the old instance, we can stop the old server. You can stop ReportServer by using the server monitor application that was installed with ReportServer. You find it in the installation directory. The manager application should be called manager-PLATFORM where platform could be linux-x64, windows or osx.
Create a complete backup of Reportserver data and a DB backup
To export all relevant data from your current ReportServer installation, open a Terminal or cli with administration rights and navigate to your current Bitnami ReportServer installation.
Something like this here
E.G.: C:\Bitnami\reportserverenterprise-3.7.1.6048-1\mariadb\bin>
You should look up your credentials for your internal database of your current ReportServer installation by checking the folder that contains this data.
E.G.: C:\Bitnami\reportserverenterprise-3.7.1.6048-1\apps\reportserver\reportserver-conf\persistence.properties>
As you have obtained the username and password for your internal db you now have the possibility to create a backup or so called dumpfile of your current database.
To create an actual dumpfile you need the the following command.
mysqldump -u username -ppassword source_database_name > path_to_dumpfile_on_hdd_or_ssd
- username = actual ReportServer db username
- password = actual ReportServer password for username
- source_database_name = the name of the database you want to export data from
- path_to_dumpfile_on_hdd_or_ssd = the full path of the dumpfile including the dumpfile itself with its *.sql suffix
Please note that for the password you need to write it without a space between the code tag -p and the actual password.
This is not allowed and prompts you to write down the password
mysqldump -u username -p password source_database_name > path_to_dumpfile_on_hdd_or_ssd
E.G.: mysqldump -u bn_reportserver -pa95f96a6d1 bitnami_reportserver > C:\BitnamiDBbackup\backup\bitnami_reportserver_backup.sql
After that it should appear in your specified folder and should contain a dumpfile that can be used to upgrade your new updated ReportServer later.
Install the new Bitnami ReportServer Stack
Now you need to uninstall your current bitnami Reportserver installation and need to install the latest version of Reportserver with your installer package from bitnami.
This guide demonstrates this with the aforementioned version 3.1.0 to 3.7.1.
Delete the internal db of your ReportServer Stack
As you have installed the new Bitnami ReportServer version succesfully, you can now begin to update your database.
For this step we need to delete the current iteration of the internal database. This will be done in order to make room for your backup that you just created in step 3 of the overview.
Please start by opening a command prompt or terminal and navigate to your installations bin folder.
Then you need to access your database via the username and password from step 3 with the following command.
mysql -u username -ppassword
E.G.: C:\Bitnami\reportserverenterprise-3.7.1.6048-1\mariadb\bin>mysql -u bn_reportserver -pa95f96a6d1
In this environment you can actually use sql code, which comes in handy when you need to make queries for checking out e.g. databases and database names, tables of databases etc. .
SQL is very powerful in that regard and helps you now.
MariaDB [(none)]>show databases;
After that we can basically see that we have multiple databases but just the bitnami_reportserver db is of interest to us.
Therefore we need to use the following command to delete the underlying db for the new reportserver.
mysql -u username -ppassword source_database_name < path_of_the_drop_ddl_sqls;
E.G.: C:\Bitnami\reportserverenterprise-3.7.1.6048-1\mariadb\bin>mysql -u bn_reportserver -pa95f96a6d1 bitnami_reportserver < C:\Bitnami\reportserverenterprise-3.7.1.6048-1\apache-tomcat\webapps\reportserver\ddl\reportserver-RS3.7.1-6048-schema-MySQL5_DROP.sql
By doing this we have finally made room for our backup that will take place in the next step. Thanks to the empty set and the command
show tables in bitnami_reportserver;
We can be sure that the dbs is empty and contains no further tables that could interfere with our backup.
Import the backup data
Once that we have exported all relevant data from the old instance, we can stop the old server. You can stop ReportServer by using the server monitor application that was installed with ReportServer. You find it in the installation directory. The manager application should be called manager-PLATFORM where platform could be linux-x64, windows or osx.
mysql -u username -ppassword source_database_name < path_to_dumpfile_backup
E.G.: C:\Bitnami\reportserverenterprise-3.7.1.6048-1\mariadb\bin>mysql -u bn_reportserver -pa95f96a6d1 bitnami_reportserver < C:\BitnamiDBbackup\backup\bitnami_reportserver_backup.sql
Thanks to the wonders of sql we can now check whether our import was succesful with the following command.
MariaDB [(none)]>show tables from bitnami_reportserver;
Obviously it worked thanks to seeing a reinstalled database with all its content for our latest version of Bitnami ReportServer.
Restart ReportServer
After finishing the succesful import, you now have a chance of restarting the ReportServer by opening the management tool within the Bitnami installation folder and navigating to the tab "Manage Servers" and clicking on Tomcat Server the start button.
This should start the ReportServer again and allow you to get access to the landing page.
Check the logs
Even though it looks like everything went as expected, there can always happen something unexpected, due to the delicate nature of upgrading software systems.
Due to this situation that could occur it is always wise to check the logs as found in here for anything that could have happened during the import and especially during the call of ReportServer with the new database.
E.G.: C:\Bitnami\reportserverenterprise-3.7.1.6048-1\apache-tomcat\logs\reportserver.xxxx-xx-xx.log
The xxxx-xx-xx is the placeholder for the date of the log so it can vary depending on when you start up tomcat and ReportServer.
If everything went well you should see something like this.
Version: RS3.7.1-6048 2021-11-08-13-44-40 Code Version: 2021-11-08-13-44-40 Java Version: BellSoft OpenJDK 64-Bit Server VM 11.0.13+8-LTS (11) VM Args: -Dcatalina.home=C:\Bitnami\REPORT~1.605\APACHE~1 -Dcatalina.base=C:\Bitnami\REPORT~1.605\APACHE~1 -Dignore.endorsed.dirs=C:\Bitnami\REPORT~1.605\APACHE~1\endorsed -Djava.io.tmpdir=C:\Bitnami\REPORT~1.605\APACHE~1\temp -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file=C:\Bitnami\REPORT~1.605\APACHE~1\conf\logging.properties -Drs.configdir=C:\Bitnami\reportserverenterprise-3.7.1.6050-0/apps/reportserver/reportserver-conf -Dfile.encoding=UTF8 --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/jdk.internal.ref=ALL-UNNAMED --add-opens=java.base/jdk.internal.reflect=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang.ref=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/sun.reflect.generics.repository=ALL-UNNAMED -Djavax.net.ssl.trustStoreType=JKS --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED exit abort -Xms512m -Xmx1536m rs.configdir: C:\Bitnami\reportserverenterprise-3.7.1.6048-1\apps\reportserver\reportserver-conf (OK)
### DB Config ### hibernate.dialect: net.datenwerke.rs.utils.hibernate.MariaDbDialect (OK) hibernate.connection.driver_class: org.mariadb.jdbc.Driver (OK) hibernate.connection.url: jdbc:mariadb://127.0.0.1:3306/bitnami_reportserver (OK) hibernate.connection.username: bn_reportserver hibernate.connection.password: ********** hibernate.default_schema:
Connection Test: OK Schema Version: RS3.0-14
06-Jan-2022 08:54:13.682 INFORMATION [Catalina-utility-1] net.datenwerke.rs.EnvironmentValidator.schemaupdate Performing database update RS3.0-14 -> RS3.0-20 06-Jan-2022 08:54:13.683 INFORMATION [Catalina-utility-1] net.datenwerke.rs.EnvironmentValidator.doVersionUpdate Running script RS3.0-15-MySQL5_UPDATE.sql 06-Jan-2022 08:54:13.760 INFORMATION [Catalina-utility-1] net.datenwerke.rs.EnvironmentValidator.doVersionUpdate Running script RS3.0-16-MySQL5_UPDATE.sql 06-Jan-2022 08:54:13.849 INFORMATION [Catalina-utility-1] net.datenwerke.rs.EnvironmentValidator.doVersionUpdate Running script RS3.0-17-MySQL5_UPDATE.sql 06-Jan-2022 08:54:13.941 INFORMATION [Catalina-utility-1] net.datenwerke.rs.EnvironmentValidator.doVersionUpdate Running script RS3.0-18-MySQL5_UPDATE.sql 06-Jan-2022 08:54:15.308 INFORMATION [Catalina-utility-1] net.datenwerke.rs.EnvironmentValidator.doVersionUpdate Running script RS3.0-19-MySQL5_UPDATE.sql 06-Jan-2022 08:54:16.596 INFORMATION [Catalina-utility-1] net.datenwerke.rs.EnvironmentValidator.doVersionUpdate Running script RS3.0-20-MySQL5_UPDATE.sql 06-Jan-2022 08:54:17.483 INFORMATION [Catalina-utility-1] net.datenwerke.rs.EnvironmentValidator.startup Validating database schema... 06-Jan-2022 08:54:40.259 INFORMATION [Catalina-utility-1] net.datenwerke.rs.EnvironmentValidator.hibernateValidateSchema Schema validation completed
Important are these lines.
Performing database update RS3.0-XY -> RS3.0-XY Update Running script RS3.0-XY-MySQL5_UPDATE.sql
Update the password of the new database
Reportserver should be callable when you restart the tomcat under the following address considering the standard implementation. You just need to start the browser of your choice and type in
127.0.0.1/reportserver/
When you have typed this in and pressed Enter, you should see the Bitnami ReportServer landing page.
Now you just need to log in with your credentials you put up during installation of the new upgrade and navigate to Administration within the gui of ReportServer.
You should see under the Administration tab multiple categories that are enclosed in the left frame of ReportServer and you should click on Datasources.
Due to the new installment of ReportServer you also probably have noticed that your db user credentials have changed.
This encompasses in particular the password as has been discovered in step 3. We need this new password because the backup we imported still is based on the old password of the former credentials of your internal db.
Now you just need to press apply and click on the button test connection and if everything went well you should see a connection succesful pop-up.
Congratulations! You have successfully updated not only your ReportServer to the latest version, but also have taken over all the data you have backed up into the new version.
Please take a note, If you edited the default config files (e.g. for SSO) you can replace them with your backup files after again stopping Tomcat (refer to the C:\Bitnami\reportserverenterprise-3.7.1.6048-1\apps\reportserver\reportserver-conf directory of the backup-section of this guide)
Happy Reporting
Thats why we need to change this now and put in the new password in the password field of the new internal datasource that Bitnami ReportServer uses.
Upgrade Guide to ReportServer 4.7.2
In this guide we describe how to upgrade ReportServer installations to the latest ReportServer 4.7 version. This upgrade guide is valid for ReportServer installations 3.0.1 and higher. This tutorial is valid both for Enterprise and Community versions, as well as for manual installations and Bitnami installer installations.
If you want to perform a new installation check here: Linux or Windows.
Backup all your data before upgrading.
- Pivot table variants: you have to run this script once in order to upgrade your variants using the "-c" (commit) flag: "exec -c pivotUpdate.groovy"
- Mondrian datasources: if your Mondrian XML schema is defined in reportserver, you can choose:
- Mondrian 4 users: You can upgrade the schema to version 9, which is compatible with Mondrian 3's syntax, i.e. your schema should not contain after the upgrade: "<Schema name="YourSchema" metamodelVersion="4.0">". More information here. You have to create new variants, configure them, and save them again.
- Mondrian 3 users: Since Mondrian 9's syntax is compatible to Mondrian 3, you only have to create new variants, configure them, and save them again.
In order to use ReportServer 4.7 with Oracle, the following jars (or newer versions) are needed: ojdbc6.jar, orai18n.jar, xdb6.jar and xmlparserv2.jar. These are needed because of the new XML/JSON support. Please download and copy them to the WEB-INF/lib directory. Note: we recommend to use ojdbc7.jar or newer because of this jdbc driver incompatibility with null parameters in BIRT reports.
Default configuration files are created on first run of ReportServer. Later, when upgrading ReportServer to a newer version, it is probable that newly added configuration files will be missing (i.e. all configuration files added between the version originally installed and the version upgraded to). You can use the "diffconfigfiles" command for finding out these configuration files, check the Administration Guide for details. Note that default values are used for configuration files that are not found.
The Upgrade in a Nutshell
The upgrade basically consists of replacing the webapp files of ReportServer, that is, the files under TOMCAT/webapps/reportserver (or TOMCAT/webapps/ROOT, depending on your installation) are removed and the freshly downloaded files are copied there instead. In the following we explain the upgrade in detail
Preconditions
In this upgrade guide we assume that you are working with an externalized configuration directory as described in Chapter 5 of the configuration guide. We are also assuming that you use Tomcat as application server. If you are not yet using an external configuration directory, this is a good point to change your setup. As for this upgrade, the main difference is that you do not need to worry about your configuration changes, but can simply do a one-step copy and paste upgrade.
For Bitnami users: We here describe how to upgrade ReportServer without upgrading the entire Bitnami stack.
The Upgrade
To upgrade ReportServer, follow these steps.
- Download the newest version of ReportServer 4.7
- Stop your ReportServer instance.
- Go to the webapps directory of Tomcat and there to the directory in which ReportServer is installed. If you used the Bitnami installer this is
INSTALLATION DIRECTORY/apache-tomcat/webapps/reportserver
If you followed the installation tutorial this would be
TOMCAT DIRECTORY/webapps/ROOT
To check that you have the right folder confirm that this folder contains amongst others a file called ReportServer.html and a folder called ddl.
- Clear out the above folder, that is, remove the entire contents. If you do not use external configuration, all your drivers will be deleted, since the whole directory is being replaced by the downloaded one. Backup them to easily add them later. These are found on the WEB-INF/lib directory of your reportserver installation.
- Unzip ReportServer and copy everything to the now empty folder. The structure should be identical to before, that is, you should once more find a file called ReportServer.html and a folder called ddl.
- Completely delete your browser's cache (including all temporary internet files!!!) to ensure you will get the newest files. If you get any error of this type: "Type 'net.datenwerke.rs.base.client.dbhelper.dto.DatabaseHelperDto' was not assignable to 'com.google.gwt.user.client.rpc.IsSerializable' and did not have a custom field serializer.For security purposes, this type will not be serialized.: instance = net.datenwerke.rs.base.client.dbhelper.dto.DatabaseHelperDto@62cbf40f at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serialize(ServerSerializationStreamWriter.java:667)" you have to delete all browser's cache including all temporary internet files.
- Start up Tomcat
This completes the Upgrade to the latest ReportServer 4.7 version. To ensure that installation was successful have a look at your Tomcat log files. Startup should look similar to the following
_____ _ _____ ______ _ _ | __ \ | | / ____| | ____| | | (_) | |__) |___ _ __ ___ _ __| |_| (___ ___ _ ____ _____ _ __ | |__ _ __ | |_ ___ _ __ _ __ _ __ _ ___ ___ | _ // _ \ '_ \ / _ \| '__| __|\___ \ / _ \ '__\ \ / / _ \ '__| | __| | '_ \| __/ _ \ '__| '_ \| '__| / __|/ _ \ | | \ \ __/ |_) | (_) | | | |_ ____) | __/ | \ V / __/ | | |____| | | | || __/ | | |_) | | | \__ \ __/ |_| \_\___| .__/ \___/|_| \__|_____/ \___|_| \_/ \___|_| |______|_| |_|\__\___|_| | .__/|_| |_|___/\___| | | | | |_| |_| Version: RS4.7.VERSION Code Version: CODE_VERSION
Java Version: Oracle Corporation Java HotSpot(TM) 64-Bit Server VM 25.91-b14 (1.8) VM Args: -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:55866 -Xmx2g -Drs.configdir=/opt/reportserver -Dfile.encoding=UTF-8 rs.configdir: /opt/reportserver (OK) ### DB Config ### hibernate.dialect: net.datenwerke.rs.utils.hibernate.MySQL5Dialect (OK) hibernate.connection.driver_class: com.mysql.jdbc.Driver (OK) hibernate.connection.url: jdbc:mysql://127.0.0.1:3309/bitnami_reportserver (OK) hibernate.connection.username: bn_reportserver hibernate.connection.password: ********** hibernate.default_schema: Connection Test: OK Schema Version: RS3.0-11 16:27:30.458 INFO n.datenwerke.rs.EnvironmentValidator - Performing database update RS3.0-11 -> RS3.0-27 16:27:34.220 INFO n.datenwerke.rs.EnvironmentValidator - Running script RS3.0-12-MySQL5_UPDATE.sql
16:27:34.230 INFO n.datenwerke.rs.EnvironmentValidator - Running script RS3.0-13-MySQL5_UPDATE.sql
16:27:34.240 INFO n.datenwerke.rs.EnvironmentValidator - Running script RS3.0-14-MySQL5_UPDATE.sql
16:27:34.250 INFO n.datenwerke.rs.EnvironmentValidator - Running script RS3.0-15-MySQL5_UPDATE.sql
16:27:34.260 INFO n.datenwerke.rs.EnvironmentValidator - Running script RS3.0-16-MySQL5_UPDATE.sql
16:27:34.270 INFO n.datenwerke.rs.EnvironmentValidator - Running script RS3.0-17-MySQL5_UPDATE.sql
16:27:34.280 INFO n.datenwerke.rs.EnvironmentValidator - Running script RS3.0-18-MySQL5_UPDATE.sql
16:27:34.290 INFO n.datenwerke.rs.EnvironmentValidator - Running script RS3.0-19-MySQL5_UPDATE.sql
16:27:34.300 INFO n.datenwerke.rs.EnvironmentValidator - Running script RS3.0-20-MySQL5_UPDATE.sql
16:27:34.310 INFO n.datenwerke.rs.EnvironmentValidator - Running script RS3.0-21-MySQL5_UPDATE.sql
16:27:34.320 INFO n.datenwerke.rs.EnvironmentValidator - Running script RS3.0-22-MySQL5_UPDATE.sql
16:27:34.330 INFO n.datenwerke.rs.EnvironmentValidator - Running script RS3.0-23-MySQL5_UPDATE.sql
16:27:34.340 INFO n.datenwerke.rs.EnvironmentValidator - Running script RS3.0-24-MySQL5_UPDATE.sql
16:27:34.350 INFO n.datenwerke.rs.EnvironmentValidator - Running script RS3.0-25-MySQL5_UPDATE.sql
16:27:34.360 INFO n.datenwerke.rs.EnvironmentValidator - Running script RS3.0-26-MySQL5_UPDATE.sql
16:27:34.360 INFO n.datenwerke.rs.EnvironmentValidator - Running script RS3.0-27-MySQL5_UPDATE.sql
16:29:35.175 INFO n.datenwerke.rs.EnvironmentValidator - Validating database schema...
The important parts are the last rows which indicate that the database upgrade script was successfully executed. Note that this only happens once after the upgrade and that from then on any startup would show an updated schema version, that is:
Schema Version: RS3.0-27
Adapt Internal Configuration
An optional step is to adapt the internal configuration files located in ReportSever's file server (folder etc). For an overview of the changes see here.
Upgrade/Install Demo Data
A final optional step is to install or upgrade your existing demo data. For this, check this link. This step will delete all your existing data in the system.
Happy Reporting
Upgrade Guide to ReportServer 3.0.1
In this guide we describe how to upgrade ReportServer 2.2.2 (RS2.2.2-5639) installations to ReportServer 3.0.1. This guide assumes that you have installed ReportServer manually and have not setup ReportServer via the Bitnami ReportServer Stack. A separate upgrade guide is available for the Bitnami ReportServer Stack.
Before we continue, make sure that you have a backup of the system including your ReportServer database.
With ReportServer 3.0.1 we have introduced some changes in licensing and now provide two versions of ReportServer: ReportServer Community Edition and ReportServer Enterprise Edition. In the cause of this restructuring we have moved some of the functionality that was previously available in the Open Source version of ReportServer to ReportServer Enterprise (see a detailed comparison between the two versions). In particular, scripting is no longer available in the open source variant of ReportServer. A trial version of ReportServer Enterprise Edition including full scripting capabilities is available on request from mailto:sales@infofabrik.de
If you require any of the following features and do not wish to purchase ReportServer Enterprise Edition, please do not upgrade to Reportserver 3.0.1: Scripting, Dynamic List Templates, Dynamic List Pivot Mode, SAP Crystal, Database Bundles or Conditional Scheduling: click here for a detailed comparison between ReportServer Enterprise and ReportServer Community.
Overview
The following upgrade installation explains how to upgrade to either ReportServer 3.0.1 Community Edition or ReportServer 3.0.1 Enterprise Edition. The upgrade consists of the following steps.
- Obtain the ReportServer Program Files
- Replace the Tomcat webapp with the new version of ReportServer.
- Adapt the external configuration files
- Create the Schema Info table
- Start ReportServer for an automated upgrade of the database schema
Obtaining the ReportServer Program Files
The binaries for ReportServer Community Edition are available from SourceForge. On SourceForge look for Files and then the bin directory. Beneath the bin directory you should find directories for the various versions of ReportServer. The files are named according to the following schema: RS followed by the version number and the build number, followed by reportserver.zip. For example, RS3.0.1-5834-2016-03-24-23-46-15-reportserver-ce.zip denotes ReportServer version 3.0 minor revision 1 and build number 5834.
Note: You can download a trial version of the latest ReportServer Enterprise Edition here.
Download the new program files to a temporary location. We will refer to the zip archive as REPORTSERVER301.ZIP.
Replace the Program Files
As a first step we need to replace the ReportServer program files. In the following we assume that Tomcat has its webapps directory in TOMCAT_WEBAPPS. In a standard installation, ReportServer is installed in a folder TOMCAT_WEBAPPS/reportserver or in case you decided to install it to be directly accessible it is installed in TOMCAT_WEBAPPS/ROOT. In the following we will refer to the ReportServer installation directory as REPORTSERVER_WEBAPPS_DIR.
To upgrade the program files perform the following steps:
Stop Tomcat
Make sure that the Tomcat server is stopped.
Remove old files
Move everything beneath REPORTSERVER_WEBAPPS_DIR to a temporary directory, such that we can later access old configuration files.
Unzip the new Program Files to REPORTSERVER_WEBAPPS_DIR
Unzip the new program files (REPORTSERVER301.ZIP) to REPORTSERVER_WEBAPPS_DIR. Ensure that the files were extracted to the correct folder. For this check that directly beneath REPORTSERVER_WEBAPPS_DIR there are (amongst others) the folders WEB-INF and ddl.
Adapt External Configuration
In the next step we need to adapt the external configuration. For this we need to configure two configuration files:
- reportserver.properties — contains ReportServer specific properties
- persistence.properties — contains the connection properties for ReportServer
Note that the connection properties in previous versions were defined via a file called persistence.xml. Starting with ReportServer 3.0.1 the connection properties are instead defined via the configuration file persistence.properties.
reportserver.properties
You can safely reuse your old version of the configuration file reportserver.properties. Simply copy the old file to REPORTSERVER_WEBAPPS_DIR/WEB-INF/classes.
persistence.properties
The configuration persistence.properties defines the connection properties used by ReportServer. An example configuration file called persistence.properties.example is provided in directory REPORTSERVER_WEBAPPS_DIR/WEB-INF/classes. Rename the persistence.properties.example to persistence.properties and set the following properties according to your database settings:
hibernate.connection.username= hibernate.connection.password= hibernate.dialect= hibernate.connection.driver_class= hibernate.connection.url=
You can find the correct configuration values from your old configuration file persistence.xml which is located in REPORTSERVER_OLD_TMPDIR/WEB-INF/classes/META-INF.
Please ensure that the hibernate.dialect is one of the supported ReportServer dialects. These differ from the standard hibernate settings. The supported dialects are:
- net.datenwerke.rs.utils.hibernate.DB2Dialect
- net.datenwerke.rs.utils.hibernate.MySQL5Dialect
- net.datenwerke.rs.utils.hibernate.PostgreSQLDialect
- net.datenwerke.rs.utils.hibernate.Oracle10gDialect
- net.datenwerke.rs.utils.hibernate.SQLServer2008Dialect
Tip: ReportServer allows to remove the configuration files from Tomcat's web directory and put them into an external configuration directory. This makes future upgrades easier as upgrading the program files then does not overwrite any configuration. For further information on using an external configuration directory see the chapter on the configuration dir in ReportServer's Configuration Guide as well as the best-practice installation guide.
Create Schema Info Table
Next, we need to perform a small schema update to tell ReportServer what version of the schema is currently used. This step will only be necessary for this upgrade. Connect to your database and issue the following create tabel statement.
For MySQL:
create table RS_SCHEMAINFO (
ENTITY_ID bigint not null auto_increment,
KEY_FIELD varchar(128) not null,
value longtext,
primary key (ENTITY_ID)
) CHARACTER SET utf8 COLLATE utf8_bin;
For PostgreSQL:
create table RS_SCHEMAINFO (
ENTITY_ID serial not null,
KEY_FIELD varchar(128) not null,
value text,
primary key (ENTITY_ID)
);
For DB2:
create table RS_SCHEMAINFO (
ENTITY_ID bigint generated by default as identity,
KEY_FIELD varchar(128) not null,
value clob(1g),
primary key (ENTITY_ID)
);
For Oracle:
create table RS_SCHEMAINFO (
ENTITY_ID number(19,0) not null,
KEY_FIELD varchar2(128 char) not null,
value clob,
primary key (ENTITY_ID)
);
For Microsoft SQL Server:
create table RS_SCHEMAINFO (
ENTITY_ID bigint identity not null,
KEY_FIELD varchar(128) not null,
value varchar(MAX),
primary key (ENTITY_ID)
);
We now need to insert a single line identifying the current schema version. If you have the latest version from source forge installed, then this would be RS2.2.2-5639.
For MySQL, PostgreSQL, DB2 and Microsoft SQL Server
insert into RS_SCHEMAINFO(KEY_FIELD, value) VALUES('version', 'RS2.2.2-5639');
For Oracle
insert into RS_SCHEMAINFO(ENTITY_ID, KEY_FIELD, value) VALUES(1, 'version', 'RS2.2.2-5639');
Restart Tomcat
All that is left to do is to restart Tomcat. On the first startup ReportServer will detect that the database needs a schema upgrade and perform the necessary steps. Note that this only works in case you use one of the databases that are supported by ReportServer:
- DB2
- Microsoft SQL Server
- MySQL / MariaDB
- PostgreSQL
- Oracle
Adapt Internal Configuration
A final optional step is to adapt the internal configuration files located in ReportSever's file server (folder etc). For an overview of the changes see the release notes.
This completes the Upgrade to ReportServer 3.0.1.
Happy Reporting
Installing ReportServer on Ubuntu
In this best-practice guide we discuss step-by-step how to manually install ReportServer on a freshly set-up Ubuntu box (Version 22.04). While we have chosen Ubuntu as the underlying operating system we hope that this guide also helps with setting up ReportServer on different operating systems. If you would rather use a preconfigured installation package, have a look at the docker images available from our download page.
In this guide we discuss how to install ReportServer. This instruction is valid both for ReportServer Community Edition and for ReportServer Enterprise Edition (see here for a detailed comparison).
Overview
ReportServer is a database backed Java web application and thus to install ReportServer we additionally need to install Java, a servlet container (in this guide we choose Tomcat) and a database (in this guide we choose PostgreSQL). Thus, a high-level overview of an installation of ReportServer consists of the following steps.
- Obtaining the ReportServer binaries
- Installation of Java
- Installation of Tomcat
- Installation of PostgreSQL
- Setting up ReportServer
Obtaining the ReportServer Binaries
The binaries for ReportServer Community Edition are available from SourceForge. On SourceForge look for Files and then the bin directory. Beneath the bin directory you should find directories for the various versions of ReportServer. At the time of writing, the latest version is ReportServer 4.7. Download the latest version (the file should be named RS followed by the version number and the build number, followed by reportserver.zip). For example, RS4.7.2-6108-reportserver.zip denotes ReportServer version 4.7.2 build number 6108.
To obtain a trial version of ReportServer Enterprise Edition go to our download page and look for Binaries.
Go to your home directory (or some directory where we can store the binaries for now) and download ReportServer via https://sourceforge.net/projects/dw-rs/files/latest/download
Note that you need to adapt the version number. We will get back to the binaries in a moment. First, let's install Java, Tomcat and PostgreSQL.
Installing Java
For a smooth experience it is best to install Oracle Java or OpenJDK (via repository) or Azul Zulu Builds of OpenJDK (https://www.azul.com/downloads/?version=java-11-lts&os=ubuntu&architecture=x86-64-bit&package=jre).
$ sudo apt-get update $ sudo apt install openjdk-11-jdk
If java has been installed successfully, then a call to java -version should print out the installed java version.
$ java -version openjdk version "11.0.16" 2022-07-19 OpenJDK Runtime Environment (build 11.0.16+8-post-Ubuntu-0ubuntu122.04) OpenJDK 64-Bit Server VM (build 11.0.16+8-post-Ubuntu-0ubuntu122.04, mixed mode, sharing)
Installing Tomcat
In the following we install Apache Tomcat a web application server that is well suited to run ReportServer. To install Tomcat (currently Ubuntu has precompiled packages for tomcat9 (Tomcat 10 is not supported by ReportServer at the moment)) issue the following command
$ sudo apt-get install tomcat9
Next, we need to configure Tomcat and tell it to invoke java. On Ubuntu you can find these options in the configuration file /etc/default/tomcat9. Open this file with your favorite editor (e.g. nano) and look for the setting for JAVA_OPTS. This tells Tomcat how to invoke java. In particular you should set the options -Xmx4g (which will set the memory of Tomcat and thus ReportServer to 4 GB), -Dfile.encoding=UTF8 (to use UTF-8 by default) and -Drs.configdir=/opt/reportserver (to configure ReportServer's config dir; we will see the effect of this later). Thus, you could have the following configuration:
$ sudo nano /etc/default/tomcat9
JAVA_OPTS="-Djava.awt.headless=true -Xmx4g -XX:+UseConcMarkSweepGC -Dfile.encoding=UTF8" JAVA_OPTS="${JAVA_OPTS} -Drs.configdir=/opt/reportserver"
If you use Java 11 or newer, (ReportServer currently supports Java 11 and Java 17), you need some extra configuration which can be done in the setenv.sh of your Tomcat environment ( /usr/share/tomcat9/bin/setenv.sh). Specifically, the following configuration is needed:
$ sudo nano /usr/share/tomcat9/bin/setenv.sh
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.net=ALL-UNNAMED" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/jdk.internal.ref=ALL-UNNAMED" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/jdk.internal.reflect=ALL-UNNAMED" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.lang.invoke=ALL-UNNAMED" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.util=ALL-UNNAMED" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.lang.ref=ALL-UNNAMED" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.lang.reflect=ALL-UNNAMED" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/sun.reflect.generics.repository=ALL-UNNAMED" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS -Djavax.net.ssl.trustStoreType=JKS" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS -Dfile.encoding=UTF-8" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS -Djava.awt.headless=true"
By default the Tomcat web server will listen on port 8080. Standard web applications are, however, usually run on port 80. To allow serving ReportServer directly on port 80, you can either configure a reverse proxy (e.g. via Apache) or, as we will explain next, configure Tomcat to use authbind. You can't use port 80 because ports under 1024 are restricted to root access in Linux (and we don't want that für our Tomcat) unless you use authbind to override that restriction. First ensure that the authbind packages are installed:
$ sudo apt-get install authbind
Next we go to authbind's byport directory, and create files for the port 80 which we also give to the Tomcat user. This user was automatically created at the process of installing tomcat "apt-get install tomcat":
$ cd /etc/authbind/byport $ sudo touch 80 $ sudo chown tomcat:tomcat 80 $ sudo chmod u+x 80
Next step, modify systemctl to use AUTHBIND when starting the tomcat
$ sudo nano /lib/systemd/system/tomcat9.service change --> ExecStart=/bin/sh /usr/libexec/tomcat9/tomcat-start.sh to --> ExecStart=authbind --deep /usr/libexec/tomcat9/tomcat-start.sh
Finally, we need to change Tomcat's default connector configuration to listen on port 80. For this change the connector settings in server.xml.
$ sudo nano /var/lib/tomcat9/conf/server.xml
<Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000" URIEncoding="UTF-8" redirectPort="8443" />
Tomcat should now be properly configured and you can give it a test spin. Reload the systemd manager configuration (because of our ExecStart change) and restart Tomcat via
$ sudo systemctl daemon-reload $ sudo systemctl restart tomcat9
Make sure that you can connect to Tomcat on port 80 by simply pointing your browser to
http://YOUR_IP
where YOUR_IP is the server's IP address (for example, 127.0.0.1, if you are on the same machine). If Tomcat is running properly, stop it again via
Installing PostgreSQL
Having installed Tomcat, we next need to install a database to hold ReportServer's metadata. For this we will use PostgreSQL in this guide. At the time of writing the latest prepackeged Ubuntu packages is for PostgreSQL 14.5.
Install PosgreSQL via
$ sudo apt-get install postgresql postgresql-contrib
Next we setup a password for the postgres main user (called postgres). For convenience we here assume that the password is set to postgres
$ sudo -u postgres psql postgres \password postgres Enter new password: postgres Enter it again: postgres
Press CTRL+D (corresponds \q) to leave the command prompt.
Next, we create a database called reportserver which will serve as the metadata storage for ReportServer. For run
$ sudo -u postgres createdb reportserver
To set up a database user for reportserver (with password reportserver) run
$ sudo -u postgres createuser -P -s -e reportserver Enter password for new role: reportserver Enter it again: reportserver CREATE ROLE reportserver PASSWORD 'SCRAM-SHA-256$4096:6nEpH9JVjNcBTGtPq4qcgA==$cMIGrF3ZoQZOD4A4qcapAYipQjaJUlxi0DldbWqMZKU=:ONrxkjQezRee0aSNWW9LtAnWA9CNRz0m1Pyqo9qAMO8=' SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN;
Next we need to grant user reportserver the necessary privileges. For this we run
$ sudo -u postgres psql GRANT ALL PRIVILEGES ON DATABASE reportserver TO reportserver; CTRL+D
Finally, since PostgreSQL on Ubuntu per default only allows to connect locally via the postgres user, we need to adapt the configuration file /etc/postgresql/14/main/pg_hba.conf. Here look for a line containing
$ sudo nano /etc/postgresql/14/main/pg_hba.conf
local all all peer
and replace it by
local all all scram-sha-256
Which allows users that provide a valid password hash to connect. For the changes to take effect we need to restart the postgres service.
$ sudo systemctl restart postgresql
Setting up ReportServer
Now that we have Tomcat and PostgreSQL set up, we can install and configure ReportServer. Let REPORTSERVER.zip denote the zip file that we downloaded in the very first step. If not already present let's install unzip.
$ sudo apt-get install unzip
Next, remove the folder ROOT beneath Tomcat's webapp folder. This is where afterwards we are going to put ReportServer in.
$ cd /var/lib/tomcat9/webapps/
$ sudo rm -r /var/lib/tomcat9/webapps/ROOT
Now unzip RS4.7.VERSION-reportserver-ce.zip to that directory. But before executing, change MYUSER to your user or the path to the directory where you stored the downloaded zip-file.
$ sudo unzip /home/MYUSER/RS4.7.VERSION-reportserver-ce.zip -d /var/lib/tomcat9/webapps/ROOT
In the next step we will create a configuration directory for ReportServer. Remember that when we configured Tomcat, we set the environment variable JAVA_OPT to contain the option -Drs.configdir=/opt/reportserver. This tells ReportServer to look for its configuration directory in /opt/reportserver. Let's create that directory as well as the sub-directories lib and config.
$ sudo mkdir /opt/reportserver $ sudo mkdir /opt/reportserver/lib $ sudo mkdir /opt/reportserver/config
The lib directory can hold additional jars (for example, additional JDBC drivers). The config directory can hold internal configuration files. For further information about the config dir see the chapter on the configuration dir in ReportServer's Configuration Guide.
In a next step we will copy default configuration files to the configuration directory. We will copy the following files.
- persistence.properties.example
- reportserver.properties
- logging-rs.properties
$ sudo cp /var/lib/tomcat9/webapps/WEB-INF/classes/persistence.properties.example /opt/reportserver/persistence.properties $ sudo cp /var/lib/tomcat9/webapps/WEB-INF/classes/reportserver.properties /opt/reportserver/reportserver.properties $ sudo cp /var/lib/tomcat9/webapps/WEB-INF/classes/logging-rs.properties /opt/reportserver/logging-rs.properties
Note that we have renamed "persistence.properties.example" into persistence.properties. This file holds the database configuration which we setup next. For this open the file persistence.properties with a text editor and set up the following connection parameters
$ sudo nano /opt/reportserver/persistence.properties
# Credentials hibernate.connection.username=reportserver hibernate.connection.password=reportserver # PostgreSQL hibernate.dialect=net.datenwerke.rs.utils.hibernate.PostgreSQLDialect hibernate.connection.driver_class=org.postgresql.Driver hibernate.connection.url=jdbc:postgresql://localhost/reportserver
Having changed the configuration, let's give the tomcat user the necessary permissions to read the configuration files.
$ sudo chown -R tomcat:tomcat /opt/reportserver
The same applies for tomcat webapps.
$ sudo chown -R tomcat:tomcat /var/lib/tomcat9/webapps/
Initialize the ReportServer Database
ReportServer expects that its database is properly initialized. For this we need to run the DDL script for PostgreSQL that is located in the ddl subdirectory of ReportServer (i.e., in /var/lib/tomcat9/webapps/ddl). ReportServer supports various databases and in that directory you not only find the create script for PostgreSQL but also for all other supported databases. The files are named according to the following format:
reportserver-VERSION-schema-PostgreSQL_CREATE.sql
Besides a CREATE script there is also a DROP script, if you wish to clean up the database.
To set up ReportServer's database run the following command to run the CREATE ddl:
psql -U reportserver -preportserver -d reportserver -a -f /var/lib/tomcat9/webapps/ddl/reportserver-RS4.7.VERSION-schema-PostgreSQL_CREATE.sql
If you run the ddl script via a database GUI make sure to call commit.
Setup Logging
By default, Tomcat will write its log entries into a file called catalina.out, which not only contains ReportServer specific entries but anything that involves Tomcat. To have a ReportServer specific log file, edit the file /var/lib/tomcat9/conf/logging.properties. The following changes need to be made:
- add 5reportserver.org.apache.juli.FileHandler to handlers
- define handler specific properties
- tell tomcat to use the new handler for anything ReportServer specific
- adapt the log format to include a proper timestamp
A sample configuration could look as follows:
$ sudo nano /var/lib/tomcat9/conf/logging.properties
# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. handlers = 1catalina.org.apache.juli.AsyncFileHandler, 2localhost.org.apache.juli.AsyncFileHandler, 3manager.org.apache.juli.AsyncFileHandler, 4host-manager.org.apache.juli.AsyncFileHandler, 5reportserver.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler .handlers = 1catalina.org.apache.juli.AsyncFileHandler, java.util.logging.ConsoleHandler ############################################################ # Handler specific properties. # Describes specific configuration info for Handlers. ############################################################ 1catalina.org.apache.juli.AsyncFileHandler.level = FINE 1catalina.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs 1catalina.org.apache.juli.AsyncFileHandler.prefix = catalina. 2localhost.org.apache.juli.AsyncFileHandler.level = FINE 2localhost.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs 2localhost.org.apache.juli.AsyncFileHandler.prefix = localhost. 3manager.org.apache.juli.AsyncFileHandler.level = FINE 3manager.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs 3manager.org.apache.juli.AsyncFileHandler.prefix = manager. 4host-manager.org.apache.juli.AsyncFileHandler.level = FINE 4host-manager.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs 4host-manager.org.apache.juli.AsyncFileHandler.prefix = host-manager. 5reportserver.org.apache.juli.FileHandler.level = FINE #5reportserver.org.apache.juli.FileHandler.maxDays = 90 5reportserver.org.apache.juli.FileHandler.directory = ${catalina.base}/logs 5reportserver.org.apache.juli.FileHandler.prefix = reportserver. java.util.logging.ConsoleHandler.level = FINE java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter java.util.logging.SimpleFormatter.format = [%1$tc] %4$s: %2$s - %5$s %6$s%n ############################################################ # Facility specific properties. # Provides extra control for each logger. ############################################################ org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = 2localhost.org.apache.juli.AsyncFileHandler org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level = INFO org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].handlers = 3manager.org.apache.juli.AsyncFileHandler org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].level = INFO org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].handlers = 4host-manager.org.apache.juli.AsyncFileHandler org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/reportserver].level = INFO org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/reportserver].handlers = 5reportserver.org.apache.juli.FileHandler # For example, set the org.apache.catalina.util.LifecycleBase logger to log # each component that extends LifecycleBase changing state: #org.apache.catalina.util.LifecycleBase.level = FINE # To see debug messages in TldLocationsCache, uncomment the following line: #org.apache.jasper.compiler.TldLocationsCache.level = FINE
Starting ReportServer
We are finally at an end. To test the installation, start tomcat and observe the output in ReportServer's log file.
$ sudo systemctl restart tomcat9
$ tail -f /var/lib/tomcat9/logs/reportserver.DATE.log
If all goes well you should see a message such as:
INFO [Thread-13] net.datenwerke.gf.service.lateinit.LateInitStartup$1.run Startup completed
You can now direct your browser to
http://YOUR_IP/ReportServer.html
and log in to ReportServer via user root with password root which ReportServer created on the first start.
Happy Reporting
Installing ReportServer on Windows
In this best-practice guide we discuss step-by-step how to manually install ReportServer on a freshly set-up Windows 10 box. If you would rather use a preconfigured installation package, have a look at the installer packages available in our download page.
The instructions in this guide are valid both for ReportServer Community Edition and for ReportServer Enterprise Edition (see here for a detailed comparison).
Overview
ReportServer is a database backed Java web application. In order to install ReportServer we additionally need to install Java, a servlet container (in this guide we choose Tomcat) and a database (in this guide we choose MariaDB). Thus, a high-level overview of an installation of ReportServer consists of the following steps.
- Overview
- Obtaining Binaries
- Install Java
- Install Tomcat
- Install MariaDB
- Setup ReportServer
- Setup Database
- Setup Logging
- Start ReportServer
Obtaining the ReportServer Binaries
The binaries for ReportServer Community Edition are available from SourceForge. On SourceForge look for Files and then the bin directory. Beneath the bin directory you should find directories for the various versions of ReportServer. At the time of writing, the latest version is ReportServer 4.7. Download the latest version (the file should be named RS followed by the version number and the build number, followed by reportserver.zip). For example, RS4.7.2-6108-reportserver.zip denotes ReportServer version 4.7.2 build number 6108.
To obtain a trial version of ReportServer Enterprise Edition go to our download page and look for Binaries.
Download ReportServer (either enterprise or community). We will get back to this shortly.
Installing Java
For a smooth experience it is best to install e.g. Java 11. Note that Java 17 is also supported. You can download Java JDK 11 from here. If you are running on a 64-bit Windows (which is highly recommended when using ReportServer) you should ensure to download the 64-bit version of java. Scroll down the site and download the Java 11 JDK Windows x64 MSI Installer and run it. During the installation choose to set the Java HOME Path (check Screenshot).
Once the installation is complete let us ensure that java is running properly. For this open a command prompt (open the Cortana quick search and search for cmd). Then run the following command
java -version
You should see a response similar to the following
openjdk version "11.0.18" 2023-01-17 LTS OpenJDK Runtime Environment Zulu11.62+17-CA (build 11.0.18+10-LTS) OpenJDK 64-Bit Server VM Zulu11.62+17-CA (build 11.0.18+10-LTS, mixed mode)
If so, you have successfully installed a 64-bit Java. Let's proceed with installing Tomcat.
Installing Tomcat
In the following we install Apache Tomcat a web application server that is well suited to run ReportServer. We will install Tomcat in version 9. The easiest is to install the 32-bit/64-bit Windows Service Installer. You find the download page in the left navigation.
Once the installer is downloaded, run the installer and follow the setup. You will be asked to choose the Components for the installation. Here choose the Minimum setup which consists only of the Start Menu Items. In the next step you can adapt Tomcat's basic configuration, to locate the installed Java JRE and to define the installation directory. The default values are usually fine so simply proceed.
Once the installation has finished you are asked whether to start Tomcat. For the moment we do not yet start Tomcat, so disable the checkbox and hit Finish.
In a next step we need to configure Tomcat. For this open the tomcat management application Configure Tomcat which you can find in the start menu. Go to the Java tab. Here we need to adapt the the Java Options as well the memory settings. Go to the end of the Java options and add the following lines
-Drs.configdir=C:\Program Files\reportserver -Dfile.encoding=UTF8
The first one tells ReportServer that we are using an external configuration directory which will be C:\Program Files\reportserver. The second property tells Tomcat (and ReportServer) to use UTF8 encoding by default. As for the memory setting, set the initial memory pool to 512 MB and the maximum memory pool should be set to at least 1.5 GB. Click on Apply and then OK to close the configuration.
If you use Java 9 or newer, you need some extra configuration which can be done in the setenv.bat of your Tomcat environment. Specifically, the following configuration is needed, so create a setenv.bat in the C:\Program Files\Apache Software Foundation\Tomcat 9.0\bin Folder containing:
--add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/jdk.internal.ref=ALL-UNNAMED --add-opens=java.base/jdk.internal.reflect=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang.ref=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/sun.reflect.generics.repository=ALL-UNNAMED -Djavax.net.ssl.trustStoreType=JKS -Dfile.encoding=UTF-8 -Djava.awt.headless=true
Next step is to install a database. ReportServer can be installed on DB2, MariaDB, MySQL, Microsoft SQL Server, Oracle or PostgreSQL. Here we choose MariaDB.
Installing MariaDB
You can find current installers for MariaDB on http://downloads.mariadb.org. Choose the latest stable version (at the time of writing 10.11.1 RC is the latest supported) and download the corresponding 64-bit MSI package.
Once downloaded, run the installer. Note that Windows may regard MariaDB as an Unknown Publisher in which case it will ask you twice whether you want to run the installer. Hit Run anyway to continue and follow the setup steps. Most of the standard options are fine so the only thing you need to change is the root password and you should select the checkbox next to Use UTF8 as default server's character set. The UTF8 option is on the bottom of the same page as the password.
MariaDB comes with a copy of HeidiSQL which provides a front end to the database. Once the installation has finished, open HeidiSQL and create a new Session. On the right side under Settings you 'll have to choose a Library that works (most likely libmysql.dll, if not, choose another one), then choose user root and the password that you provided during the installation and hit Open. You should now see your database.
To create the database for ReportServer, move the mouse to the left (Database) panel, right click and choose Create New -> Database. As name we choose reportserver and hit OK.
We will come back to the database in a bit to create the ReportServer tables. You may also want to create an additional user to be used by ReportServer that only has the privileges to work on the freshly created reportserver database. For the purpose of this guide, we assume that ReportServer will also use the root user.
Setting up ReportServer
Now that we have Tomcat and MariaDB set up, we can install and configure ReportServer. Let REPORTSERVER.zip denote the zip file that we downloaded in the very first step. Unzip the file.
Next, go to the Tomcat installation directory. If you haven't changed the directory during installation the directory should be
C:\Program Files\Apache Software Foundation\Tomcat 9.0
Here you should find a folder webapps that contains a single folder called ROOT. Remove the ROOT folder. Now move the extracted ReportServer folder into Tomcat's webapps directory and rename it to ROOT. Within the ROOT folder you should now have amongst others a folder called ddl and one called WEB-INF.
Next we create the ReportServer configuration directory. For this create the following directories
- C:\Program Files\reportserver
- C:\Program Files\reportserver\config
- C:\Program Files\reportserver\lib
The lib directory can hold additional jars (for example, additional JDBC drivers). The config directory can hold internal configuration files. For further information about the config dir see the chapter on the configuration dir in ReportServer's Configuration Guide.
In a next step we will copy default configuration files to the configuration directory. We will copy the following files to:
- C:\Program Files\reportserver\persistence.properties.example
- C:\Program Files\reportserver\reportserver.properties
- C:\Program Files\reportserver\logging-rs.properties
The files are located in the WEB-INF/classes directory. That is, if you installed Tomcat to its default location, the files are in
C:\Program Files\Apache Software Foundation\Tomcat 9.0\webapps\ROOT\WEB-INF\classes
Copy the files to C:\Program Files\reportserver and rename the file persistence.properties.example into persistence.properties. This file holds the database configuration which we setup next. For this open the file persistence.properties with a text editor (if no proper editor is installed try Notepad++ which is freely available from https://notepad-plus-plus.org/) and set up the following connection parameters (replace the password with the password you chose during the MariaDB installation).
hibernate.connection.username=root hibernate.connection.password=**** hibernate.dialect=net.datenwerke.rs.utils.hibernate.MariaDbDialect hibernate.connection.driver_class=org.mariadb.jdbc.Driver hibernate.connection.url=jdbc:mariadb://localhost:3306/reportserver
Initialize the ReportServer Database
ReportServer expects that its database is properly initialized. For this we need to run the DDL script for MySQL that is located in the ddl subdirectory of ReportServer (i.e., in C:\Program Files\Apache Software Foundation\Tomcat 9.0\webapps\ROOT\ddl). ReportServer supports various databases and in that directory you not only find the create script for MySQL (which is also the one needed for MariaDB) but also for all other supported databases. The files are named according to the following format:
reportserver-VERSION-schema-MySQL5_CREATE.sql
Besides a CREATE script there is also a DROP script, if you wish to clean up the database.
To set up ReportServer's database again open HeidiSQL and a connect to the reportserver database. Select the database by double clicking in the left panel on the reportserver database. Next chose Load SQL file from the File Menu and select the reportserver-VERSION-schema-MySQL5_CREATE.sql file. Once the file is loaded you should see the file in within HeidiSQL. Hit F9 (or select the blue play button from the toolbar) to run the script. Wait for the script to finish (this can take a few seconds). Once finished you can close HeidiSQL.
If you are not installing on MariaDB make sure to call commit to commit the inserts at the end of the DDL.
Setup Logging
By default, Tomcat will write its log entries into a file called catalina.out, which not only contains ReportServer specific entries but anything that involves Tomcat. To have a ReportServer specific log file, edit the file C:\Program Files\Apache Software Foundation\Tomcat 9.0\conf\logging.properties. Note that this step is optional and can be skipped in which case the standard Tomcat log structure will be used.
To have a ReportServer specific log file, the following changes need to be made to C:\Program Files\Apache Software Foundation\Tomcat 9.0\conf\logging.properties:
- add 5reportserver.org.apache.juli.FileHandler to handlers
- define handler specific properties
- tell tomcat to use the new handler for anything ReportServer specific
- adapt the log format to include a proper timestamp
A sample configuration could look as follows:
# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. handlers = 1catalina.org.apache.juli.AsyncFileHandler, 2localhost.org.apache.juli.AsyncFileHandler, 3manager.org.apache.juli.AsyncFileHandler, 4host-manager.org.apache.juli.AsyncFileHandler, 5reportserver.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler .handlers = 1catalina.org.apache.juli.AsyncFileHandler, java.util.logging.ConsoleHandler ############################################################ # Handler specific properties. # Describes specific configuration info for Handlers. ############################################################ 1catalina.org.apache.juli.AsyncFileHandler.level = FINE 1catalina.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs 1catalina.org.apache.juli.AsyncFileHandler.prefix = catalina. 2localhost.org.apache.juli.AsyncFileHandler.level = FINE 2localhost.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs 2localhost.org.apache.juli.AsyncFileHandler.prefix = localhost. 3manager.org.apache.juli.AsyncFileHandler.level = FINE 3manager.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs 3manager.org.apache.juli.AsyncFileHandler.prefix = manager. 4host-manager.org.apache.juli.AsyncFileHandler.level = FINE 4host-manager.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs 4host-manager.org.apache.juli.AsyncFileHandler.prefix = host-manager. 5reportserver.org.apache.juli.FileHandler.level = FINE #5reportserver.org.apache.juli.FileHandler.maxDays = 90 5reportserver.org.apache.juli.FileHandler.directory = ${catalina.base}/logs 5reportserver.org.apache.juli.FileHandler.prefix = reportserver. java.util.logging.ConsoleHandler.level = FINE java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter java.util.logging.SimpleFormatter.format = [%1$tc] %4$s: %2$s - %5$s %6$s%n ############################################################ # Facility specific properties. # Provides extra control for each logger. ############################################################ org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = 2localhost.org.apache.juli.AsyncFileHandler org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level = INFO org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].handlers = 3manager.org.apache.juli.AsyncFileHandler org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].level = INFO org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].handlers = 4host-manager.org.apache.juli.AsyncFileHandler org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/reportserver].level = INFO org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/reportserver].handlers = 5reportserver.org.apache.juli.FileHandler # For example, set the org.apache.catalina.util.LifecycleBase logger to log # each component that extends LifecycleBase changing state: #org.apache.catalina.util.LifecycleBase.level = FINE # To see debug messages in TldLocationsCache, uncomment the following line: #org.apache.jasper.compiler.TldLocationsCache.level = FINE
Starting ReportServer
We are finally ready to start Tomcat and thus ReportServer. Again open the Tomcat management application. In the general tab on the bottom you have buttons for starting and stopping Tomcat. Hit Start. If everything goes fine, Tomcat should show a progress bar and then switch to showing Service Status: Started. To ensure that everything worked (or to find out what does not) let us have a look at the log files. They are located in
C:\Program Files\Apache Software Foundation\Tomcat 9.0\logs
Starting ReportServer
again assuming that you installed Tomcat in its default location. There you should now find a log file called reportserver.DATE.log which, if everything went fine should have content similar to the following:
26-Jan-2023 17:21:12.600 INFO [main] net.datenwerke.rs.EnvironmentValidator.startup _____ _ _____ ______ _ _ | __ \ | | / ____| | ____| | | (_) | |__) |___ _ __ ___ _ __| |_| (___ ___ _ ____ _____ _ __ | |__ _ __ | |_ ___ _ __ _ __ _ __ _ ___ ___ | _ // _ \ '_ \ / _ \| '__| __|\___ \ / _ \ '__\ \ / / _ \ '__| | __| | '_ \| __/ _ \ '__| '_ \| '__| / __|/ _ \ | | \ \ __/ |_) | (_) | | | |_ ____) | __/ | \ V / __/ | | |____| | | | || __/ | | |_) | | | \__ \ __/ |_| \_\___| .__/ \___/|_| \__|_____/ \___|_| \_/ \___|_| |______|_| |_|\__\___|_| | .__/|_| |_|___/\___| | | | | |_| |_| Version: RS4.4.0-6084 2022-12-08-13-49-54 Code Version: 2022-12-08-12-41-21 Java Version: Azul Systems, Inc. OpenJDK 64-Bit Server VM 11.0.18+10-LTS (11) VM Args: -Dcatalina.home=C:\Program Files\Apache Software Foundation\Tomcat 9.0 -Dcatalina.base=C:\Program Files\Apache Software Foundation\Tomcat 9.0 -Djava.io.tmpdir=C:\Program Files\Apache Software Foundation\Tomcat 9.0\temp -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file=C:\Program Files\Apache Software Foundation\Tomcat 9.0\conf\logging.properties -Drs.configdir=C:\Program Files\reportserver --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED exit abort -Xms512m -Xmx1500m Max memory: 1,500 MB JVM Locale: en_US rs.configdir: C:\Program Files\reportserver (OK) ### DB Config ### hibernate.dialect: net.datenwerke.rs.utils.hibernate.MariaDbDialect (OK) hibernate.connection.driver_class: org.mariadb.jdbc.Driver (OK) hibernate.connection.url: jdbc:mariadb://localhost:3306/reportserver (OK) hibernate.connection.username: root hibernate.connection.password: ********** hibernate.default_schema: Connection Test: OK Schema Version: RS3.0-25 ### Internal datasource metadata ### Database name: MariaDB Database version: 10.11.1-MariaDB Driver name: MariaDB Connector/J Driver version: 3.1.0 JDBC major version: 4 JDBC minor version: 2 JDBC URL: jdbc:mariadb://localhost:3306/reportserver?user=root&password=*** JDBC username: root 26-Jan-2023 17:21:12.636 INFO [main] net.datenwerke.rs.EnvironmentValidator.startup Validating database schema... 26-Jan-2023 17:21:31.434 INFO [main] net.datenwerke.rs.EnvironmentValidator.hibernateValidateSchema Schema validation completed 26-Jan-2023 17:21:35.373 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure try to bind: net.datenwerke.rs.saiku.server.rest.resources.InfoResource 26-Jan-2023 17:21:35.373 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure done binding: net.datenwerke.rs.saiku.server.rest.resources.InfoResource 26-Jan-2023 17:21:35.373 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure try to bind: net.datenwerke.rs.saiku.server.rest.resources.OlapDiscoverResource 26-Jan-2023 17:21:35.373 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure done binding: net.datenwerke.rs.saiku.server.rest.resources.OlapDiscoverResource 26-Jan-2023 17:21:35.373 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure try to bind: net.datenwerke.rs.saiku.server.rest.resources.StatisticsResource 26-Jan-2023 17:21:35.373 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure done binding: net.datenwerke.rs.saiku.server.rest.resources.StatisticsResource 26-Jan-2023 17:21:35.373 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure try to bind: net.datenwerke.rs.saiku.server.rest.resources.ExporterResource 26-Jan-2023 17:21:35.377 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure done binding: net.datenwerke.rs.saiku.server.rest.resources.ExporterResource 26-Jan-2023 17:21:35.377 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure try to bind: net.datenwerke.rs.saiku.server.rest.resources.Query2Resource 26-Jan-2023 17:21:35.377 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure done binding: net.datenwerke.rs.saiku.server.rest.resources.Query2Resource 26-Jan-2023 17:21:35.377 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure try to bind: net.datenwerke.rs.saiku.server.rest.resources.SessionResource 26-Jan-2023 17:21:35.377 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure done binding: net.datenwerke.rs.saiku.server.rest.resources.SessionResource 26-Jan-2023 17:21:35.377 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure try to bind: net.datenwerke.rs.saiku.server.rest.resources.BasicRepositoryResource2 26-Jan-2023 17:21:35.377 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure done binding: net.datenwerke.rs.saiku.server.rest.resources.BasicRepositoryResource2 26-Jan-2023 17:21:35.377 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure try to bind: net.datenwerke.rs.saiku.server.rest.resources.License 26-Jan-2023 17:21:35.377 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure done binding: net.datenwerke.rs.saiku.server.rest.resources.License 26-Jan-2023 17:21:35.377 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure try to bind: net.datenwerke.rs.saiku.server.rest.resources.DataSourceResource 26-Jan-2023 17:21:35.377 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure done binding: net.datenwerke.rs.saiku.server.rest.resources.DataSourceResource 26-Jan-2023 17:21:35.377 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure try to bind: net.datenwerke.rs.saiku.server.rest.resources.FilterRepositoryResource 26-Jan-2023 17:21:35.377 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure done binding: net.datenwerke.rs.saiku.server.rest.resources.FilterRepositoryResource 26-Jan-2023 17:21:35.377 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure try to bind: net.datenwerke.rs.saiku.server.rest.resources.SaikuI18nResource 26-Jan-2023 17:21:35.377 INFO [main] net.datenwerke.rs.saiku.server.rest.SaikuRestModule.configure done binding: net.datenwerke.rs.saiku.server.rest.resources.SaikuI18nResource 26-Jan-2023 17:21:36.369 INFO [main] net.datenwerke.rsenterprise.main.service.RsEnterpriseModule.configureServlets SAP Crystal libraries not present 26-Jan-2023 17:21:45.705 WARNING [main] net.datenwerke.rs.configservice.service.configservice.ConfigServiceImpl.getConfigFailsafe Configfile scheduler/scheduler.cf could not be loaded. Default values are in effect. 26-Jan-2023 17:21:45.705 WARNING [main] net.datenwerke.rs.configservice.service.configservice.ConfigServiceImpl.getConfigFailsafe Configfile exportfilemd/pdfexport.cf could not be loaded. Default values are in effect. 26-Jan-2023 17:21:45.705 WARNING [main] net.datenwerke.rs.configservice.service.configservice.ConfigServiceImpl.getConfigFailsafe Configfile exportfilemd/pdfexport.cf could not be loaded. Default values are in effect. 26-Jan-2023 17:21:46.430 INFO [main] net.datenwerke.rs.saiku.service.saiku.ThinQueryServiceImpl.<init> loaded thinqueryservice 26-Jan-2023 17:21:46.430 INFO [main] net.datenwerke.rs.saiku.service.saiku.ThinQueryServiceImpl.<init> loaded thinqueryservice 26-Jan-2023 17:21:47.142 INFO [main] net.datenwerke.rs.core.service.i18ntools.RemoteMessageServiceImpl.<init> Loading messages... 26-Jan-2023 17:21:58.073 INFO [main] net.datenwerke.rs.core.service.i18ntools.RemoteMessageServiceImpl.<init> Available locales: de, hi, lo, pt, lt, hr, lv, hu, zh-CN, hy, uk, id, mk, mn, af, uz, ms, el, mt, en, is, it, my, es, et, eu, vi, ja, ne, ro, nl, no, be, fi, ru, bg, keys, bn, fr, jw, bs, ka, si, sk, sl, ga, gd, ca, sq, sr, kk, km, sv, ko, zh-TW, ta, cs, th, lb, tl, pl, da, tr 26-Jan-2023 17:22:16.830 INFO [Thread-12] net.datenwerke.rs.installation.ExecutePackagedScriptsTask.executeOnFirstRun Executing package scripts from: C:\Program Files\Apache Software Foundation\Tomcat 9.0\webapps\ROOT\pkg 26-Jan-2023 17:22:17.814 INFO [Thread-12] net.datenwerke.rs.installation.ExecutePackagedScriptsTask.executeOnFirstRun Executing package: C:\Program Files\Apache Software Foundation\Tomcat 9.0\webapps\ROOT\pkg\baseconfig-RS4.4.0-6084-2022-12-08-13-49-54.zip 26-Jan-2023 17:27:47.611 INFO [Thread-12] net.datenwerke.rs.core.service.jarextension.hookers.ReportServerExtenderHooker.initialize Start loading ReportServer extensions. 26-Jan-2023 17:27:47.627 INFO [Thread-12] net.datenwerke.rs.core.service.EnvironmentAfterStartupInformation.initialize Application Server: Apache Tomcat/9.0.71 ### PAM Configuration ### Static PAM configuration: net.datenwerke.rs.authenticator.service.pam.UserPasswordPAMAuthoritative Finalized PAM configuration: class net.datenwerke.rs.authenticator.service.pam.UserPasswordPAMAuthoritative 26-Jan-2023 17:27:51.941 INFO [Thread-12] net.datenwerke.rs.search.service.search.SearchServiceImpl.rebuildIndex Rebuilding search index... 26-Jan-2023 17:27:52.401 INFO [Thread-12] net.datenwerke.gf.service.lateinit.LateInitStartup$1.run Startup completed</init></init></init></init>
The last line (Startup complete) indicates that ReportServer was properly started. You can now direct your browser to
http://127.0.0.1:8080/ReportServer.html
and log in to ReportServer via user root with password root which ReportServer created on the first start.
Happy Reporting
ReportServer - An Introduction
We are thrilled that you decided to give ReportServer a try. With this tutorial we want to give you a short introduction to our demo system and it should lead you step by step through the most important functionality from the perspective of end users.
First things first. You will find the demo system at
http://demo.raas.datenwerke.net
Or, if you already have ReportServer installed on your system, then you will probably find it at 127.0.0.1:8080/reportserver. In order to follow this tutorial you must have installed the demo data. If you have not done so already during the installation (try to log in with user: demodata, password: demodata to find out if the demo data is installed) follow these five steps:
- Login with user: root, password: root (or the super user that you defined during installation)
- Press CTRL+ALT+T to open the ReportServer terminal
- Type "pkg install -d demob" (without quotes) and press TAB
- The text should have been completed to look like:
pkg install -d demobuilder-VERSION-DATE.zipIf so, press ENTER - Wait for a minute or so, once the terminal returns, reload your browser window.
In the following we will walk you through the demo system. If you would rather explore the demo system on your own initiative you can simply login with the following credentials:
username: demoadmin
password: demoadmin
This account will give you broad (although read-only) access to the system and allows you to explore almost all areas or ReportSever. Of course, if you have installed ReportServer locally then you can use the super user account that you specified during installation (default is: root/root) to get full access to ReportServer.
The Demo System
Our demo system simulates the reporting platform of a toys retailer. The data originates from the Eclipse BIRT Sample Database and was adapted by us to form a small data warehouse; that is, we mainly aggregated information according to different keys into aggregate tables. In addition we have created several reports which we will introduce in the following sections.
The organizational chart of our fictional enterprise is also based on the Eclipse sample data. On this basis we have introduced departments and divisions each with access to reports regarding their area of work. In the course of this introduction we will assume the position of different users to introduce the various aspects of ReportServer.
To start, we want to show you how ReportServer looks from the point of view of a typical user. For this please go to login page and login with the following credentials:
username: gbondur
password: secret
Gerard Bondur is a sales person and responsible for the regions Europe, the Middle East and Africa. After logging into the system you'll find yourself in the dashboard module. Gerard has a single dashboard with four dadgets (short for DAshboard gaDGETS). Dashboards allow to create a page (or multiple pages) which contains aggregated information such that, for example, key performance indicators are visible on a single glance. Users, such as Gerard, manage dashboards themselves. Alternatively they can choose to include dashboards created by an administrator. In our online demo-system you will only have read access to the dashboard and can thus not create new dashboards or change the demo dashboard.
Besides the dashboard module the second important module for users is the TeamSpace. To change to the TeamSpace switch modules by clicking on TeamSpace on the module bar on the top edge of the screen. Gerard is part of the sales team and has thus access to the Sales TeamSpace. The Sales TeamSpace is a shared workspace for all employees of the sales department. Naturally there can be many TeamSpaces and users can have access to multiple TeamSpaces. We will take a closer look at TeamSpace later on in this introduction.
Before we get to the TeamSpace note that the UI of ReportServer is structured into several areas. On the very top you find the module navigation which allows you to access various areas of ReportServer (you have already seen the Dashboard and TeamSpace modules; other modules are for example the Scheduler overview or the Administration module). On the top right you find a link to the user's profile allowing the user to change various settings. Next to the link to the user's profile you see a global search field.
Beneath the module navigation you will see the currently active module: in our case the TeamSapce. The TeamSpace is again structured into two parts. The left part is modeled after the Windows Explorer and allows to navigate through folders to the various report objects. On the right you find detailed information to the currently selected objects. Dragging the border between the two windows allows you to resize the windows.
Let us start with the execution of a dynamic list which is the ReportServer way of ad hoc reporting. Access the folder Customers and open the report T_AGG_CUSTOMERS - Basis by double clicking its icon. This report accesses an aggregate table in the warehouse and allows the user to freely configure which kind of data from that table he or she wants to select.
After opening the report you will find yourself in the list configuration view (the different views are listed on the left hand side). Begin with clicking on the button select columns to start adding data to your report. The opened popup contains a list of all available columns that you can choose from for this dynamic list. You can select columns by double click or via drag and drop from the left to the right. Please select the following columns (it might be easier to use the search box to find the columns):
CUS_CUSTOMERNAME
Y_VOLUME
Click on apply to confirm the selection. You should now be back in the list configuration view and the two columns should have been added to the configuration.
To view a preview of your report click on "preview" in the left column. The preview view displays the first 100 data-records and provides some metadata at the bottom. The bottom toolbar also allows you to page through the data. Via right click on data cells you can access several functions such as sorting, filters and formats. These and additional functionality can also be accessed from the list configuration view ("configure list" in the left column). To see a record in detail, double click on it. To export your report into one of various formats you can click on "Excel-Export" (for Microsoft Excel) or on the little arrow next to it. The configuration of a dynamic list (which columns, filters, aggregations, etc.) can, of course, be saved as a so called variant such that it can be easily accessed again at some later point. In the read-only demo system this functionality is, however, disabled.
Now go back to the TeamSpace (click on Team Spaces in the module navigation at the top of the page) and select the report Customermap (located in the root folder of the TeamSpace). For selection, a single click is sufficient. A double-click, on the other hand, runs the report.
ReportServer's scripting interface allows to incorporate almost any kind of report format such as, for example, a view of the existing customers displayed on google maps. As you can see, additional information for a report including a preview of the report is directly displayed in the detail panel on the right hand side of the TeamSpace. Click on one of the customers to get some additional information and a link to a dynamic list displaying detailed information for that particular customer. If you click on the link you'll be forwarded to that dynamic list. Notice that the new report is opened in a tab which allows you to easily go back to any previously opened report. To close a report click on the cross icon on the upper right or on the tab (at the bottom).
ReportServer integrates the jXLS library which allows users to directly export data into a preconfigured Excel template thereby bridging the gap between simple list like reports and highly formatted reports such as as Birt, Jasper or SAP Crystal Reports. To get a feeling of what can be achieved using Excel templates go back to the TeamSpace and open the report OrderReport Template (located in the Customers folder). Then click on "Template-Export".
TeamSpace Administration and Pixel-Perfect Reporting
Please log off the system and login again with the following credentials:
username: magrippa
password: secret
You are now logged in as Menenius Agrippa, a user who has access to two TeamSpaces. Go to the TeamSpace module. In the upper menu bar of the TeamSpace you can switch between the TeamSpaces for the Accounting and Backoffice divisions. In addition to the ACL based permission management used throughout ReportServer, TeamSpaces have a simple role based access scheme. In the demo system all users are assigned the guest role and thus have read-only access only. The roles user and manager allow users to make changes in the TeamSpace (e.g., add reports) and to manage who can access the TeamSpace. With this, TeamSpaces provide a flexible view on the data without disturbing the administrative structure which can only be accessed by administrators. Managing permissions (who can access which reports) is an administrative task which sets an upper bound on the data that can be accessed by a single user. That is, even if a user is given permissions to access a TeamSpace he or she might not have the access to access all reports in this TeamSpace.
Now switch to the TeamSpace Backoffice and open the ProductsByProductline report (in the Product Reports folder). This is an example of a graphical report created using the JasperReports library. ReportServer seamlessly integrates report types and engines of different vendors, which allows administrators to chose the engine that caters best to their current needs. Besides JasperReports, ReportServer integrates Eclipse BIRT and SAP Crystal for pixel-perfect reporting.
Go back to TeamSpaces and select the Accounting TeamSpace. Open the folder Sales and execute the report SalesInvoice. This is an example of a parameterized Eclipse BIRT Report. Every report type in ReportServer can be parameterized to allow users to adapt the displayed data. Various types of parameters provide a comfortable and integrated user experience. In this case you see a simple text input parameter.
Insert the order number 10100 and select preview (in the left column) to get a preview of the report.
Administration
Finally let us take a quick glance at the administration of ReportServer. For this pleas logoff and login again using the following credentials:
username: demoadmin
password: demoadmin
Select the administration module from the module navigation (on the top). Users, reports and data sources are all structured in a hierarchical tree structure. This allows for efficient management of objects as well as for a powerful permission scheme. The file server takes a special role. It contains resources used in reports (such as images) but also configuration files and ReportServer extension: scripts. These are used, for example, to customize the the user permissions in the online demo systems: for example users cannot change their passwords and scheduling is disabled. To get an idea about what is possible with ReportServer scripts, please have a look at the scripts active in the demo system as well as the scripting guide. The scripts are located in the fileserver in /bin/onstartup.d/. The script prohibiting users from changing their password is prohibitpasswordchange.groovy (this is only available on the online demo system).
With this our short introduction to ReportServer is at its end. We hope you got a first impression on the possibilities offered by ReportServer. Many features were only briefly covered or not covered at all simply to keep this guide short and readable. Here is a list of some of the features that we did not have the time to look at:
- Scheduling of reports
- Configuring of dynamic lists
- The Pivot mode of the dynamic list
- OLAP reporting
- Grid Editors
- How to administer ReportServer
- Extending ReportServer
- ...
For a list of features please also have a look at the ReportServer documentation.
If we have piqued your interest, feel free to explore the demo system on your own or simply download the software and go through all the aspects without any restrictions. Also please feel free to contact us directly or via our forum. We are looking forward to hearing from you.
ReportServer Permissions
ReportServer comes with a very powerful and flexible permission system based on Access Control Lists. This flexibility can, however, also be a bit overwhelming and with this tutorial we want to provide a gentle introduction into working with permissions. This tutorial is meant as an addition to the description of permissions in the Administration Guide.
Users, Organizational Units and Groups
ReportServer organizes users (as most other objects) in a hierarchical structure, the user tree. This allows you to organize users, for example, along the structure of your company: use folders and subfolders for departments and sub-departments and place users into their department folder. Folders within the user tree are also called Organizational Units or OU for short.
Once we have fixed the structure in which we organize users we can start configuring the permissions. For example, we can grant permissions directly to individual users (this is usually not advisable) or you can grant permissions to OUs in which case the permissions are granted to all the users that are within the OU. Note that a user is in the OU if its parent is the OU (i.e., the user is a child node) or if it is a descendant of the OU. For example, all users are within the User Root OU. Thus, if you want to grant a permission to every user, you could grant the permission to the User Root OU.
Besides granting permissions to users or OUs you can also grant permissions to Groups. Groups allow you to directly group users by adding them to a group. Then if you grant permissions to a group, all users within the group will inherit these permissions. When looking at a group configuration you might note that groups can not only contain users but also groups or even OUs. The easiest way to think about this is the following:
- When a user is added to a group it becomes part of that group.
- When a group A is added to a group B then all the users of group A become members of group B.
- When an OU is added to a group then all the users of the OU become members of the group.
Groups and Roles
From a security management perspective, the terms Group and Role basically refer to the very same thing. The difference is the perspective. A group puts the focus on the members (grouping together members that have something in common). An example could be the group of department heads. When talking about roles the focus is usually on a set of permissions which are grouped together. An example of a role could be a report designer.
When a system provides a role-based security system the roles are usually predefined and the configuration requires you to assign roles to users. ReportServer is does not have a role-based permission system. Instead it allows you to grant permissions to groups, OUs or even to individual users. This, however, means that you can easily simulate a role-based permission system in ReportServer. All that you need to do is to set up groups for the roles that you want to have in your system and then assign roles to users by adding users to those groups.
Two Default Roles
On a fresh installation of ReportServer you will note a folder within the user manager labeled DEFAULT_ROLES. This folder contains two pre-configured groups that can be used as the basis for a more elaborate role-based permission configuration.
The two default roles that are setup on installation are called Administrators and Users. As the name suggest the Administrators role (note that we speak of a role instead of a group now since we put the focus on the permissions rather than the members) grants full access to the system. That is, any user that is assigned the role (i.e., any user that is a member of the group Administrators) has full access to ReportServer. The Users role on the other hand assigns basic permissions that a "normal" user would need. These include access to the dashboard and teamspace module as well as the permissions to log on to the system and execute reports. We'll have a look at all the different permissions next.
In ReportServer 3.0.1 the role Users is missing the execute permission on the generic security target ReportServer Access. This permission is necessary to login.
Object Permissions and Generic Permissions
So far we have discussed how to organize users in OUs and groups. What we have not yet talked about are how to actually grant (or revoke) permissions.
When discussing ReportServer permissions we need to distinguish between two kinds of permissions: object permissions and generic permissions. An object permission is a permission to access a specific object where an object could be a report or file or datasource or even a user or OU. Object permissions are configured within the corresponding "object trees". That is, permissions on reports are configured within the "report tree" which you can access in the report management module. Generic permission, on the other hand, allow to provide access to specific functionality. For example, a user can have the (generic) permission to access the dashboard module. Generic permissions are configured within the Permissions module of the administration. Let us look at these first (see screenshot on the right).
Generic Permissions
When opening the (Generic) Permissions module you will see about 20 different permission targets on the left, such as,
- Administration
- Dashbaord
- Datasources
- ...
A full list with descriptions can be found in the Administration Guide.
The Administration target, for example, controls access to the administration module. Each of the permission targets consists of a single access control list (ACL) which in turn consists of zero or more access control entries (ACEs). In the above screenshot we see that the ACL consists of three ACEs:
#Folk | Access Type | Rights |
1 Administrators | ✔ | rwxdg |
2 IT | ✔ | r-x-- |
3 Admin, Demo | ✔ | r-x-- |
Each ACE consists of a folk (this is the object to which the permissions are granted/revoked, i.e., a user, group or OU), an Access Type this denotes whether the ACE grants or revokes access and a set of rights.
In the example, the ACE is for the group Administrators it is of type grant/allow (instead of revoke/deny) and it grants all rights: rwxdg. Here rwxdg is short for read, write, execute, delete, grant. The second ACE is for the OU IT and the third ACE is for user Admin, Demo.
As stated in the Administration Guide what is needed in order to use the Administration Module is read access on the Administration target. So how does ReportServer determine whether a user has read access? Let's consider a slightly different set of ACLs.
#Folk | Access Type | Rights |
1 Doe, John | - | -wxdg |
2 Administrators | ✔ | rwxdg |
We have a user John Doe and a group Administrators. Let us further assume that John Doe is also member of the Administrators group. The ACL in the example consists of two ACEs, the first one being directly for John, the second for the Administrators group. Note that the two ACEs are of different type: the first is a revoke ACE while the second a grant ACE.
In order to determine whether user John Doe has read access for the ACL ReportServer would proceed as follows. It would check each ACE in turn. For each ACE it checks whether the user in question (i.e., John Doe) matches the ACE's folk. So in our example, the first ACE would be a match. If so, it checks whether the asked for right is part of the ACEs set of rights. If this is the case it decides that the user has the permission in case the ACE is a grant ACE and it decides that the user does not have the permission in case it is a revoke ACE. Now in the example, the first ACE does not specify the read right so ReportServer would continue with the second ACE. Again it would check the folk and find that John Doe is part of the group Administrators. Furthermore the ACE specifies the read permission and, thus, ReportServer determines that the read right is granted. In other words, John Doe has read access.
What about write access? Here ReportServer would find that the first ACE matches John Doe and sets the write right and as the ACE is a revoke ACE ReportServer would rule that John does not have write access. Note that the second ACE is irrelevant in this case because the first matching ACE decides.
There is a final case that we have not yet considered and this is what ReportServer decides in case no ACE matches. In this case ReportServer takes the conservative road and denies access, that is, if no explicit access is granted, access is denied.
The Default Roles
The Users role is granted permissions for the following generic targets:
- Dashboard (access Dashbaord module)
- TeamSpace (access TeamSpace module)
- Scheduler (access Scheduler module)
- ReportServer Access (login to ReportServer)
The Administrators role is granted permissions for all the generic targets.
Object Permissions
Now that we have covered generic permissions lets turn to object permissions. Object permissions are used to control access to objects stored in the object trees:
- Users and Groups
- Datasources
- Reports
- File System
- Dashboard/Dadget Library
Each object within a tree (i.e., each folder and each report, each user, OU or group, etc.) has an attached ACL controlling the acces to that particular object.
ACEs for object permissions are similar to ACEs in generic permissions with one exception: you can leverage the hierarchical setup of objects and define whether ACEs are inherited by child objects. This allows you to also manage complex setups of permissions. Consider the example given in the screenshot of the Confidential folder. The setup here is:
ACEs
#Folk | Access Type | Inheritance | Rights |
1 Administrators | ✔ | ⇔ | rwxdg |
2 User Root | - | ⇔ | rwxdg |
Inherited ACEs
#Folk | Access Type | Inheritance | Rights |
1 Administrators | ✔ | ⇔ | rwxdg |
1 Users | ✔ | ⇔ | r-x-- |
1 ClassicModelCars | ✔ | ⇔ | r-x-- |
When checking whether a user has x-access (where x is either read, write, execute, delete, grant) on an object, ReportServer will go through the list of ACEs in sequential order starting with the ACEs that are specified at the object and then the ACEs which it inherits. In the example one can see that one of the top-level folders has some ACEs that are marked to be inherited by child folders. Thus, we have the inherited ACEs given access to users in the groups Administrators and Users, as well as the OU ClassicModelCars. Now for the confidential folder we wanted to revoke the access and only grant access to users in the Administrators group. Thus, two ACEs were specified at the object directly, the first one granting the access for any member of the Administrators group and the second revoking access for anybody else. Consequently the inherited ACEs will never be taken into consideration on this folder since the first two ACEs will be able to decide for any user whether it has access or not (every user is in the OU User Root).
The Default Roles
Finally, let us quickly establish the permissions that are set for the two default roles in a fresh installation of ReportServer. The Administrators role is, again, granted access to any object. For this the permissions on each root folder in each tree are set. The Users role is granted permissions for the following generic targets:
- Users and Groups (read access to all objects; this allows users to for example choose others as schedule targets)
- Datasources (no access)
- Reports (read and execute access)
- File System (no access)
- Dadget Library (read access)
So much for the introduction to ReportServer's permission system. For further information see the Administration Guide.
Setting up a Multi-Tenant System
In this tutorial we discuss how to set up a multi-tenant system in ReportServer.
ReportServer is built upon a very flexible permission scheme that allows to easily handle even the most complext requirements. For this tutorial we are going to consider the following scenario: A software vendor SV provides a set of services and wants to use ReportServer to allow its clients to access their data. We will demonstrate how to set everything up, so that the four clients A, B, C, and D get access. The data is structured such that every table in the datawarehouse is extended by an attribute CustomerID which specifies for which client the data record is.
The design goals of our setup are twofold:
- Each client should get access to the system and to their data (and only their data).
- We would like to reduce maintenance and administration overhead on the IT team of our software vendor SV as much as possible. In particular, reports should be generated only once and then be used for all clients. That is, the reports should not be duplicated for each client, but ReportServer should ensure that when client A is running a report that only the data for client A is included.
In order to set up the system we are going to use ReportServer's hierarchical model of users and ReportServer's user variable concept. In the following we discuss how to structure users in order to simulate separate sub systems for each client. We then discuss how to use user variables within reports to allow the reuse of reports across clients. Finally, we will discuss some more advanced use cases.
The Basic Setup
We start with a fresh and empty system. (For instructions on how to install ReportServer see the ReportServer Configuration Guide as well as the best practice tutorials for installing ReportServer on Windows and Linux. Or simply use one of the prepackaged installers.)
With a fresh setup, the user management module should contain two folders (or Organizational Units) called Administration and DEFAULT_ROLES, a single user and two groups (see the screenshot on the right). We assume a basic working knowledge of ReportServer's permission and user management system. For an introduction to ReportServer's permission management see the tutorial Getting Started with Permissions as well as the Administration Guide.
The idea with a multi-tenant system is to create an organizational unit per tenant. That is, we would add an organizational unit called Clients to the root folder and then beneath that one unit for each of the clients. Thus, the basic structure could be as follows
root +-Administration +-Clients +-A +-Alice +-Andreas +-B +-Bob +-Bill +-C +-Charly +-Chris +-D +-Dora +-Dean +-DEFAULT_ROLES
Each of the users should be granted the standard user permissions, that is, the user should be allowed to log in to the system, see the Dashboard and TeamSpace module, etc. For this, we are going to use the default role Users which grants these permissions. As the default user role provides a bit too many permissions, we will later need to adapt it, but for getting started, we will add the Clients OU (short for organizational unit) as a member to the Users group (see screenshot on the right).
Adding an OU to a group is equivalent to adding each and every user that is in the OU to the group. Thus, by adding the Clients OU to the Users group we have added all the users, Alice, Andrease, ..., Dora, and Dean, to the group. Also, if at any later point new users (or clients) are added, they directly get the correct permissions.
To test the current configuration we can login as one of the users. For this, we can use the switch user feature. By pressing CTRL+ALT+L (and if the current user has the necessary permissions) we can login as a different user and confirm that, say, Alice can login and see the modules Dashboard, TeamSpace and Scheduler. Once we log out, we will again be logged in with our actual user.
Let us quickly recap, what we have done so far. We have created an organizational unit for each of the clients, we have added the users for each client and we have granted permissions for all clients to access the system. In the next step we are going to set up a basic report and provide access to the report to all clients.
Adding Basic Reports
In the following we will create a report and provide access to the report to all clients. The report will be a Dynamic List on top of a database table called ClientData which has the following structure:
CREATE TABLE `ClientData` (
`id` mediumint(8) unsigned NOT NULL auto_increment,
`City` varchar(255),
`Name` varchar(255) default NULL,
`Company` varchar(255),
`SomeDate` varchar(255),
`SomeNumber` varchar(13) default NULL,
`ClientID` varchar(255) default NULL,
PRIMARY KEY (`id`)
) AUTO_INCREMENT=1;
The above SQL is for MySQL. The demodata was generated via http://www.generatedata.com/. The database script to load the demodata used in this tutorial can be downloaded from
Before we can create a report, we need to create a datasource pointing to the demodata. In our case, we create a new relational database datasource which we name Client Data Datasource. For more information about how to set up datasources in ReportServer see the Administration Guide.
Assuming the database is correctly set up, we can now create our first report. For this, we go to the report administration module, create a folder with the name Common Reports (which will contain reports that can be accessed by all the clients) and within that we create a new Dynamic List called DemoReport. As datasource we select the Client Data Datasource and as query we enter
SELECT * FROM ClientData
In order to confirm that the report is working properly, we run the report by double clicking it, selecting all available attributes and checking the preview, which should give the following picture (note the ClientID column which contains the information about to which client each record belongs).
Now that we have a report, let's provide access to the report to all of our clients. For this, we are going to create a TeamSpace for each of the clients. Of course, we could also create multiple teamspaces for each client but for this tutorial we go with one TeamSpace per client. To do this, we switch to the TeamSpace module and create the four TeamSpaces called
- Client A
- Client B
- Client C
- Client D
In each of the TeamSpaces we add the corresponding users to become members and import the report that we've created in the previous step.
To once again test that everything is set up correctly, we can switch to one of the users, say Alice, and confirm that she can access the TeamSpace of Client A and that she can there access the Demo Report.
What we have done until now, is that we have created TeamSpaces for each client and added the single report to each of the TeamSpaces. The latter step is of course problematic since now every client can see the data of everyone. We could mitigate this problem by creating four reports instead of one each restricted to the data of one client and then giving each client only the permission to access their report. This, however, creates lots of additional maintenance work which we would like to avoid and in the following section we will explain how we can restrict the view within a single report such that it only shows the data the current user is allowed to see.
Reporting with User Variables
Our Demo Report was set up with the simple SQL query
SELECT * FROM ClientData
Using parameters, we could, for example, add a dropdown box to the report which allows to select a client and then only show the data for that particular client. Assuming that we name the parameter P_CLIENT the adapted query could be:
SELECT * FROM ClientData WHERE CLientID = ${P_CLIENT}
Of course, adding a selection box to choose which client's data one wants to see does not solve the problem that in the current setup client A can see also the data of client B. The idea that we will use to restrict the access of each client will, however, use a similar approach. We will use a parameter to restrict the data records returned by the query, but we will not allow the user to select the parameter. Instead the parameter's value is automatically assigned depending on which user is currently logged in. The concept that makes this work are ReportServer's User Variables.
A user variable can be considered a property that is put on each user and that can be used by report designers when creating reports. In our case, we are going to use a very simple user variable which for each user will assign the client the user belongs to. That is, for Alice and Andreas the variable will be set to A, for Bob and Bill it will be B and so forth.
User variables are defined in the user management view at the root node. Once you select User root you should see a tab User variable management. This allows us to define what user variables are available. Currently ReportServer supports two types of user variables:
- Text variables
- Have a single value for each user
- List
- Allows to define a list of values for each user
In our example, we want to store for each user which client he or she belongs to. We thus create a text user variable with the name client. What we need to do next is to actually set the value for each user.
Naturally, explicitly setting the value for each and every user in the system would be a tedious endeavour. Thus, to make things easier, ReportServer allows you to use the hierarchical structure of the user tree to define variables. That is, you can not only specify the variable's value directly per user but also on an organizational unit. In this case ReportServer scans through all the user's parent OUs to find the value to use. The first one it finds, will be the one taken. This allows you to, for example, specify a default value on the root node and then overwrite this value for certain users or OUs deeper in the user tree.
To assign the users of each of the clients the correct value we set the user variable at each of the client OUs. That is, we select the OU for client A and then the tab User variables. There we select the previously created user variable client and set its value to A. We do the same for each of the other clients.
Now that we have the user variable defined, we need to adapt our report to use it. For this, we go to the Demo Report and open the parameters section. Add a parameter of type User Variable, name it Client ID with key P_CLIENT_ID and then in the specific properties of the parameter choose the previously created user variable client. You might also want to set the parameter to be hidden, as otherwise the user will see a parameter page with the value of the user variable. As this is the only parameter for the report and it is static (i.e., the user cannot change it) it does not make much sense to show it to the user. In other scenarios it might of course be useful to show the users what restriction is in place.
Now we are almost done, all that is left to do is to adapt the report's query to
SELECT * FROM ClientData WHERE CLientID = ${P_CLIENT_ID}
Let's test it. If we now log in with Alice and execute the report we expect to only see records for Client A.
If instead we log in with Bob's account, we only see records for Client B.
This concludes the basic set up of a multi-tenant system, so let us once more recapitulate the steps until now. For each client we created an organizational unit which holds all the users of that particular client. Additionally, we added a user variable and set it up so for each user it reflects to which client the user belongs. We have then employed this user variable in the definition of a report shared between all users so that each user can only see the data for the client they belong to.
In the following we will discuss a few more advanced topics. In particular, if you followed the above tutorial, then you might have noticed that the super user that you used to set up the entire system can no longer run the report. The reason being that it does not have the user variable set and thus ReportServer cannot execute the query
SELECT * FROM ClientData WHERE CLientID = ${P_CLIENT_ID}
Before we get to this, let us first present a few warnings on the permission setup and TeamSpaces.
Careful with Permissions
In a multi-tenanted (and also in a single-tenant) setup one might be tempted to use TeamSpaces for granting or revoking access to reports. This is, however, not a secure approach. Permissions, that is, the right to access and execute a report is granted in the report management module. If a user is given the permission to access a report there, that user can access the report even in case he or she does not have access to any TeamSpace. All they would need to know is the report's id.
The default user role grants access to all reports in the system and is thus usually not well suited for a multi-tenant system where you often still have some reports which are specific to certain clients. It is thus adviseable to use a more granular permission scheme and, for example, have a report folder for each of the clients to store client specific reports. Permissions for these folders should then be granted only to users that belong to the corresponding client.
Similarly, the default user role grants read access to all users in the system, meaning, for example, that a user could select any other user in the system as a recipient of a scheduler job, or if a user is a TeamSpace manager he or she could add any other user to the TeamSpace.
Let us quickly show, how we could adapt the permissions in our example. The first step would be to remove the permissions that the default user role grants on users and reports. For this we go to the report root folder and there to the permissions tab and remove the Access Control Entry for Users.
Let us stress that the default user role is nothing static which is fixed in ReportServer but simply a pregenerated group that is meant to simplify setting up permissions. You can (and should) adapt it to you own requirements.
Since we want all clients to be able to access the Common Reports folder we select it, and switch to the permissions tab. We add a new Access Control Entry and select the Organization Unit Clients as folk. Note that, as folk (the recipient of the permission) we can select either individual users, groups, or organizational units. As permissions we grant the read and execute permission.
Next we swith to the user management module and also remove the permissions that are granted to the default user role. Again we go to the root node (in this case User Root) and there to the permission tab to remove the Access Control Entry for Users. We then go to each client, that is, client A, B, C and D and there add an Access Control Entry granting users of the current client to read all other users of the same client.
Advanced Use Cases
After this short introduction there are two more advanced topics we would like to discuss. First we want to show the necessary steps to setup a user that sees the data for more than one client. For example, this could be an employee of our software vendor who is assigned to two clients and should also be able to see their data. The second point we want to briefly touch is how to use ReportServer scripts to perform some automated maintenance.
Access to Multiple Clients
As we've mentioned earlier with our set up only users that have the CLIENT_ID variable set will be able to run the Demo Report. In particular the root user would no longer be able to run the report. We could of course, provide a simple solution to this. We assign a default value, say DEFAULT to the user variable (that is we set a value at the Root node). Then we adapt the report's query to look somewhat like
SELECT * FROM ClientData WHERE CLientID = ${P_CLIENT_ID} OR ${P_CLIENT_ID} = 'DEFAULT'
While this solves the problem for administrators that should be able to see the report in its entirety, it is not clear how to let a particular user see the data for two clients, say A and B. One approach to solve this is to use a user variable of type list instead of a simple text variable. We can then set the variable to 'A' for users within client A, but for our special user we could set it to 'A|B'. (Here note that to enter multiple values in the list variable the values are separated by athe | (pipe) symbol.) When working with a list in the report's query we would then use the $X expression (see the Using Parameters from the administration guide)
SELECT * FROM ClientData WHERE $X{IN,ClientID,P_CLIENT_LIST}
Note that $X can only be used in Dynamic Lists and Jasper reports.
Automating ReportServer
In the final part of this tutorial we want to present a glimpse into what is possible using ReportServer scripts. What we would like to do is to automate the generation of TeamSpaces. That is, we want to create a script, that generates a TeamSpace for each client (unless the TeamSpace already exists) and synchronizes the users of that particular TeamSpace with all the users of the client. Of course, this could be extended by also checking reports which are in the TeamSpace etc. The possibilities are endless.
Following is the script. The explanation follows
import net.datenwerke.security.service.usermanager.UserManagerService
import net.datenwerke.rs.teamspace.service.teamspace.TeamSpaceService
import net.datenwerke.security.service.usermanager.entities.User
import net.datenwerke.rs.teamspace.service.teamspace.entities.TeamSpaceMember
import net.datenwerke.rs.teamspace.service.teamspace.entities.TeamSpaceRole
/* the client folder */
def clientOuID = 3484l; // note the trailing l to indicate that the number is a long
/* load services */
def teamSpaceService = GLOBALS.getInstance(TeamSpaceService.class);
def userService = GLOBALS.getInstance(UserManagerService.class);
/* load root user -> this user will be the owner of all teamspaces */
def owner = userService.getUserByName("root");
/* get client OU */
def clientOu = userService.getNodeById(clientOuID);
/* for each of the children, i.e., each client */
clientOu.getChildren().each { client ->
def name = client.getName();
/* try to load TeamSpace */
def ts = teamSpaceService.getTeamSpaceByName("Client " + name);
/* create teamspace if it does not exist */
if(null == ts){
/* create teamspace with owner as owner */
ts = teamSpaceService.createTeamSpace(owner);
/* set name and description */
ts.setName("Client " + name);
ts.setDescription("Users of Client " + name);
}
/* synchronize users -> remove all users from teamspace then add all users of client */
ts.setMembers(null);
/* add users */
client.getDescendants().each{
if(it instanceof User){
// add user as teamspace member
def member = new TeamSpaceMember(it);
member.setRole(TeamSpaceRole.USER);
ts.addMember(member);
}
}
}
return "done synchronizing";
In order to work with users, we will use the UserManagerService and for TeamSpaces we use the TeamSpaceService. Additionally, we need to import a few entity objects that we need to create (User, TeamSpaceMember and TeamSpaceRole). First we define the base OU, that is, the Clients folder in the user manager tree. We could load the OU by name but since that is not necessarily unique we opted to store its id. Then in lines 12 and 13 we load the two services that we are going to use in the script. To complete the script's setup we load the root user in line 16 and the actual OU object in line 19. Since each TeamSpace has an owner we need to assign this to some user and in the above script we will use the root user for this purpose.
The meat of the script starts in 22 when we loop over all children of the Clients folder. As each child represents a client we will create a TeamSpace for each of the children. The first step, in line 26 is to check whether the TeamSpace already exists. If this is not the case, we need to create it (lines 30 to 37). In the next step we remove all the members from the current team space (line 40). Finally, we loop over all the user objects beneath the current client folder and make them a member in the TeamSpace.
Admittedly, this was a rather quick explanation but we hope that this gives you some idea of what can be done with scripting. To get a better understanding of how scripting works, have a look at the scripting chapter in the administration guide as well as the ReportServer Scripting Guide.
This script could now be used by an administrator to update the current TeamSpace configuration, if users or clients changed, or it could even be scheduled and run on a regular basis, say once every morning.
This concludes this tutorial on multi tenant scenarios with ReportServer. As always we are happy about feedback.
Happy Reporting
Translating ReportServer — I18N
ReportServer is available in numerous languages. However, if you like to add a new language, or improve one of the existing translations this document outlines the necessary steps.
Let’s start by looking at some of the core concepts of how different language versions of ReportServer are implemented. The first thing you need to know is that within ReportServer every text, label or message is assigned a unique key. You can view these keys by starting ReportServer using the special “keys”-language. To select this language append the locale=keys parameter to your ReportServer URL (e.g. http://127.0.0.1:8080/reportserver/ReportServer.html?locale=keys).
This replaces all labels with their respective keys, for example the OK button on the logon screen is now no longer labeled “OK” but has a caption of “ok::net.datenwerke.gxtdto.client.locale.BaseMessages” which is the logon buttons key. The key consists of two parts: The part before the “::” is the module specific key, the second part is the name of the module. The combination of both form the canonical version of the key, which is unique throughout ReportServer, the module specific key (“ok”) on the other hand might be used in different modules.
The translations into the different languages for these keys are stored in textfiles which are included in the .jar archive for ReportServer. So for the login OK button there is a file named BaseMessages.properties (in the folder net/datenwerke/gxtdto/client/locale). Actually, there is not just one file but one for each supported language, so the English version is stored in the BaseMessages_en.properties file, Portugese in BaseMessages_pt.properties and so on. The format of the properties files is simple: each line defines one key and assignes the value for the respective language. So BaseMessages_en.properties contains
helpLabel=Help loadingMsg=Loading data ok=OK open=open prev=Previous progressMsg=Please wait...
On few occasions you might find a key with one or more parameters:
confirmDeleteManyMsg=Do you really want to delete {0} many objects?
The parameters go in curly brackets, the first parameter is {0}, the second is {1} and so on.
In theory that is all you need to know to edit ReportServer translations. But as you can imagine working with a huge number of text files and always moving them in and out of the archives isn’t much fun. So we created some tools that make the process much easier. The idea is that instead of having dozens of separate files we merge the translations for all modules and all languages into a single excel spreadsheet that has one key per row and shows all available translations side by side. The resulting file translation-template.xlsx is included in every ReportServer download and can be found in the resources directory.
To test your changes you can load the excel spreadsheet into ReportServer. To do so, open the terminal (Ctrl+Alt+T) and issue the command:
localization importMessages
This will look for the translation-template file in ReportServer's resources directory and read in the new translations. After you executed the command, reload your browser. You should now see the updated translations.
There are some drawbacks to this method, that are important to be aware of:
Translations loaded via this method are not permanent. That is, if you restart the server the default translations are loaded. To be honest this isn’t entirely accidental – we want to encourage you to share your translations with all users of ReportServer. So if you created a new language version, or improved an existing one, send us your excel spreadsheet and we will include it into the official build. If you send us translations please include a short statement, that you provide the translation under a creative commons zero (CC0 – http://creativecommons.org/publicdomain/zero/1.0/) license. There are some few labels, for which the translation does not show up when loaded via this method. It’s really just a few, but don’t be surprised, if that happens.
Theming — Adapting ReportServer's UI
With ReportServer 3.0 Enterprise Edition we have added capabilities to style the user interface to allow to adapt the look and feel to match your corporate identity. In this tutorial we want to show how to develop a new theme and provide some helpers that make theme development easier.
The Configuration File theme.cf
The ReportServer theme is controlled via the configuration file /etc/ui/theme.cf which in its basic form looks something like:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<theme type="default">
<logo>
<!-- define which logos to use -->
</logo>
<colors>
<!-- define names for colors to be used -->
</colors>
<colorMapping>
<!-- define mapping of colors to elements -->
</colorMapping>
<css>
<!-- define additional css rules -->
</css>
</theme>
</configuration>
The <theme> element has a single attribute type which controls the ReportServer base theme that is loaded. Currently two base themes are available which are called:
- default
- The standard ReportServer theme.
- borders
- The standard theme with some additional borders.
The <logo> tags allow to define the logos that are used for the login screen, the logo on the top left of the module bar as well as a logo to be used in the report documentation that is displayed in the TeamSpace. Let's consider the following directive:
<logo>
<login>
<html><![CDATA[<b>THE LOGIN LOGO</b>]]></html>
<width>200px</width>
</login>
<header>
<html><![CDATA[<b>THE HEADER LOGO</b>]]></html>
<width>185px</width>
</header>
<report>Some URI pointing to a Logo to be used in the report documentation</report>
</logo>
The first directive (logo.login) allows to specify an HTML snippet to be used on the login page. The width defines the width for the resulting html element. The header directive, on the other hand, controls the logo in the top left corner after login. Finally, the <report> tag can contain a URI that points to an image to be used within the report documentation.
Remember that when changing the config you need to issue the config reload command on the terminal (press CTRL+ALT+T to open the terminal) for the changes to take effect.
The <colors> and <colorMapping> tags make up the main part of a new theme. Here you can define colors (within the <colors> tag) and then assign these to various elements (within the <colorMapping> tag). A simple color definition could be
<color name="white" color="#FFFFFF"/>
This could then be assigned to the background via
<map useFor="body.bg" colorRef="white"/>
which assigns the color white to any element that uses body.bg as its color. The resulting theme.cf would then be:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<theme type="default">
<logo>
<!-- define which logos to use -->
</logo>
<colors>
<!-- define names for colors to be used -->
<color name="white" color="#FFFFFF"/>
</colors>
<colorMapping>
<!-- define mapping of colors to elements -->
<map useFor="body.bg" colorRef="white"/>
</colorMapping>
<css>
<!-- define additional css rules -->
</css>
</theme>
</configuration>
Besides defining a color mapping by referencing a previously defined color, you can also directly specify a color via the color attribute. A second alternative is to set the mapping of one element group to follow another element group. For this use the sameAs attribute.
<colorMapping>
<!-- define mapping of colors to elements -->
<map useFor="body.bg" colorRef="white"/>
<map useFor="tbar.btn.bg" sameAs="body.bg"/>
<map useFor="terminal.text" color="#00B000"/>
</colorMapping>
If we can change the background color via assigning a custom color to body.bg this immediately raises the question, which element groups can we assign colors to? To get a list of the currently available element groups, you can look through ReportServer's css (and scan for css comments /*col:NAME*/) or use the following script:
import net.datenwerke.gf.service.theme.ThemeService
new TreeMap(GLOBALS.getInstance(ThemeService).colorMap).each{ k,v ->
tout.println "$k: $v"
}
null
The script is available here.
Currently, this would tell us that the following groups are available (here with an added description):
Element Group |
Color |
Color Code |
Description |
bg |
#B8BDC0 |
The background |
|
text |
#000000 |
Text when on background (bg) |
|
bg.light |
#FFFFFF |
Light variant of background. For example used as background of panels |
|
light.text |
#000000 |
Text on light background |
|
bg.shaded |
#EEEEEE |
A shaded variant of the background. Used, for example, as the background for toolbars |
|
shaded.text |
#666666 |
Text on shaded background |
|
bg.dark |
#6D708B |
A darker variant of the background color |
|
border.light |
#B8BDC0 |
A color used for (thin) borders on light background |
|
header.bg |
#132834 |
The background of the top module bar (the header) |
|
header.text.active |
#FFFFFF |
Text color of active modules and logo |
|
header.text.inactive |
#B8BDC0 |
Text color of inactive modules |
|
header.text.right |
#B8BDC0 |
Text color of user name and profile |
|
hl.dark.bg |
#3E4059 |
A dark highlight color |
|
hl.dark.text |
#FFFFFF |
Text on the dark highlight |
|
hl.light.bg |
#DFE0EB |
A lighter highlight color. |
|
hl.light.text |
#000000 |
Text on the lighter highlight color. |
|
tbar.btn.bg |
#B8BDC0 |
Background color of buttons in toolbars. |
|
terminal.bg |
#000000 |
Background of the terminal. |
|
terminal.hl.bg |
#6D708B |
highlighted background of the terminal |
|
terminal.link |
#FFFFFF |
Links on the terminal |
|
terminal.text |
#00B000 |
Standard text on the terminal |
We have tried to group together elements with a similar exposure to make theming easier. At the extreme we could have given a name to every element, which, while yielding complete flexibility, would make theming much harder. However, we'd be happy to receive feedback if the above grouping causes you problems. Note that even though our grouping is not too granular, you can easily dive into the css and overwrite the styles of any specific element.
This is what the last part of the config is for. If you are not familiar with CSS, simply skip the following paragraphs.
The <css> tags enclose any additional styles that you might want to add. These are added at the very end, thus allowing you to overwrite each and every single element if you wish. Consider, for example, the teamspace grid. Here, the grid header is using the dark highlight color. The CSS from the ReportServer's default stylesheet is:
.rs-teamspace-list .rs-grid-head {
border: none !important;
background: none repeat scroll 0 0 #3E4059 /*col:hl.dark.bg*/ !important;
}
.rs-teamspace-list .rs-grid-head td{
border-bottom: none !important;
border-color: #FFFFFF /*col:hl.dark.text*/ !important;
color: #FFFFFF /*col:hl.dark.text*/ !important;
}
If we instead wanted to use a shaded background, we could add the following directive to the theme.cf config file
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<theme type="default">
<logo>
<!-- define which logos to use -->
</logo>
<colors>
<!-- define names for colors to be used -->
<color name="white" color="#FFFFFF"/>
</colors>
<colorMapping>
<!-- define mapping of colors to elements -->
<map useFor="body.bg" colorRef="white"/>
</colorMapping>
<css>
<!-- define additional css rules -->
.rs-teamspace-list .rs-grid-head {
background: none repeat scroll 0 0 #EEEEEE /*col:bg.shaded*/ !important;
}
.rs-teamspace-list .rs-grid-head td{
border-bottom: none !important;
border-color: #666666 /*col:shaded.text*/ !important;
color: #666666 /*col:shaded.text*/ !important;
}
</css>
</theme>
</configuration>
There is one thing to note. In the above above directive the css comment /*col:NAME*/ denotes a marker for ReportServer what color to choose. If present and the config file contains a custom color for bg.shaded (or shaded.text) then ReportServer would also there overwrite the color. If you want to fix the color, simply remove the CSS comment.
So much for the config file. In theory, you can now start theming. However, we can make our lives even easier with a bit of scripting.
Automatic Reload of Config File
When changing the configuration, the changes are only picked up, after we issue a config reload on the terminal. In the following we will write little script that will do this automatically. For this, we will listen to change events on files and if we detect a change on file theme.cf then we'll trigger a reload of the config file. Let's have a look at the script (for an introduction to scripting see the Administration and Script guides.
import net.datenwerke.rs.fileserver.service.fileserver.entities.FileServerFile
import net.datenwerke.security.service.eventlogger.jpa.MergeEntityEvent
import net.datenwerke.rs.utils.eventbus.EventHandler
import net.datenwerke.rs.utils.config.ConfigService
def HANDLER_NAME = "FORCE_THEME_RELOAD_HANDLER"
def configServiceProvider = GLOBALS.getProvider(ConfigService.class)
def callback = [
handle: { e ->
if(null == e || ! (e instanceof MergeEntityEvent))
return
def file = e.getObject();
if(! "theme.cf".equals(file.getName()))
return
configServiceProvider.get().clearCache("ui/theme.cf")
}
] as EventHandler
GLOBALS.services.callbackRegistry.attachObjectEventHandler(HANDLER_NAME, MergeEntityEvent, FileServerFile, callback)
We are interested in change events on files (internally represented by net.datenwerke.rs.fileserver.service.fileserver.entities.FileServerFile objects). For this, we use the callbackRegistry in line 22 to attach an ObjectEventHandler for so called MergeEntityEvents (represented by class MergeEntityEvent.class).
ReportServer will throw events whenever an object is changed, and the above registration ensures that our script is called whenever an object of type FileServerFile is merged (i.e., changed). Now the callback itself implements the single method handle which takes one parameter, the event which should be of type MergeEntityEvent (as we only registered for these). Then in the remainder (lines 14 to 18) we simply check if the merged file is called theme.cf and if so we ask the ConfigService (loaded in line 8) to clear the cache for the theme config.
When registering to receive object events you need to ensure, that your code does not throw any exceptions, as these are executed within ReportServer's main thread and an error in this case would interrupt the storage of the changed file.
Now, if you execute the script then you will notice that after changing the configuration it is sufficient to reload the browser. The changes are then directly picked up and the new theme is loaded.
This already makes theme development a few clicks faster. In the next section we introduce one more helper, to make it a blast.
Reloading new Theme without Reloading Browser
Having to reload the browser is a bit tedious, so in this final part we are going to add a button to the toolbar on the theme.cf config file which instantly reloads the theme. Following is the completed script:
import net.datenwerke.rs.scripting.service.scripting.extensions.*
def service = GLOBALS.services['clientExtensionService']
def entry = new AddToolbarEntryExtension()
entry.setLabel("Reload theme")
entry.setIcon("refresh")
entry.setToolbarName("fileserver:admin:view:toolbar")
entry.setJavaScript("""
\$wnd.\$("#rs-the-theme").remove();
\$wnd.\$('head').append( \$wnd.\$('<link id="rs-the-theme" rel="stylesheet" type="text/css" />').attr('href', 'reportserver/rstheme?' + Math.random() ) );
""")
entry.addDisplayCondition(
new DisplayCondition("path", "/fileserver/etc/ui/theme.cf")
)
service.addToolbarEntry(entry)
The script uses the ClientExtensionService (loaded in line 3) which allows to add buttons to various toolbars, or menus. In our case, we want to add a button to the toolbar in the FileServer when the theme.cf config file is active. For this we create an AddToolbarEntryExtension (lines 5 to 12) object which we give a label and an icon (choose any Font Awesome icon).
In line 8 we tell the extension that it should apply to the toolbar identified by fileserver:admin:view:toolbar (the file server's toolbar). Without any additional conditions, the button would now appear for every object in the toolbar. Thus, to only show it on file theme.cf we add a display condition and specify the specific path (lines 14-16).
The actual work (replacing the theme) is done when pressing the button and for this we use a little bit of javascript which we add to the entry in lines 10 and eleven. ReportServer's CSS is loaded into a link tag with the id rs-the-theme. So what we need is to remove it, and replace it with a fresh tag. For this we use jQuery. Removing the tag is done by
$("#rs-the-theme").remove();
This is what we do in line 10, or almost. The reason why the above does not work directly is that ReportServer is written in GWT and the javascript is run within the GWT context (i.e., a nested frame). Thus, to access the actual context and its window object we use the variable $wnd which is provided by GWT for this purpose, making the call.
$wnd.$("#rs-the-theme").remove();
Now noticing that $ is a special instruction in groovy strings, we need to escape the $-signs.
In line 11 we add a fresh link tag to the head element which reloads the theme. And with this we are at the end of this tutorial.
Happy Theming
Getting Started with Scripting
In this tutorial we introduce the scripting capabilities of ReportServer Enterprise Edition and look at four standard use cases for scripting: Script Reports, Script Datasources, Maintenance Scripts and Script Extensions. For a detailed introduction into scripting we refer to the ReportServer Scripting Guide. For this tutorial we assume a basic knowledge of programming, preferably in Java or Groovy.
Scripting 101
ReportServer comes with a scripting interface that allows you to run scripts written in the Groovy language in the context of ReportServer. ReportServer scripts are stored in the internal file server. In order to be executable they have to be placed somewhere beneath the bin folder. Usually, when working with scripts you will use the ReportServer terminal. You can open the terminal by pressing CTRL+ALT+T.
The ReportServer terminal mimicks a standard unix terminal. You can navigate to specific folders via the cd (change directory command) and show the contents of the current folder via the ls (list) command. Running the ls command on a freshly opened terminal should return the following output.
reportserver$ ls
datasources tsreport
reportmanager usermanager
dadgetlib
fileserver
Scripting 101
ReportServer knows various virtual file systems for the various components present in ReportServer. That is, for users, reports, datasources, TeamSpaces and dashboards there exists a virtual file system that you can access via the terminal. In addition, the fileserver entry provides the root to the internal file system which we are going to use to store and run scripts.
To move to the root of the internal file server issue the following command.
reportserver$ cd fileserver/
The terminal supports auto completition. To try to autocomplete a command, press the TAB key.
If you again run the ls command you should now see the contents of the root folder of the internal file system (compare the output to the view provided in the administration module; they should be identical). Scripts need to be placed somewhere beneath the bin folder. If the folder does not yet exist, you can create it via
reportserver$ mkdir bin
Note that, contrary to standard file systems ReportServer currently allows to have two (or more) identically named files (or folders) within the same folder. Hence mkdir bin will not throw an error message even if a folder with the name bin already exists. It is generally adviseable not to make use of this feature and we note that this might also change in future versions of ReportServer.
Let us create a directory called getstarted beneath the bin folder and move there.
reportserver$ mkdir /fileserver/bin/getstarted reportserver$ cd /fileserver/bin/getstarted/
In order to work with text files in the terminal there are two important commands that you should know: createTextFile takes a single argument and will create a new text file and open it for editing. editTextFile opens an existing file for editing. Again note that createTextFile, similarly to mkdir, does not check whether the file already exists, and allows you to create multiple files with the same name. Consider the following sequences of commands
reportserver$ createTextFile test.txt
file created
reportserver$ createTextFile test.txt
file created
reportserver$ ls -l
10469 test.txt FileServerFile
10472 test.txt FileServerFile
reportserver$
By running the command createTextFile test.txt twice we have created two files with name test.txt. If you run the ls command with the -l flag, then you are given additional details, such as the ID of each element. In order to remove a file you can use the rm command. This command takes a single argument which points to the file you want to remove. This can be the name of the element in which case the first found element is removed. Alternatively you can remove an element via its id as
rm id:10469
Thus, the following sequence of commands clears our getstarted directory again:
reportserver$ rm id:10469
reportserver$ rm test.txt
reportserver$ ls
reportserver$
It is now time to create our very first script. Let us create the traditional hello world script.
reportserver$ createTextFile hello.groovy
Scripts by convention are given the suffix .groovy or .rs. Scripts can return a single object which if possible is printed to the console. Thus, to print the string Hello World onto the terminal we can issue a single return statement
return "Hello World"
Scripts can be executed directly from the terminal via the exec command. Sure enough, running exec hello.groovy writes Hello World onto the console.
reportserver$ exec hello.groovy
Hello World
Congratulations, you have just run your first ReportServer script. Before we dig deeper, let us discuss one of the most important aspects of scripting: debugging.
Error Handling
Let's be honest. Writing scripts that are flawless on the first attempt is almost impossible. Especially, once you are starting on a bit more complex scripts. Thus, being able to read error messages and find the bugs is one of the most important skills you will need to learn. A solid background in programming is, naturally, very helpful for this. However, don't be discouraged, even if you do not have a strong programming background. ReportServer tries to simplify error messages and point you towards the line that contains the error. If that fails, java stacktraces, though long and bulky at first, will become easier to read with time. And finally, the ReportServer community will help you, if you get stuck. So, to prepare you for this we start our scripting exploration with looking at how things go wrong. Before you try out the example, we suggest to read to the end of this section.
So how do error messages look like? Let us create a new script called error1.groovy (createTextFile error1.groovy) with the following contents (note that this script has a small bug):
import net.datenwerke.rs.core.service.reportmanager.ReportService;
def reportService = GLOBALS.getInstance(ReportService.class);
reportService.getAllReports().each { report ->
tout.println(report.getname());
}
return null;
Don't worry, if there is lots that you do not understand at this point. This script is, indeed, much more complex than our previous hello world script. In short, it uses the ReportService that is provided by ReportServer to access reports that are stored in the system. We use the service to get a list of all the reports in the system (getAllReports) and then for each of the reports we run the following Groovy closure:
tout.println(report.getname());
A Groovy closure is a small code snippet that can be stored in a variable or passed as an argument to a function. A closure is wrapped in curly brackets, and consists of one or two parts. There can be an optional first part to define a (comma separated) list of parameters. In the above example this is
report ->
where we specify that there exists a single parameter which we name report. Then there is the (mandatory) closure body, in our case
tout.println(report.getname());
Here we use the special object tout which is given to every ReportServer script and allows to write to the terminal via its println method.
So let's put this together. The script uses the ReportService to get all the reports in the system and then for each of the reports it executes the code provided by the closure which simply accesses the report's name and prints it to the console. Finally, since the script does not return anything we add a return null to the end of the script.
As mentioned earlier, the script contains a small bug, and if we execute it ReportServer will print the following error message.
reportserver$ exec error1.groovy
Script execution failed.
error message: No signature of method: net.datenwerke.rs.base.service.reportengines.table.entities.TableReport.getname() is applicable for argument types: () values: []
Possible solutions: getName(), getName(), setName(java.lang.String), getType(), getAcl(), getAt(java.lang.String) (groovy.lang.MissingMethodException)
script arguments:
file: error1.groovy (id: 10702, line 6)
line number: 6 (5)
line: tout.println(report.getname());
Let us go through this line by line. The first line tells us, that the script execution failed. Ok, so this is what we expected. The next line contains the error message, and this often already pinpoints the problem. In this case the message says that there is no signature of method
net.datenwerke.rs.base.service.reportengines.table.entities.TableReport.getname()
Here everything until the last full stop identifies a class, and the final part a method. In short, the error message says that the (...)TableReport class (which represents a Dynamic List) does not have the method getname().
The error message then continues to give some information about the script that was executed, printing the arguments with which the script was called, a line number in which the error is suspected (line 6), and the line in question.
To rectify the error, we need to change report.getname() into report.getName(). With this change, the code the runs smoothly.
Now, at times the information provided by ReportServer does not allow to pinpoint the error. In this case it can be helpful to run the script with the -t flag which tells ReportServer to output the entire stack trace in case an error occurs. If we run our original error1.groovy script with the -t flag, we will get the following response:
reportserver$ exec -t error1.groovy
net.datenwerke.rs.scripting.service.scripting.exceptions.ScriptEngineException: javax.script.ScriptException: groovy.lang.MissingMethodException: No signature of method: net.datenwerke.rs.scriptreport.service.scriptreport.entities.ScriptReport.getname() is applicable for argument types: () values: []
Possible solutions: getName(), getName(), setName(java.lang.String), getType(), getAcl(), getAt(java.lang.String)
------- SCRIPT ERROR INFO -------
Script execution failed.
error message: No signature of method: net.datenwerke.rs.scriptreport.service.scriptreport.entities.ScriptReport.getname() is applicable for argument types: () values: []
Possible solutions: getName(), getName(), setName(java.lang.String), getType(), getAcl(), getAt(java.lang.String) (groovy.lang.MissingMethodException)
script arguments:
file: error1.groovy (id: 17459, line 6)
line number: 6 (5)
line: tout.println(report.getname());
at net.datenwerke.rs.scripting.service.scripting.engines.GroovyEngine.eval(GroovyEngine.java:79)
at net.datenwerke.rs.scripting.service.scripting.ScriptingServiceImpl.executeScript(ScriptingServiceImpl.java:217)
at net.datenwerke.rs.scripting.service.scripting.ScriptingServiceImpl.executeScript(ScriptingServiceImpl.java:263)
at net.datenwerke.rsenterprise.license.service.EnterpriseCheckInterceptor.invoke(EnterpriseCheckInterceptor.java:35)
at net.datenwerke.rs.scripting.service.scripting.ScriptingServiceImpl.executeScript(ScriptingServiceImpl.java:317)
at net.datenwerke.rsenterprise.license.service.EnterpriseCheckInterceptor.invoke(EnterpriseCheckInterceptor.java:35)
at net.datenwerke.rs.scripting.service.scripting.ScriptingServiceImpl.executeScript(ScriptingServiceImpl.java:288)
at net.datenwerke.rsenterprise.license.service.EnterpriseCheckInterceptor.invoke(EnterpriseCheckInterceptor.java:35)
at net.datenwerke.rs.scripting.service.scripting.terminal.commands.ExecScriptCommand.doRollbackExecute(ExecScriptCommand.java:335)
at com.google.inject.persist.jpa.JpaLocalTxnInterceptor.invoke(JpaLocalTxnInterceptor.java:66)
at net.datenwerke.rs.scripting.service.scripting.terminal.commands.ExecScriptCommand$1$1.doFilter(ExecScriptCommand.java:272)
at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:66)
at com.google.inject.servlet.FilterDefinition.doFilter(FilterDefinition.java:168)
at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:58)
at com.google.inject.servlet.FilterDefinition.doFilter(FilterDefinition.java:168)
at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:58)
at com.google.inject.servlet.FilterDefinition.doFilter(FilterDefinition.java:168)
at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:58)
at com.google.inject.servlet.ManagedFilterPipeline.dispatch(ManagedFilterPipeline.java:118)
at com.google.inject.servlet.GuiceFilter.doFilter(GuiceFilter.java:113)
at net.datenwerke.rs.scripting.service.scripting.terminal.commands.ExecScriptCommand$1.call(ExecScriptCommand.java:263)
at net.datenwerke.rs.scripting.service.scripting.terminal.commands.ExecScriptCommand$1.call(ExecScriptCommand.java:1)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.lang.Thread.run(Thread.java:745)
Caused by: javax.script.ScriptException: groovy.lang.MissingMethodException: No signature of method: net.datenwerke.rs.scriptreport.service.scriptreport.entities.ScriptReport.getname() is applicable for argument types: () values: []
Possible solutions: getName(), getName(), setName(java.lang.String), getType(), getAcl(), getAt(java.lang.String)
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:347)
at org.codehaus.groovy.jsr223.GroovyCompiledScript.eval(GroovyCompiledScript.java:41)
at net.datenwerke.rs.scripting.service.scripting.engines.GroovyEngine.eval(GroovyEngine.java:74)
... 23 more
Caused by: groovy.lang.MissingMethodException: No signature of method: net.datenwerke.rs.scriptreport.service.scriptreport.entities.ScriptReport.getname() is applicable for argument types: () values: []
Possible solutions: getName(), getName(), setName(java.lang.String), getType(), getAcl(), getAt(java.lang.String)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:56)
at org.codehaus.groovy.runtime.callsite.PojoMetaClassSite.call(PojoMetaClassSite.java:46)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:110)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:114)
at Script6$_run_closure1.doCall(Script6.groovy:6)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:324)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:292)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1016)
at groovy.lang.Closure.call(Closure.java:423)
at groovy.lang.Closure.call(Closure.java:439)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2027)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2012)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2053)
at org.codehaus.groovy.runtime.dgm$162.invoke(Unknown Source)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:271)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:53)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:110)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:122)
at Script6.run(Script6.groovy:5)
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:344)
... 25 more
That is quite a bit longer, than the standard error message. The first part, however, is identical, it contains the summary which we already saw earlier. Then, starting from line 13, we have the stack trace. The first line of the stack trace gives you the outer most error location which in our case is the location in ReportServer that calls the erroneous script: the GroovyEngine. This doesn't provide much information and similarly the following lines which provide information on the call stack that led to executing the script won't hold much information. The interesting bit starts with line 37 and the Caused by clause. With each Caused by we are getting one step closer to the original error position, so usually the last one is the one you should be looking for. We find the last Caused by in line 43 and it tells us that the error message originated in the call
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:56)
From there we can trace the error backwards by looking at the following lines. We get our hit in line 50.
at Script6$_run_closure1.doCall(Script6.groovy:6)
Anything that is prefixed Script refers to a dynamic groovy file, and since there is only a single file in our example, Script6 represents our script error1.groovy. The last number in each line represents the line number of the corresponding command, that is, line 50 of the stack trace tells us that the last line that was executed in our script was line 6.
This has been quite a lot of information, and don't worry if the stack trace still looks like a huge amount of nonsense. Usually, the summary message is sufficient to trace an error, and, with time, you will get used to reading stack traces.
Groovy with ReportServer 101
Now that we have an idea of how to deal with errors, let's have a look at the basic Groovy that you usually need when scripting. Being a fully matured programming language we can't hope to give you a complete picture. The good news is, there is tons of material on the net that tells you about how to get started with Groovy, and once you've mastered the basics, how to dig deep. A good start is the official Groovy website.
We have already seen the traditional Hello World program
return "Hello World"
Now, if you open the next best Groovy tutorial the hello world example will be slightly different. It will tell you that to print Hello World to the console you have to write
println "Hello World"
and they'd be right if you were using Groovy in a standard environment. Since we run Groovy within the context of ReportServer things are sometimes a bit different and this is one of these times. The println command tells groovy to print the following string to its standard output stream. Usually, that would be the console, for example, of your development environment. In ReportServer, the standard output stream of scripts is sent to the ReportServer log files and thus, if you execute the above script, although you will not see any message on the terminal window you will see the string Hello World if you expect the log files.
In order to write to the terminal in ReportServer you need to use the tout object which is short for (Terminal Output). Thus, if we translate the above to ReportServer we get
tout.println "Hello World"
If we now execute this script (assuming it is called hello.groovy), we get the expected result:
reportserver$ exec hello.groovy Hello World
Additionally to writing directly to the console, by convention ReportServer will print the result of the script (whatever the script returns) onto the console which is why
return "Hello World"
produces an equivalent result. One thing to note is that the return key word is not necessary. In other words, the script will return the result of the last instruction. So the following would again have an identical effect:
"Hello World"
Now that we know how to print messages on to the terminal, let us introduce variables. Variables are defined via the def key word. The following code
def myVariable = "I am a variable";
tout.println myVariable;
prints I am a variable. Statements in Groovy can be terminated by a semicolon (as in the above example) but they don't have to. So equivalently we can write
def myVariable = "I am a variable"
tout.println myVariable
Naturally, variables can hold not just strings but also, for example, numbers and we can work with variables.
def a = "I am a String"
a = 19 // once we have defined a variable, no more def
/* this is a multi-line
comment */
// this is a single line comment
def b = 23
def c = a + b
tout.println c
The above code first defines a variable a to be the string "I am a String". It then changes a to 19, defines b as 23 and c as the sum of a and b. Also note the two ways of writing comments in Groovy via a double slash and slash-star. Strings as we have seen are enclosed in double quotes. Additionally, Groovy knows the following syntax for strings.
def a = "I am a String"
def b = 'I am also a String'
def c = """I am a
multi-line
String"""
tout.println c
When using double quoted strings, you can also refer to other variables like so
def firstname = "John"
def lastname = 'Doe'
tout.println "Hello ${firstname} ${lastname}"
Groovy has quite a few tricks when it comes to composing strings so you should definitely have a look at the templating capacitites that groovy has to offer (a good start is to search for "Groovy Templates").
Groovy supports the standard C/Java-like conditional and loop expressions.
def a = 5
if(a < 5) {
tout.println "small number"
} else {
tout.println "big number"
}
for(def i = 1; i < a; i++)
tout.print "."
tout.println "\nThere we printed some dots in a single line"
You will probably often use lists and maps.
def list = ["I", "am", "a", "list"]
tout.println list.size() // outputs 4
tout.println list[1] // outputs am
def map = [ "I" : "You", "am" : "are"]
tout.println map.size() // outputs 2
tout.println map["am"] // outputs are
So be sure to check out the Groovy Collections as they have quite some nifty functionality. What we already saw in the error handling section was an example of how to iterate over a list of items via the each method of a list.
def list = ["I", "am", "a", "list"]
list.each { it ->
tout.println it;
}
return null;
This prints each item of the list onto a single line. That is, the each method takes as argument a Groovy Closure which is executed for each of the lists items. For further information on the each method and other helper methods to use with lists see the Groovy doc for lists.
When you want to work with ReportServer or advanced Groovy objects you need to import them
import groovy.xml.MarkupBuilder
def writer = new StringWriter();
def hb = new MarkupBuilder(writer);
hb.html {
head {
title "Some document title"
}
body {
h1 "Some heading"
p "And a corresponding paragraph"
}
}
tout.println writer
Now that was some shorthand to write an HTML document the Groovy way with the MarkupBuilder object. The StringWriter object is part of the java.io package which is imported by default by Groovy and we could thus use it directly without additionally importing it. For more informatin on Groovy program structure and default imports look here.
So much for our little Groovy 101. As we mentioned at the beginning, this barely scratches the surface, but we hope that it helps to understand the remaining parts of this tutorial and that it encourages you to search the net for further information on Groovy. There is tons out there. And, if you get stuck, try our community forum or, if it is a programming question rather than a ReportServer question, have a look at stack overflow.
Installing Libraries
Before we look at what you can do with scripts in ReportServer let us briefly discuss how to install additional libraries for scripts that are not yet part of ReportServer. For example, if you want to access a REST webservice then you might want to use the Unirest library.
In order to install additional libraries they need to be placed on ReportServer's classpath. The easiest way to do so is to use the lib directory of the external configuration dir. In case you used the Bitnami installer this would be
INSTALL_DIR/apps/reportserver/reportserver-conf/lib
If you did a manual installation and followed one of our tutorials for Linux or Windows then this would be
/opt/reportserver/lib
or
C:\Program Files\reportserver\lib
Once you've placed the .jar library files into the right location, you will need to restart ReportServer to make it aware of the new library.
Script Reports
The first use case we want to disucss are script reports. Script reports are the swiss army knife of reporting. They can be anything from a static list of data to a complex HTML5 application. The idea is that you use a ReportServer script to generate the output and hence, anything that you can program can be a report. So let us start with a simple Hello World example once more.
Hello Script Report
For a script report we need two ingredients, a script and a script report. Let us begin by preparing the script. We create a new script called helloreport.groovy with the following contents.
return """<html>
<head>
<title>A hello world</title>
</head>
<body>
<h1>Hello Script Report</h1>
</body>
</html>"""
What we have done is to simply return a static HTML page. While script reports can generate arbitrary outputs (e.g., PDFs, Excel or even custom byte formats) HTML is necessary when you want to present the result within ReportServer's report preview. Of course, just like other reports, a script report can also offer multiple output formats. We'll get to that in a moment.
Now that we have the script, the next step is to create the script report. For this we go to the Reports section in the administration and create a new Script report. What we need to configure for now is the name of the report (e.g., Hello Script Report) and we need to set the script to our previously created helloreport.groovy script. We can leave the other config options (datasource, arguments, export formats) blank for the moment.
Once we submit the report configuration and then open the report in ReportServer you should see a blank page with a single headline: Hello Script Report. Congratulations, you have just created your first script report.
Permissions. As with every other report in ReportServer script reports can only be executed by users that have the correct permissions. Note, however, that in case of a script report it is not sufficient to have the execute permission on the report object but that the user also needs the execute permission on the corresponding script.
Parameters
Naturally, script reports can make use of parameters. So let us add a a simple text parameter with key name to our script report. Now the question is, how can we access the parameter from within the script. To access parameters, ReportServer adds the special variable parameterMap to each script when it is executed via a script report. The parameterMap, as the name suggests is a map containing an entry for each of the parameters. Thus, to access our previously created parameter, we could use the following.
def name = parameterMap['name']
return """<html>
<head>
<title>A hello world</title>
</head>
<body>
<h1>Hello ${name}</h1>
</body>
</html>"""
Datasources
If you look at the properties of a script report, you see that besides the script you can also provide a datasource. While scripts could leverage ReportServer's services to access datasources directly, providing a datasource as part of the configuration allows you to access the data more easily as ReportServer takes care of opening and closing a connection. Additionally, it would allow to reuse the same script with multiple datasources.
If you provide a database datasource as configuration (note that you do not need to provide a query), ReportServer will place a special connection variable into the scope of the script, that you can then use to load data from the datasource. Assuming we have configured our script with the demo datasource that is shipped with ReportServer. In this case, we could use the connection to read data using Groovy's SQL object as follows:
new SQL(connection).eachRow("SOME SQL SELECTION") { row ->
/* do something with the data */
}
Following is a complete example (note that we need to import the SQL object). Additionally, we use Groovy's MarkupBuilder to get a somewhat more structured script.
import groovy.sql.Sql;
import groovy.xml.MarkupBuilder;
/* create writer for catpuring html output */
def writer = new StringWriter()
/* create report */
new MarkupBuilder(writer).html {
head {
title 'A script report with a data source'
}
body {
h1 'Customer List'
ul {
/* get customer data from datasource */
new Sql(connection).eachRow('SELECT CUS_CUSTOMERNAME FROM T_AGG_CUSTOMER ORDER BY 1') { row ->
li row.CUS_CUSTOMERNAME
}
}
}
}
return writer.toString();
Output Formats
So far we have only generated HTML reports. As mentioned before, script reports are of course not limited to producing HTML, although you would need to produce HTML if you want to show a preview within ReportServer. In order to add multiple output formats, you need to configure the Export formats property on the script report. Here you can provide a comma separated list of formats. For example, if you wanted to produce HTML and PDF output you could set the property to
HTML,PDF
If we adapt our previous script report with the above configuration and execute it, you will notice that now ReportServer offers you to export the report to HTML and PDF. However, whatever output format you choose, ReportServer will send you a text file with the HTML document. The reason is that we have not yet taken care of the different formats within our script.
In order to tell ReportServer what type of file it should provide as download, we need to return a special object of type net.datenwerke.rs.core.service.reportmanager.engine.CompiledReport (if you have downloaded ReportServer's API documentation search for CompiledReport). Further information on working with these CompiledReport objects can be found in the Scripting Guide. For the most common types (HTML and PDF) there is a shortcut which you can access via the special variable renderer.
Let us adapt our previous example, to produce PDF and HTML depending on the selected output format.
import groovy.sql.Sql;
import groovy.xml.MarkupBuilder;
/* create writer for catpuring html output */
def writer = new StringWriter()
/* create report */
new MarkupBuilder(writer).html {
head {
title 'A script report with a data source'
}
body {
h1 'Customer List'
ul {
/* get customer data from datasource */
new Sql(connection).eachRow('SELECT CUS_CUSTOMERNAME FROM T_AGG_CUSTOMER ORDER BY 1') { row ->
li row.CUS_CUSTOMERNAME
}
}
}
}
if(outputFormat == 'pdf')
return renderer.get("pdf").render(writer.toString())
return renderer.get("html").render(writer.toString())
The only parts that changed are the last three lines. The special variable outputFormat stores the output format specified by the user. In case of the preview in ReportServer the variable will contain the String preview. Otherwise it contains the format (in lowercase). In line 23 we check whether the output format specifies that we should produce a PDF report. To generate PDF, ReportServer offers a shortcut that allows to transform HTML directly into PDF. To get the PDF renderer and render html you use
renderer.get("pdf").render(StringContainingHTML)
The renderer transforms the HTML into a valid PDF document and creates the propert CompiledPDFReport object to let ReportServer know that the output is PDF.
Besides the PDF renderer there are also renderers for Microsoft Word (use renderer.get("docx")) and, of course, for HTML which we used in the very last line.
Script Datasources
While script reports provide you with a tool to implement almost any type of report requirement, script datasources allow you to incorporate almost any data source into the system to then use any of ReportServer's reporting engines (such as the Dynamic List) to report upon this data. In the following we are going to implement a datasource that accesses an XML dataset of the World Health Organization (WHO) to then use a Dynamic List to report upon it. The WHO offers various open datasets via http://www.who.int/gho/en/ and in the following we will show how to import a dataset on life expectancy per country which is available at
The dataset has the following format:
<Data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://apps.who.int/gho/athena/data/GHO/WHOSIS_000001,WHOSIS_000002.xsd?format=xml&profile=simple-schema">
<Fact>
<COUNTRY>Angola</COUNTRY>
<GHO>Life expectancy at birth (years)</GHO>
<PUBLISHSTATE>Published</PUBLISHSTATE>
<REGION>Africa</REGION>
<SEX>Female</SEX>
<YEAR>2015</YEAR>
<Display>54.0</Display>
<Numeric>54.02480</Numeric>
</Fact>
<Fact>
<COUNTRY>United Arab Emirates</COUNTRY>
<GHO>Life expectancy at birth (years)</GHO>
<PUBLISHSTATE>Published</PUBLISHSTATE>
<REGION>Eastern Mediterranean</REGION>
<SEX>Both sexes</SEX>
<YEAR>2015</YEAR>
<Display>77.1</Display>
<Numeric>77.06616</Numeric>
</Fact>
...
</Data>
In order to use this data set in ReportServer we need a script datasource that reads the above XML and transforms it into a format that ReportServer understands which in most cases will be an RSTableModel object. But, before we explain the details, let us show you the entire script.
import net.datenwerke.rs.base.service.reportengines.table.output.object.*;
/* the url from which to load the data */
def url = "http://apps.who.int/gho/athena/data/GHO/WHOSIS_000001,WHOSIS_000002.xml?profile=simple&filter=COUNTRY:*;YEAR:2015"
/* read in data */
def dataText = new URL(url).getText();
/* prepare table model */
def tableDefinition = new TableDefinition(
['Country', 'Region', 'Sex', 'Year', 'Life Expectancy'],
[String.class, String.class, String.class, String.class, BigDecimal.class]
);
def model = new RSTableModel(tableDefinition);
/* process data */
def data = new XmlSlurper().parseText(dataText);
data.Fact.each { it ->
model.addDataRow(it.COUNTRY.toString(),it.REGION.toString(),it.SEX.toString(),it.YEAR.toString(),it.Numeric.toBigDecimal())
}
return model;
22 lines, not bad. So let us go through this line by line. First line, we need to import the necessary objects to create the RSTableModel, which provides an internal object representation of a table for ReportServer. The whole point of the script, so to speak, is to then transform the XML structure into an RSTableModel.
In line 4, we simply store the URL with the data feed to then in line 7 read in the entire document to store it in variable dataText. Lines 10 to 14 prepare the RSTableModel object that we will use. To create an RSTableModel we need to tell ReportServer how the table will look like and we do this via a TableDefinition. The TableDefinition takes two lists, one to define the names of the table's columns and the second to define the data type of each column. In the example, we are going to transform the XML into the following table:
Country | Region | Sex | Year | Life Expectancy |
Angola | Africa | Female | 2015 | 54.02480 |
... | ... | ... | ... | ... |
Here, the first four columns are of type String, the last column is a decimal number and hence of type BigDecimal.
Once we have a TableDefinition object, we can create an RSTableModel as done in line 14. What is left is to add the actual data to the model which we do in lines 17 to 20.
To process XML, Groovy offers a nice helper called the XMLSlurper. The first step is to read in our XML text into the XMLSlurper which happens in line 17. We can then use the resulting object to access various parts of the XML. For example,
data.Fact[0].COUNTRY
would access the COUNTRY field within the first Fact tag. Similarly, data.Fact contains a list of all the Fact tags and we use the each method to process each in turn (line 18). Line 19 now contains the transformation of a single Fact tag to a data row. We add a new data row to our RSTableModel via the addDataRow method and provide the data fields as input. Here we need to ensure that the fields are of the appropriate type and we use the toType helper methods provided by the XMLSlurper.
And, that is it. You can now use this script to power a script datasource which can then be used, for example, together with a Dynamic List. To create a script datasource go to the Datasources section in the Administration module and create a new Script Datasource which you configure with the script.
Behind the Scenes
When working with script datasources it is important to understand what happens behind the scenes. If your script returns an RSTableModel, ReportServer will load the resulting data into its Internal Database, that is, into a temporary database table. This allows all the reporting engines to access the data as they would access any other database tables and thus all the functionality of the Dynamic List or Jasper can be used when reporting on a scripted data set.
Loading data from an external source, processing it, and then loading it into a database takes some time which is why you might not want to perform these steps for every single request that is made to the data set. For this, ReportServer allows you to define whether or not a data set defined via a script datasource is cached and if you decide to enable a cache for how long the data is cached before it is recomputed. By default the Database cache option of a script datasource is set to -1 which means that the data is cached indefinitely. To turn off the cache, set this entry to 0. Any positive integer specifies a cache in minutes, for example, a setting of 10 would mean that the data is recomputed after 10 minutes (but only if it is accessed).
In case you want to clean the cache manually, you can simply hit the Apply button on the script datasource. That is, any change on the script datasource (and even if no property actually changed) will cause the cache to be flushed.
Maintenance Scripts
A very useful property of scripts is that they can access the entire ReportServer object model and thus can be used to automate and maintain the platform. For example, you can automatically generate users, or reports, synchronize user groups with TeamSpaces or automatically update user dashboards. Or consider the case that some fields changed in your data warehouse and you now want to understand the impact this is having on existing reports. You could, for example, search for all Dynamic Lists that contain a particular attribute, or even perform a test execution of all reports in the system.
In order to make use of these techniques you will need a basic understanding of the ReportServer object model and the helper services provided by ReportServer which goes far beyond this tutorial. A good place to start is the scripting guide as well as the various blog posts and tutorials. Also, you might want to have a look at the API documentation and ReportServer sources. For questions and pointers also try our community forum or if your company has a support contract, our development team will be happy to help.
In the introduction we have already seen an example of how to use ReportServer service objects to loop over all the reports in the system. For this we used the ReportService object provided by ReportServer. The most important services to manipulate objects are:
Service | Description | Full name |
---|---|---|
ReportService | Manage reports | net.datenwerke.rs.core.service.reportmanager.ReportService |
UserManagerService | Manage users, groups and OUs | net.datenwerke.security.service.usermanager.UserManagerService |
DatasourceService | Manage datasources | net.datenwerke.rs.core.service.datasourcemanager.DatasourceService |
FileService | Manage the internal file system | net.datenwerke.rs.fileserver.service.fileserver.FileServerService |
TeamSpaceService | Manage TeamSpaces | net.datenwerke.rs.teamspace.service.teamspace.TeamSpaceService |
TsDiskService | Manage the file system of a TeamSpace | net.datenwerke.rs.tsreportarea.service.tsreportarea.TsDiskService |
Each service manages a set of related entities, for example, the ReportService is used to fetch or create report entities. But there is not only one entity to represent a report, but one entity per report type. For example, Dynamic Lists are internally represented by net.datenwerke.rs.base.service.reportengines.table.entities.TableReport while a BIRT report would be represented by objects of type net.datenwerke.rs.birt.service.reportengine.entities.BirtReport. All together, ReportServer consists of more than 180 different entity objects.
To get a list of all provided services and entities in ReportServer, download the API documentation.
When starting to manipulate ReportServer's object model with scripts you can seriously damage your installation. You should thus develop scripts always on a seperate development server and only deploy such scripts that have been thoroughly tested.
To access ReportServer internals we have said that you will need to access ReportServer's services. For this, ReportServer provides yet another special variable to every script which is called GLOBALS and which offers various helper methods. In particular, it offers the method getInstance which allows you to obtain a ReportServer service. To load a ReportServer service you will need to import the service class and then use GLOBALS.getInstance to access the service. The following example loads the UserManagerService.
import net.datenwerke.security.service.usermanager.UserManagerService;
def userService = GLOBALS.getInstance(UserManagerService.class);
We could now, for example, use the service to list all users in the system.
import net.datenwerke.security.service.usermanager.UserManagerService;
def userService = GLOBALS.getInstance(UserManagerService.class);
userService.getAllUsers().each{ it -> tout.println it.getFirstname() + " " + it.getLastname() }
return null;
So what about creating a new user? The user entity is net.datenwerke.security.service.usermanager.entities.User and we can create a new user object by
import net.datenwerke.security.service.usermanager.entities.User;
def user = new User();
user.setUsername("testuser");
user.setFirstname("John");
user.setLastname("Testuser");
This script alone, however, does not add a user to the system. For this, we would need to make ReportServer aware of the user and attach the user into an organizational unit (i.e., add it as a child of a user folder in the user tree). For the first part we need to call the persist method of the UserManagerService and pass the newly created user. To add the user to a folder, we need to load a folder that is already in the system. To obtain the root folder of the user tree we can use the method getRoots() of the UserManagementService. Note that the method is called getRoots and not getRoot and indeed it returns a list rather than a single object. This is due to the fact that the abstract implementation of a tree in ReportServer can have multiple roots. In case of the user tree, there will, however, always be only one root object so we can access the folder via getRoots().get(0). Once we have the folder, we can add the user by calling the addChild object of the folder.
Note that all tree-based entities, i.e., any entity that is represented in a tree in ReportServer such as users or reports have a method called setParent. It may be tempting to add a user to a folder by calling user.setParent(folder). Don't!
Thus, we have all that we need to put the example together. Following is the resulting code.
import net.datenwerke.security.service.usermanager.UserManagerService;
import net.datenwerke.security.service.usermanager.entities.User;
/* load service */
def userService = GLOBALS.getInstance(UserManagerService.class);
/* create user */
def user = new User();
user.setUsername("testuser");
user.setFirstname("John");
user.setLastname("Testuser");
/* load root folder and add user */
def root = userService.getRoots().get(0);
root.addChild(user);
/* persist user */
userService.persist(user);
return "added new user";
If you run the above script on the terminal you will see that it returns with the message added new user, but if you go to the user management and look the user isn't there. The reason for this is that, by default, scripts are executed in non-commiting mode meaning that changes to ReportServer's object model will not be made persistent. This provides an extra level of security to not accidentally cripple your system by running the wrong script. In order to run a script in commiting mode you need to call exec with the -c flag. Thus, if your script is named addUser.groovy then you need to call:
exec -c addUser.groovy
Now, if you refresh the user tree you should see John Testuser beneath the root OU. We'll leave you to explore the possibilities at this point. And once more let us warn you: ReportServer scripts are a fantastic maintenance tool. However, remember that with great power comes great responsibility, so please make sure to develop and test your scripts on a dedicated development machine.
Script Enhancements
The final use case that we want to discuss in this tutorial is the enhancement of ReportServer using scripts. ReportServer is build upon a flexible Hooking mechanism which allows to register hooks that can add or influence various functionality. For example, you can register a hook that is called before a report is executed to either change or deny execution. A list of all available hooks is given in the API documentation. Besides registering hooks, you can also register for certain events, such as the event that an entity changed or was removed. You could then either perform an action or deny the change by throwing an exception.
As an example we are going to implement the net.datenwerke.rs.core.service.reportmanager.hooks.ReportExecutionNotificationHook which is called before a report is executed. Hooks that were designed to be implemented by scripts usually come with a default implementation called HOOKNAMEAdapter. In the case of ReportExecutionNotificationHook we should thus find the ReportExecutionNotificationHookAdapter. You should always implement the adapter rather than the hook directly. To implement a hook in Groovy we create a map that for each of the methods we want to implement has a closure. In the example, we want to implement the doVetoReportExecution method and deny execution after seven pm. We understand that reporting is fun, but at some point we should call it a day. To implement the hook we use the following structure
import HOOK_NAME;
import HOOK_NAME_ADAPTER;
def callback = [
method1 : { params ->
},
method2 : { params ->
}
] as HOOK_NAME_ADAPTER;
If you are familiar with Java, then what we are doing is that we provide an anonymous implementation of the hook. Using a map and the as class keyword is a Groovy shortcut for implementing interfaces.
Note that we import both the actual hook and the adapter. We will see in a moment why. So here is the implemataton of ReportExecutionNotificationHook.
import net.datenwerke.rs.core.service.reportmanager.hooks.ReportExecutionNotificationHook;
import net.datenwerke.rs.core.service.reportmanager.hooks.adapter.ReportExecutionNotificationHookAdapter;
def callback = [
doVetoReportExecution : { report, parameterSet, user, outputFormat, configs ->
if(Calendar.getInstance().get(Calendar.HOUR_OF_DAY) > 19)
throw new RuntimeException("Have some free time.");
}
] as ReportExecutionNotificationHookAdapter;
All that is missing now, is to make ReportServer aware of the hook. That is, to hook-in our implementation. For this, we once more use the GLOBALS object which provides a service called callbackRegisry which manages the hooking in for us. Here we finally need the actual hook class (and not the adapter) to tell the service where to hook this in. Following is the completed example.
import net.datenwerke.rs.core.service.reportmanager.hooks.ReportExecutionNotificationHook;
import net.datenwerke.rs.core.service.reportmanager.hooks.adapter.ReportExecutionNotificationHookAdapter;
def callback = [
doVetoReportExecution : { report, parameterSet, user, outputFormat, configs ->
if(Calendar.getInstance().get(Calendar.HOUR_OF_DAY) > 19)
throw new RuntimeException("Have some free time.");
}
] as ReportExecutionNotificationHookAdapter;
GLOBALS.services.callbackRegistry.attachHook("NO_WORK_AFTER_SEVEN", ReportExecutionNotificationHook.class, callback)
Note that we've provided a name for our implementation: NO_WORK_AFTER_SEVEN. This is used, such that if we execute the script twice (for example, if we want to change something in the implemenation), the hook is replaced rather than that a second one is added. It also allows us to deregister the hook at a later time via the detachHook method of the callbackRegistry.
Once you've executed the above script, report executions after seven are not longer possible and this is also where we end this tutorial. We hope that this provided some first insights into scripting. Scripting is one of the most complex but also one of the most powerful tools in ReportServer. Furthermore, scripting can be fun, not the least because it allows you to get the job done, whatever it is.
If you have any feedback to this tutorial, we are always happy to hear about it. Until then.
Happy Scripting.
Setting up Development with ReportServer 3.0
In this tutorial we describe how to set up a development environment based on Eclipse to look into ReportServer 3.0 Community Edition as well as to have an ideal environment for developing ReportServer scripts. With ReportServer 3.0 we have significantly simplified the process of setting up a development environment. Nevertheless there are quite a few steps that we need to follow. In short these are:
- Download a recent version of Eclipse
- Download GWT 2.7
- Configure Eclipse to use GWT
- Download ReportServer
- Download additional libraries
- Setup the project in Eclipse
- Start ReportServer from within Eclipse
In the following we'll go through these step by step.
Download Eclipse
First step is to download a recent version of Eclipse. Eclipse comes in many prepackaged variants. The best to start with ReportServer development is the Java IDE. It is available from https://eclipse.org/ide/. At the time of writing the latest Eclipse version is Mars.2 which you can download for your preferred platform from https://eclipse.org/downloads/packages/eclipse-ide-java-developers/mars2.
Once you've downloaded Eclipse, start it up and create a new workspace. Before we, however, create any new projects we will need to set up GWT (in version 2.7).
You might want to increase the available main memory for Eclipse. This is beyond the scope of this tutorial, but a bit of googling should get you there.
Download GWT
ReportServer is build on top of GWT (http://www.gwtproject.org/). To set up GWT we will need to download the GWT SDK 2.7 as well as set up the GWT developer tools in Eclipse. First step is to download the GWT SDK which is available from the GWT download page. Make sure to get version 2.7 (the latest stable version at the time of writing). If a newer version appears you will find the older versions in the GWT download archive.
Once downloaded, unpack the archive and put the folder gwt-2.7.0 somewhere safe. We'll need it shortly when setting up GWT in Eclipse.
Configure Eclipse to use GWT
Next, we will set up GWT in Eclipse. There are several tutorials (including an official tutorial) out there discussing the basics of using GWT with Eclipse. For this tutorial, we assume that you have worked with Eclipse before and are thus not going into too much detail.
To install the GWT Plugin (i.e., the Google Plugin) for Eclipse, get the latest plugin link from https://developers.google.com/eclipse/docs/download. Within Eclipse choose Install new Software from the Help menu bar, add the latest plugin url as a repository and choose to install the Google Plugin for Eclipse. Note that you do not need to install the Developer Tools or any of the SDKs.
Follow the instructions and when the plugin was successfully installed, restart Eclipse.
Next we are going to link the previously downloaded GWT 2.7 SDK. For this we open the Eclipse preferences and go to Google > Web Toolkit. Here we click Add and choose the previously downloaded gwt-2.7.0 folder. Close the preferences. This concludes setting up GWT in Eclipse.
Download ReportServer
Next, we need to download the ReportServer 3.0.1 sources as well as binaries. (The latter will be used to get most of the necessary libraries.) Sources and binaries are available from sourceforge.
Once downloaded, unpack both archives. The sources should contain a single folder ReportServer which in turn contains three folders: metamodel, src and war.
Download Additional Libraries
Besides the ReportServer sources and binaries we need some additional libraries to successfully compile ReportServer. These are
- Juel (Version 2.2.7)
- SLF4J (to get decent logs)
- rs-entityservices.jar
- rs-saiku.jar
- rs-mockup.jar
You can get Juel from http://juel.sourceforge.net/. We'll be using the juel-api-2.2.7.jar that comes with Juel.
SLF4J is a logging framework and we will be using the slf4j-simple logger to set up a basic logging. You can download slf4j from http://www.slf4j.org/download.html. Look for version 1.7.12 (although the latest version will probably work as well).
As for the three remaining jars, you can download them directly via the above links.
Setup the project in Eclipse
We now have everything we need to setup ReportServer in Eclipse. For this we go back to our empty Eclipse workspace open the Java perspective (if it is not already open) and choose to create a new Web Application Project (right click in Project Exporer, new > Other... Then choose Google > Web Application Project).
In the upcoming wizard provide the following configuration.
Project Name | ReportServer |
Package | net.datenwerke |
Use Google App Engine | disable |
Generate project sample code | disable |
Hit Finish to create the project. You should now have a more or less empty project that has a src folder with the single empty package net.datenwerke as well as a war folder containing WEB-INF/lib/gwt-servlet.jar and WEB-INF/web.xml.
Copy Libraries: In the following we are going to copy the necessary libraries to WEB-INF/lib. First copy the perviously downloaded additional libraries:
- juel-api-2.2.7.jar
- slf4j-simple-1.7.12.jar
- rs-entityservices.jar
- rs-saiku.jar
- rs-mockup.jar
Next, we are going to copy most of the libraries from the ReportServer binaries over. In the unpacked ReportServer binaries that you downloaded earlier you will also find a WEB-INF/lib folder. Copy all the jars from that folder to the new project WEB-INF/lib folder except for the following jars:
- gwt-servlet-2.7.0.jar
- reportserver.jar
- rs-schemaupdate.jar
- slf4j-ext-1.7.12.jar
Copy war: Next we are going to copy the relevant files from the ReportServer sources' war directory. In the ReportServer sources' war directory you will also find a WEB-INF directory containing two xml files web.xml and sun-jaxws.xml. Copy both files to the project's WEB-INF directory thereby overwriting the web.xml file that is already present. Directly beneath the war directory in the unpacked sources you should find additional files and folders (favicon.ico, licenses, ReportServer.html and resources). Copy these to the package's war directory.
Copy metamodel: Within the unpacked sources you should find a folder metamodel. Copy this folder directly into the project folder.
Copy sources: Finally we can copy the src folder from the unpacked ReportServer sources into the project. You'll be notified that the folder already exists: choose to overwrite it.
If all went well you should now have a view similar to the following on the right.
With ReportServer it is usually easier to work with a hierarchical package outline rather than the default flat outline. To switch the outline click on the small down-arrow on the topbar of the Package Explorer and choose Package Presentation > Hierarchical. This is also the view in the screenshot on the right.
As you can see there are still quite a few errors because we have not yet properly set up the classpath. To do so right-click the project and choose Properties. In the properties window go to Java Build Path. There in the Source tab choose Add Folder... and choose the metamodel folder. Next go to the Libraries tab and click Add Jars.... Go to WEB-INF/lib and choose all jars within that directory.
Once you've closed the properties panel, Eclipse will start a recompilation and this time it should finish without any errors. Congratulations, you have just compiled ReportServer for the first time. Next we will discuss how to start ReportServer from within Eclipse.
Starting ReportServer
In order to start ReportServer we first need to prepare a database and configure the connection settings. Installing a database is out of the scope of this tutorial so we have to assume that you already have one of the supported database systems running. In the unpacked binaries archive you will find a folder called ddl which contains SQL scripts to set up the necessary structures. Choose the script for your database and execute it.
Further information on the database setup is also given in the ReportServer Configuration Guide.
Now that we have a database ready, we need to tell ReportServer how to access it. For this we'll copy a sample configuration file from the binaries archive over to the project folder. The sample configuration is called persistence.properties.example and can be found in WEB-INF/classes in the binaries archive. Copy the file into the src folder within your Eclipse project. That is, it should be next to the other config files such as reportserver.properties.
Once you've copied the file, rename it to persistence.properties and open it for editing. It contains examples for connections to various databases. Configure the database settings and save the file.
Before we can start ReportServer there is one final folder that we should copy from the binaries archive to our Eclipse project. In the archive you should find a folder called pkg which contains the base configuration installed during the first start of ReportServer as well as the demo data. Copy the folder to the war folder within your Eclipse project.
Now all that is left is to start ReportServer. For this open the package net.datenwerke where you should find a file called ReportServer.gwt.xml. Right click the file and choose Run As > Web Application (GWT Super Dev Mode). As soon as Eclipse starts ReportServer we can stop it again, since we need to make some additional adjustment. Now that we have started ReportServer once, Eclipse has create a Run Configuration. We can open the configuration by clicking on the run button from the toolbar (round green button with play arrow) and choose Run Configurations... (see screenshot).
On the left you should find a run configuration called ReportServer. Choose that Arguments tab on the right. This allows us to specify arguments for the JVM. What we are interested in is the main memory setting which by default is 512 MB which is too little for ReportServer. Increase this to at least 2 GB, for example, change the VM arguments to be:
-Xmx3g -XstartOnFirstThread
Hit Apply and the Run to finally start ReportServer. If all went well you should see something akin to the following output on the console:
Runing CodeServer with parameters: [-noprecompile, -port, 9876, -sourceLevel, 1.7, -bindAddress, 127.0.0.1, -launcherDir, /Users/arno/Downloads/mySpace/ReportServer/war, -logLevel, INFO, net.datenwerke.ReportServer] Super Dev Mode starting up workDir: /var/folders/w7/6gcxpbxn3n18ss_vm48847w00000gn/T/gwt-codeserver-2852183346399272654.tmp Loading Java files in net.datenwerke.ReportServer. Ignored 1 unit with compilation errors in first pass. Compile with -strict or with -logLevel set to TRACE or DEBUG to see all errors. Module setup completed in 5030 ms The code server is ready at http://127.0.0.1:9876/ Code server started in 5154 ms [main] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: net.datenwerke.rs.utils.hibernate.MySQL5Dialect [main] INFO net.datenwerke.rs.EnvironmentValidator - _____ _ _____ | __ \ | | / ____| | |__) |___ _ __ ___ _ __| |_| (___ ___ _ ____ _____ _ __ | _ // _ \ '_ \ / _ \| '__| __|\___ \ / _ \ '__\ \ / / _ \ '__| | | \ \ __/ |_) | (_) | | | |_ ____) | __/ | \ V / __/ | |_| \_\___| .__/ \___/|_| \__|_____/ \___|_| \_/ \___|_| | | |_| Version: Unknown Code Version: 2016-03-24-23-46-15 VM Args: -Xmx3g -Dfile.encoding=UTF-8 rs.configdir: Not Configured ### DB Config ### hibernate.dialect: net.datenwerke.rs.utils.hibernate.MySQL5Dialect (OK) hibernate.connection.driver_class: com.mysql.jdbc.Driver (OK) hibernate.connection.url: jdbc:mysql://localhost:3306/rs (OK) hibernate.connection.username: root hibernate.connection.password: **** hibernate.default_schema: Connection Test: OK Schema Version: RS3.0-7
In the current configuration, logging goes a bit overboard, so expect to see quite a few warnings of hibernate that can, however, be safely ignored.
Once ReportServer has started, you should see a URL in the Development Mode tab, such as:
http://127.0.0.1:8888/ReportServer.html
Pointing your browser to this URL will result in GWT starting to perform a compilation of the client side code (the browser should look like the screenshot on the right). In the Eclipse Console you should see something along the following lines
GET /recompile/reportserver Job net.datenwerke.ReportServer_1_0 starting job: net.datenwerke.ReportServer_1_0 binding: gxt.user.agent=gecko1_9 binding: user.agent=gecko1_8 binding: user.agent.os=mac Compiling module net.datenwerke.ReportServer Ignored 1 unit with compilation errors in first pass. Compile with -strict or with -logLevel set to TRACE or DEBUG to see all errors.
Compilation may take several long minutes, so this is the perfect time to get a fresh cup of coffee and congratulate yourself on being almost done.
Once compilation has finished you should see the following entries in the Console:
Unification traversed 136884 fields and methods and 12755 types. 12701 are considered part of the current module and 12701 had all of their fields and methods traversed. Compiling 1 permutation Compiling permutation 0... Linking per-type JS with 12685 new types. prelink JS size = 39424903 prelink sourcemap = 39424903 bytes and 674224 lines postlink JS size = 39267502 postlink sourcemap = 39267502 bytes and 670633 lines Source Maps Enabled Compile of permutations succeeded Compilation succeeded -- 218,495s Linking into /var/folders/w7/6gcxpbxn3n18ss_vm48847w00000gn/T/gwt-codeserver-2852183346399272654.tmp/net.datenwerke.ReportServer/compile-2/war/reportserver; Writing extras to /var/folders/w7/6gcxpbxn3n18ss_vm48847w00000gn/T/gwt-codeserver-2852183346399272654.tmp/net.datenwerke.ReportServer/compile-2/extras/reportserver Link succeeded Linking succeeded -- 2,131s 221,143s total -- Compile completed
More important, your browser should have reloaded and now show you the ReportServer login screen. As per any new installation ReportServer will have created a user called root with password root. Congratulations, you are now setup.
Happy Coding