Switching A DataSource Based On A User



Purpose

This example shows how to switch a JDBC data source per user using a custom data source in JasperReports Server. The example works with JasperReports Server version 3.5.1 It is recommended to have Java programming skills and having read the Chapter 5.10 Custom Data Source of the JasperReports Server Pro Admin Guide. The author assumes you are familiar especially with chapter 5.10.5 creating a custom data source. Please note: this is the initial version of this document, comments will be added soon.
 

Use Case

In JasperReports Server there are two data sources configured:

  • /datasources/ds_1
  • /datasources/ds_2

ds_1 and ds_2 are JDBC connections to the same data source using different user credentials. Depending on the logged in user either one of them should be used, a user named jasperadmin is should access ds_2; all other users are allowed to access ds_1.
 

Implementation

We will need to implement two java classes and a spring bean definition.
 

JasperReports Server configuration

To register the custom data source in JasperReports Server you will need to add the spring bean configuration. Just add a new file named applicationContext-customds.xml and add this content. This file needs to be deployed to <JasperServer webapp home>\WEB-INF

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
    <bean id="SwitcherDS"
          class="com.jaspersoft.ps.examples.SwitchingDataSourceFactory" >
        <property name="repositoryService">
            <ref bean="repositoryService"/>
        </property>
        <property name="dataSourceServiceFactories">
            <ref bean="dataSourceServiceFactories"/>
        </property>
    </bean>
</beans>

The beanId SwitcherDS will be used within JasperReports Server configuration to identify the custom data source. The class contains the bean which manages the custom data source. This implementation needs to be provided as jar file within the JasperReports Server classpath. Just copy the jar containing your changes to the <JasperServer webapp dir>\WEB-INF\lib.

This screenshot shows how to setup the custom bean data source in JasperReports Server.

ReportDataSourceService Interface

A custom data source requires an implementation of the ReportDataSourceService interface, which is responsible for setting up and tearing down data source connections in JasperReports Server. It relies on:

  • void setReportParameterValues(Map parameterValues): called before running a report - creates resources needed by JasperReports to obtain a JRDataSource and adds them to the parameter map
  • void closeConnection(): clean up any resources allocated in setReportParameterValues()
public void setReportParameterValues(Map parameterValues) {
    MetadataUserDetails userDetails = (MetadataUserDetails)
                                      SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    String userName = userDetails.getUsername();
    // obtain a connection based on the username.
    String dataSourceURI = "/datasources/test2";
    if (userName.equalsIgnoreCase("jasperadmin")) {
        dataSourceURI = "/datasources/ds_1";
    }
    connection = getRepositoryDatasource(dataSourceURI);
    try {
        parameterValues.put(JRParameter.REPORT_CONNECTION,
                            connection.getDataSource().getConnection());
    } catch (SQLException sqle){
        sqle.printStackTrace();
    }
}
public JdbcDataSourceService getRepositoryDatasource(String repositoryURI) {
    ExecutionContext context = JasperserverUtil.getExecutionContext(LocaleContextHolder.getLocale());
    ReportDataSource datasource = (ReportDataSource)
                                  repositoryService.getResource(context, repositoryURI);
    JdbcReportDataSourceServiceFactory factory = (JdbcReportDataSourceServiceFactory)
                                                 dataSourceService.getBean(datasource.getClass());
    JdbcDataSourceService DSservice = (JdbcDataSourceService)
                                      factory.createService(datasource);
    return DSservice;
}

Factory Bean Implementation

You will need to develop a bean that manages all the custom data sources. JasperReports Server provides an interface ReportDataSourceServiceFactory.

private RepositoryService repositoryService;
private BeanForInterfaceImplementationFactory dataSourceService;
 
//  ..  add getter and setter
 
public SwitchingDataSource createDataSourceService(){
    return new SwitchingDataSource(repositoryService, dataSourceService);
}
 
public ReportDataSourceService createService(ReportDataSource dataSource) {
    return new SwitchingDataSource(repositoryService, dataSourceService);
}

Further Readings

Using domains with custom data sources

  • JasperReports Server documentation
    • JasperReports Server Pro Admin Guide, chapter 5.10 Custom Data Sources
    • JasperReports Server User Guide (CE), chapter 6.1 Custom Data Sources
  • JasperReports Server samples for custom data sources
  • Jasperforge forum
Feedback
randomness