/* vim: set ts=2 et sw=2 cindent fo=qroca: */
package com.globant.katari.report.application;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.Errors;
import com.globant.katari.core.application.Initializable;
import com.globant.katari.core.application.ValidatableCommand;
import com.globant.katari.report.domain.DropdownOptions;
import com.globant.katari.report.domain.JasperReportGenerator;
import com.globant.katari.report.domain.JasperReportRepository;
import com.globant.katari.report.domain.ParameterDefinition;
import com.globant.katari.report.domain.ReportDefinition;
import com.globant.katari.report.domain.ReportSecurityUtils;
import com.globant.katari.report.domain.ReportType;
/**
* Holds a list of parameter descriptor command. Used for binding the parameter
* list with the UI.
*
* @author sergio.sobek
*/
public class GenerateReportCommand implements ValidatableCommand<Void>,
Initializable {
/** The class logger.
*/
private static Logger log =
LoggerFactory.getLogger(GenerateReportCommand.class);
/** Map of the posted values, it is never null. */
private Map<String, String> values = new HashMap<String, String>();
/** The id of the report definition owning the parameter. */
private long reportId;
/** The repository. It is never null. */
private JasperReportRepository repository;
/** The jasper report generator service. It is never null. */
private JasperReportGenerator reportGenerator;
/**
* This command obtains a database connection from this dataSource and passes
* it to the report generator to query the database.
*
* It is never null.
*/
private DataSource dataSource;
/**
* The report generator writes the report output to this stream.
*
* This stream can be obtained, for example, form the servlet response. It
* must be set before generating the report to a non null value.
*/
private OutputStream outputStream;
/** The report output type, by default it is set to PDF, it is never null. */
private ReportType reportType = ReportType.PDF;
/** The list of parameters from a report, it is null before the init phase. */
private List<ParameterDefinition> parameters = null;
/** The report name, it is null before the init phase. */
private String reportName = null;
/**
* Constructor.
*
* @param theRepository the repository for retrieving the report definition.
* It cannot be null.
* @param theGenerator the repository generator. It cannot be null.
* @param theDataSource the data source a data source for connecting to a
* database. It cannot be null.
*/
public GenerateReportCommand(final JasperReportRepository theRepository,
final JasperReportGenerator theGenerator,
final DataSource theDataSource) {
Validate.notNull(theRepository, "The report repository cannot be null");
Validate.notNull(theGenerator, "The report repository cannot be null");
Validate.notNull(theDataSource, "The report repository cannot be null");
repository = theRepository;
reportGenerator = theGenerator;
dataSource = theDataSource;
}
/**
* Gets the reportId.
*
* @return the id of the report.
*/
public long getReportId() {
return reportId;
}
/**
* Gets the reportName.
*
* @return the name of the report.
*/
public String getReportName() {
return reportName;
}
/**
* Sets the reportId.
*
* @param theReportId
* the parameterReportId to set.
*/
public void setReportId(final long theReportId) {
reportId = theReportId;
}
/**
* Gets the list of parameters of the report with id = reportId.
*
* @return the parameters
*/
public List<ParameterDefinition> getParameters() {
return parameters;
}
/**
* Gets the map with the parameter values.
*
* @return a map with the parameter name as key and the parameter value as
* value.
*/
public Map<String, String> getValues() {
return values;
}
/**
* Sets the map with parameter values.
*
* @param theValues
* contains a map with each parameter name as key and each
* parameter value as value for that key. It cannot be null.
*/
public void setValues(final Map<String, String> theValues) {
Validate.notNull(theValues, "the parameter values map cannot be null.");
values = theValues;
}
/** Sets the output stream.
*
* @param theOutStream the stream to write the generated report. It cannot be
* null.
*/
public void setOutputStream(final OutputStream theOutStream) {
Validate.notNull(theOutStream, "The output stream cannot be null");
this.outputStream = theOutStream;
}
/**
* Getter of the reportType.
*
* @return the reportType.
*/
public ReportType getReportType() {
return reportType;
}
/**
* Setter of the reportType.
*
* @param theReportType
* wth the report output type.
*/
public void setReportType(final ReportType theReportType) {
Validate.notNull(theReportType, "theReportType cannnot be null.");
reportType = theReportType;
}
/**
* Executes the command.
*
* This Command delegates to the <code>JasperReportGenerator</code> the
* generation of the report. The report is written to the output stream
* specified that this command has as property.
*
* You must call setOutputStream and setValues before calling execute.
*
* @return null.
*/
public Void execute() {
Validate.notNull(values, "the parameter values map cannot be null.");
Validate.notNull(outputStream, "The output stream cannot be null");
Map<String, Object> map = new HashMap<String, Object>();
ReportDefinition report = repository.findReportDefinitionById(reportId);
Validate.isTrue(ReportSecurityUtils.isAccesible(report),
"User has not rigths to generate this report");
List<ParameterDefinition> definitions = report.getParameterDefinitions();
for (ParameterDefinition parameter : definitions) {
String parameterName = parameter.getName();
String parameterValue = values.get(parameterName);
if (parameter.isOptional() && StringUtils.isBlank(parameterValue)) {
break;
}
try {
map.put(parameter.getName(), parameter.convertValue(parameterValue));
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("Error converting parameter "
+ parameterName, e);
}
}
// Generates the report and writes it in the output stream.
Connection connection = null;
try {
connection = dataSource.getConnection();
reportGenerator.generate(report.getId(), map, reportType, outputStream,
connection);
} catch (SQLException e) {
log.error("Error executing report", e);
throw new RuntimeException("Error executing report", e);
} finally {
// DbUtils has rollbackAndCloseQuietly(connection), not used here because
// we would add another dependency.
if (connection != null) {
try {
connection.rollback();
} catch (SQLException e) {
log.warn("Unexpected error rolling back a connection - ignored.", e);
}
try {
connection.close();
} catch (SQLException e) {
log.warn("Unexpected error closing connection - ignored.", e);
}
}
}
return null;
}
/**
* Validation method.
*
* @param errors Holder for all the validation errors. It cannot be null.
*/
public void validate(final Errors errors) {
Validate.notNull(errors, "The errors holder cannot be null.");
ReportDefinition report = repository.findReportDefinitionById(reportId);
List<ParameterDefinition> definitions;
definitions = report.getParameterDefinitions();
for (ParameterDefinition parameter : definitions) {
String parameterName = parameter.getName();
String parameterValue = values.get(parameterName);
String[] commonArgs = {parameterName};
if (StringUtils.isBlank(parameterValue)) {
if (!parameter.isOptional()) {
errors.rejectValue("values", "error.required", commonArgs, "");
}
} else {
try {
parameter.convertValue(parameterValue);
} catch (NumberFormatException e) {
errors.rejectValue("values", "error.incorrectType", commonArgs, "");
} catch (ParseException e) {
errors.rejectValue("values", "error.incorrectFormat", commonArgs, "");
}
}
}
}
/**
* Initializes the command.
*
* During initialization, a report with the corresponding reportId is
* retrieved from the repository. This report must exist in the repository.
*/
public void init() {
Validate.isTrue(reportId > 0, "The report id should be greater than 0.");
ReportDefinition report = repository.findReportDefinitionById(reportId);
Validate.notNull(report, "The report id must exist in the repository.");
reportName = report.getName();
parameters = report.getParameterDefinitions();
}
/**
* Returns dropdown options for a given parameter.
* @param theParameter the report parameter definition, it cannot be null.
* @return a map of possible values.
* TODO that must be on the init method.
*/
public Map<String, String> getDropdownOptions(
final ParameterDefinition theParameter) {
Map<String, String> dropdownOptions = new LinkedHashMap<String, String>();
List<DropdownOptions> options = repository.getDropdownOptions(theParameter,
values);
// parse results and insert them into dropDownOptions
String firstValue = null;
if (options.size() != 0) {
firstValue = options.get(0).getValue();
}
for (DropdownOptions option : options) {
dropdownOptions.put(option.getValue(), option.getLabel());
}
// we must set a default value for linked combos
if (values.get(theParameter.getName()) == null) {
values.put(theParameter.getName(), firstValue);
}
return dropdownOptions;
}
}