Hello again,
I've following this "tutorial" for creating a dynamic datasource switch.
I want to use one report, but each client have their own datasource.
http://community.jaspersoft.com/wiki/switching-datasource-based-user
I'm using JasperServer 5.5.0 with the installer and iReport 5.5.0 with the installer
So, after many searches, finally I got working the jar, but when I run the report on the jasperserver web admin.
I got the java.lang.NullPointerException
I'm going to attach the files and a screenshot of the error, and, at the bottom the source code.
Thanks for your help!
Best regards!
JasperServerCustomDataSource.java
package com.jasperserver.custom.datasource; import java.util.Map; import java.sql.SQLException; import net.sf.jasperreports.engine.JRParameter; import org.springframework.context.i18n.LocaleContextHolder; import com.jaspersoft.jasperserver.api.metadata.jasperreports.domain.ReportDataSource; import com.jaspersoft.jasperserver.api.metadata.jasperreports.service.ReportDataSourceService; import com.jaspersoft.jasperserver.api.common.domain.ExecutionContext; import com.jaspersoft.jasperserver.api.common.service.BeanForInterfaceImplementationFactory; import com.jaspersoft.jasperserver.api.engine.jasperreports.service.impl.JdbcDataSourceService; import com.jaspersoft.jasperserver.api.engine.jasperreports.service.impl.JdbcReportDataSourceServiceFactory; //import com.jaspersoft.jasperserver.api.engine.jasperreports.util.DataSourceServiceFactory; import com.jaspersoft.jasperserver.war.common.JasperServerUtil; import com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryService; public class JasperServerCustomDataSource implements ReportDataSourceService { private JdbcDataSourceService connection; private RepositoryService repositoryService; private BeanForInterfaceImplementationFactory dataSourceServiceFactory; //private DataSourceServiceFactory dataSourceServiceFactory; public JasperServerCustomDataSource(RepositoryService repositoryService, BeanForInterfaceImplementationFactory dsServiceFactory) { super(); this.repositoryService = repositoryService; this.dataSourceServiceFactory = dsServiceFactory; } @SuppressWarnings({ "unchecked", "rawtypes" }) public void setReportParameterValues(Map parametersValue) { String ds_name = (String) parametersValue.get("jasper_ds_name"); if( (ds_name == null) || (ds_name.isEmpty()) ) { ds_name = "CUSTOMER_DS1"; } String ds_uri = "/datasources/" + ds_name; connection = getRepositoryDatasource(ds_uri); try { parametersValue.put(JRParameter.REPORT_CONNECTION, connection .getDataSource().getConnection()); } catch (SQLException e) { e.printStackTrace(); } } public JdbcDataSourceService getRepositoryDatasource(String repositoryURI) { ExecutionContext context = JasperServerUtil .getExecutionContext(LocaleContextHolder.getLocale()); ReportDataSource datasource = (ReportDataSource) repositoryService .getResource(context, repositoryURI); JdbcReportDataSourceServiceFactory factory = (JdbcReportDataSourceServiceFactory) dataSourceServiceFactory .getBean(datasource.getClass()); JdbcDataSourceService DSservice = (JdbcDataSourceService) factory .createService(datasource); return DSservice; } @Override public void closeConnection() { if(connection != null) { connection.closeConnection(); } } }
JasperServerCustomDataSourceFactory.java
package com.jasperserver.custom.datasource; import com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryService; import com.jaspersoft.jasperserver.api.metadata.jasperreports.domain.ReportDataSource; import com.jaspersoft.jasperserver.api.metadata.jasperreports.service.ReportDataSourceService; import com.jaspersoft.jasperserver.api.metadata.jasperreports.service.ReportDataSourceServiceFactory; import com.jaspersoft.jasperserver.api.common.service.BeanForInterfaceImplementationFactory; //import com.jaspersoft.jasperserver.api.engine.jasperreports.util.DataSourceServiceFactory; import com.jasperserver.custom.datasource.JasperServerCustomDataSource; public class JasperServerCustomDataSourceFactory implements ReportDataSourceServiceFactory { private RepositoryService repositoryService; private BeanForInterfaceImplementationFactory dataSourceServiceFactory; public JasperServerCustomDataSourceFactory() { } /* Factory methods */ public JasperServerCustomDataSource createDataSourceService() { return new JasperServerCustomDataSource(repositoryService, dataSourceServiceFactory); } @Override public ReportDataSourceService createService(ReportDataSource dataSource) { return new JasperServerCustomDataSource(repositoryService, dataSourceServiceFactory); } /* getters */ public RepositoryService getRepositoryService() { return repositoryService; } public BeanForInterfaceImplementationFactory getDataSourceServiceFactory() { return dataSourceServiceFactory; } /* Setters */ public void setRepositoryService(RepositoryService repositoryService) { this.repositoryService = repositoryService; } public void setDataSourceServiceFactory(BeanForInterfaceImplementationFactory dataSourceServiceFactory) { this.dataSourceServiceFactory = dataSourceServiceFactory; } }
ApplicationContext-customds.xml
<?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="JasperServerCustomDataSourceFactory" class="com.jasperserver.custom.datasource.JasperServerCustomDataSourceFactory" > <property name="repositoryService"> <ref bean="repositoryService"/> </property> </bean> </beans>
5 Answers:
Hi Oscar,
I follow your sample and the JR Server Ultimate Guide info about creating a custom datasource to make the same (switch datasource based on the user), and finally I got both working, but I prefer to use the custom datasource.
I think your code is basically OK (I uncommented the dataSourceServiceFactory, and use
ExecutionContext context = JasperServerUtil.getExecutionContext();
instead of
ExecutionContext context = JasperServerUtil.getExecutionContext(LocaleContextHolder.getLocale());
MyCustomDataSourceService.Java
package com.example.jr.datasource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JRResultSetDataSource;
import org.springframework.security.context.SecurityContextHolder;
import com.jaspersoft.jasperserver.api.common.domain.ExecutionContext;
import com.jaspersoft.jasperserver.api.common.service.BeanForInterfaceImplementationFactory;
import com.jaspersoft.jasperserver.api.engine.jasperreports.service.impl.JdbcDataSourceService;
import com.jaspersoft.jasperserver.api.engine.jasperreports.service.impl.JdbcReportDataSourceServiceFactory;
import com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryService;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.domain.ReportDataSource;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.service.ReportDataSourceService;
import com.jaspersoft.jasperserver.api.metadata.user.domain.impl.client.MetadataUserDetails;
import com.jaspersoft.jasperserver.war.common.JasperServerUtil;
public class MyCustomDataSourceService implements ReportDataSourceService {
// private properties
private JdbcDataSourceService connection;
private Connection conn;
// Bean properties
private Map propertyMap;
private RepositoryService repository;
private BeanForInterfaceImplementationFactory dataSourceServiceFactory;
/**
* Default constructor
*/
public MyCustomDataSourceService() {
super();
}
public Map getPropertyMap() {
return propertyMap;
}
public void setPropertyMap(Map propertyMap) {
this.propertyMap = propertyMap;
}
public RepositoryService getRepository() {
return repository;
}
public void setRepository(RepositoryService repository) {
this.repository = repository;
}
public BeanForInterfaceImplementationFactory getDataSourceServiceFactory() {
return dataSourceServiceFactory;
}
public void setDataSourceServiceFactory(
BeanForInterfaceImplementationFactory dataSourceServiceFactory) {
this.dataSourceServiceFactory = dataSourceServiceFactory;
}
@Override
public void closeConnection() {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
}
}
if (connection != null) {
connection.closeConnection();
}
}
@Override
public void setReportParameterValues(Map parameterValues) {
MetadataUserDetails userDetails = (MetadataUserDetails) SecurityContextHolder
.getContext().getAuthentication().getPrincipal();
String userName = userDetails.getUsername();
String dataSourceURI = "/datasources/" + userName;
connection = getRepositoryDatasource(dataSourceURI);
try {
conn = connection.getDataSource().getConnection();
parameterValues.put(JRParameter.REPORT_CONNECTION, conn);
} catch (SQLException sqle) {
//
}
}
private JdbcDataSourceService getRepositoryDatasource(String repositoryURI) {
try {
ExecutionContext context = JasperServerUtil.getExecutionContext();
ReportDataSource datasource = (ReportDataSource) repository
.getResource(context, repositoryURI);
JdbcReportDataSourceServiceFactory factory = (JdbcReportDataSourceServiceFactory) dataSourceServiceFactory
.getBean(datasource.getClass());
JdbcDataSourceService DSservice = (JdbcDataSourceService) factory
.createService(datasource);
return DSservice;
} catch (Exception ex) {
return null;
}
}
}
applicationContext-MyCustomDS.xml
<?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-3.1.xsd">
<!-- define a custom data source -->
<bean id="CustomDataSource" class="com.jaspersoft.jasperserver.api.engine.jasperreports.util.CustomDataSourceDefinition">
<!-- this prop is always the same; it registers the custom ds -->
<property name="factory" ref="customDataSourceServiceFactory"/>
<!-- name used in message catalog and elsewhere -->
<property name="name" value="CustomDataSource"/>
<!-- class name of implementation -->
<property name="serviceClassName" value="com.example.jr.datasource.MyCustomDataSourceService"/>
<!-- definitions of properties you can edit -->
<property name="propertyDefinitions">
<list>
<map>
<entry key="name" value="repository"/>
<entry key="hidden" value="true"/>
<entry key="default" value-ref="repositoryService"/>
</map>
<map>
<entry key="name" value="dataSourceServiceFactory"/>
<entry key="hidden" value="true"/>
<entry key="default" value-ref="dataSourceServiceFactories"/>
</map>
</list>
</property>
</bean>
</beans>
Hi Oscar
When we followed exact steps mentioned above, we are facing issues with Bean Data source. We have given Bean name as JasperServerCustomDataSourceFactory as specified in context.xml and it says no bean named is defined. Could you please let us know, how the connection is successful in your above mentioned data source.
Thanks.
Hi,
It passed a while, so I dont' fully remember the details, but there where two approaches to achieve this, the bean data source and the custom data source.
When I tried this, I got errors about beans not defined, or not accesing some properties, so I have to include the changes commented in my previous post, and the files "pasted" (I was not able to include in a friendly way...) work fine with the custom data source approach.
Another problem I had was when testing the custom datasource, was trying to appy changes in the java code and the contest.xml to a custom data source already defined. It seems that some metadata was stored elsewhere (I don't know anything about Spring) and changes has no "total" effect", so it was safer to delete the custom data source, update the code and start again defining a new fresh custom data source. That produces me serious headaches until I discover what was happening...
Regards,
Álvaro.
Man!, Oh man, Sorry for late response and thanks for your help, I will check out and come back with feedback. Best regards!
Few questions:
1. In the above steps ; customdatasourceservicefactory is not defined, but is mentioned in application context as Bean ID. Is this a separate class ? where is the reference.
2. Do we have complete steps with reference to achieve this.
In this case what bean name and bean method do we need to provide while creating bean data source?
@naikrish94 found a solution?