package binky.reportrunner.service.impl; import java.io.IOException; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.text.ParseException; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.UUID; import javax.naming.NamingException; import javax.sql.DataSource; import net.sf.jasperreports.engine.JRException; import org.apache.commons.beanutils.RowSetDynaClass; import org.apache.commons.mail.EmailException; import org.apache.log4j.Logger; import binky.reportrunner.data.Configuration.ConfigurationType; import binky.reportrunner.data.RunnerJob; import binky.reportrunner.data.RunnerJobParameter; import binky.reportrunner.engine.beans.ViewerResults; import binky.reportrunner.engine.renderers.AbstractRenderer; import binky.reportrunner.engine.renderers.JasperRenderer; import binky.reportrunner.engine.renderers.StandardRenderer; import binky.reportrunner.engine.utils.EmailHandler; import binky.reportrunner.engine.utils.FileSystemHandler; import binky.reportrunner.engine.utils.SQLProcessor; import binky.reportrunner.engine.utils.impl.EmailHandlerImpl; import binky.reportrunner.engine.utils.impl.FileSystemHandlerImpl; import binky.reportrunner.engine.utils.impl.SQLProcessorImpl; import binky.reportrunner.exceptions.RenderException; import binky.reportrunner.service.ConfigurationService; import binky.reportrunner.service.DatasourceService; import binky.reportrunner.service.ReportGenerationService; import binky.reportrunner.service.ReportService; public class ReportGenerationServiceImpl implements ReportGenerationService { private ReportService reportService; private DatasourceService datasourceService; private FileSystemHandler fs; private ConfigurationService configurationService; private static final Logger logger = Logger .getLogger(ReportGenerationServiceImpl.class); public ReportGenerationServiceImpl() throws IOException { this.fs = new FileSystemHandlerImpl(); } @Override public void processReport(String jobName, String groupName) throws IOException, RenderException, EmailException, InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException, NamingException, NumberFormatException, ParseException { RunnerJob job = reportService.getJob(jobName, groupName); if (job.getIsBurst()) { processBurstedReport(jobName, groupName); } else { processSingleReport(jobName, groupName); } } @Override public List<String> processBurstedReport(String jobName, String groupName) throws IOException, RenderException, EmailException, InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException, NamingException, NumberFormatException, ParseException { RunnerJob job = reportService.getJob(jobName, groupName); DataSource ds = datasourceService .getJDBCDataSource(job.getDatasource()); List<String> fileUrls = new LinkedList<String>(); AbstractRenderer renderer; switch (job.getTemplateType()) { case JASPER: try { renderer = new JasperRenderer(job.getTemplateFile(), job.getFileFormat()); } catch (JRException e) { logger.error(e.getMessage(), e); throw new RenderException(e.getMessage(), e); } break; default: renderer = new StandardRenderer(job.getFileFormat()); } Connection conn = ds.getConnection(); Map<String, ResultSet> results = getResultsForJob(job, conn); try { String lastUrl = ""; for (String fileNameValue : results.keySet()) { ResultSet rs = results.get(fileNameValue); // if we are not outputting this anywhere (must be emailing) // then // dump this as a temp file String outUrl = fs.getFinalUrl((job.getOutputToFile()!=null&&job.getOutputToFile())?job.getOutputUrl():"", jobName, groupName, job.getFileFormat().toString().toLowerCase(), fileNameValue); logger.info("bursted file being output to: " + outUrl); renderReport(rs, fileNameValue, outUrl, renderer); if (!lastUrl.equals(outUrl)) { fileUrls.add(outUrl); lastUrl = outUrl; } } } finally { renderer.closeOutputStream(); conn.close(); } String fromAddress=configurationService.getConfigurationItem(ConfigurationType.EMAIL_FROM_ADDRESS).getValue(); String smtpServer = configurationService.getConfigurationItem(ConfigurationType.EMAIL_SERVER).getValue(); // send email if need be if (job.getSendViaEmail()&&(job.getTargetEmailAddress() != null) && (!job.getTargetEmailAddress().isEmpty())) { EmailHandler email = new EmailHandlerImpl(); email.sendEmail(job.getTargetEmailAddress(), fromAddress, smtpServer, fileUrls, jobName, groupName); } // clean up any temp files if ((job.getOutputUrl() == null) || (job.getOutputUrl().isEmpty())) { for (String url : fileUrls) { fs.deleteFile(url); } } return fileUrls; } @Override public String processSingleReport(String jobName, String groupName) throws IOException, RenderException, EmailException, InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException, NamingException, NumberFormatException, ParseException { RunnerJob job = reportService.getJob(jobName, groupName); DataSource ds = datasourceService .getJDBCDataSource(job.getDatasource()); Connection conn = ds.getConnection(); logger.debug("running single report for:" + groupName + "." + jobName); AbstractRenderer renderer; switch (job.getTemplateType()) { case JASPER: try { renderer = new JasperRenderer(job.getTemplateFile(), job.getFileFormat()); } catch (JRException e) { logger.error(e.getMessage(), e); throw new RenderException(e.getMessage(), e); } break; default: renderer = new StandardRenderer(job.getFileFormat()); } Map<String, ResultSet> results = getResultsForJob(job, conn); // if we are not outputting this anywhere (must be emailing) then // dump this as a temp file String outUrl = fs.getFinalUrl((job.getOutputToFile()!=null&&job.getOutputToFile())?job.getOutputUrl():"", jobName, groupName, job.getFileFormat().toString().toLowerCase()); try { renderReport(results.get("Results"), "Results", outUrl, renderer); } finally { renderer.closeOutputStream(); conn.close(); } String fromAddress=configurationService.getConfigurationItem(ConfigurationType.EMAIL_FROM_ADDRESS).getValue(); String smtpServer = configurationService.getConfigurationItem(ConfigurationType.EMAIL_SERVER).getValue(); logger.info("writing report to: " + outUrl); // send email if need be if ((job.getTargetEmailAddress() != null) && (!job.getTargetEmailAddress().isEmpty())) { EmailHandler email = new EmailHandlerImpl(); email.sendEmail(job.getTargetEmailAddress(), fromAddress, smtpServer, outUrl, jobName, groupName); } // clean up any temp files if ((job.getOutputUrl() == null) || (job.getOutputUrl().isEmpty())) { fs.deleteFile(outUrl); } return outUrl; } public Map<String, RowSetDynaClass> getResultSet(String groupName, String jobName) throws NumberFormatException, SQLException, ParseException { return this.getResultSet(groupName, jobName, null); } public Map<String, RowSetDynaClass> getResultSet(String groupName, String jobName, List<RunnerJobParameter> parameters) throws NumberFormatException, SQLException, ParseException { RunnerJob job = reportService.getJob(jobName, groupName); DataSource ds = datasourceService .getJDBCDataSource(job.getDatasource()); Connection conn = ds.getConnection(); try { // get all the results logger.debug("going to get a set of results for job: " + job.getPk().getJobName() + "/" + job.getPk().getGroup().getGroupName()); if (parameters != null) job.setParameters(parameters); Map<String, ResultSet> rs = getResultsForJob(job, conn); Map<String, RowSetDynaClass> ret = new HashMap<String, RowSetDynaClass>(); for (String key : rs.keySet()) { RowSetDynaClass rsdc = new RowSetDynaClass(rs.get(key), false); ret.put(key, rsdc); } return ret; } finally { conn.close(); } } public Map<String, ViewerResults> getResultsForJob(String jobName, String groupName, List<RunnerJobParameter> parameters) throws SQLException, NumberFormatException, ParseException, RenderException, IOException { Map<String, ViewerResults> results = new HashMap<String, ViewerResults>(); RunnerJob job = reportService.getJob(jobName, groupName); AbstractRenderer renderer; switch (job.getTemplateType()) { case JASPER: try { renderer = new JasperRenderer(job.getTemplateFile(), job.getFileFormat()); } catch (JRException e) { logger.error(e.getMessage(), e); throw new RenderException(e.getMessage(), e); } break; default: renderer = new StandardRenderer(job.getFileFormat()); } DataSource ds = datasourceService .getJDBCDataSource(job.getDatasource()); Connection conn = ds.getConnection(); try { // get all the results logger.debug("going to get a set of results for job: " + job.getPk().getJobName() + "/" + job.getPk().getGroup().getGroupName()); if (parameters != null) job.setParameters(parameters); Map<String, ResultSet> rs = getResultsForJob(job, conn); logger.debug("converting to dynasets"); //hack to fix excel tabbed issues (i hate excel tabs) StringBuilder combinedName=new StringBuilder(); String latestId=""; boolean first=true; for (String key : rs.keySet()) { ResultSet result = rs.get(key); if ((result != null)) { String id = UUID.randomUUID().toString(); renderReport(result, key, "tmp://" + id + ".tmp", renderer); latestId=id; if (job.getFileFormat()!=RunnerJob.FileFormat.TABBED_XLS) { results.put(key, new ViewerResults(id)); } else { if (!first)combinedName.append("_"); combinedName.append(key); first=false; } } } if (job.getFileFormat()==RunnerJob.FileFormat.TABBED_XLS) { results.put(combinedName.toString(), new ViewerResults(latestId)); } return results; } finally { renderer.closeOutputStream(); conn.close(); } } public Map<String, ViewerResults> getResultsForJob(String jobName, String groupName) throws SQLException, NumberFormatException, ParseException, RenderException, IOException { return getResultsForJob(jobName, groupName, null); } @Override public Map<RunnerJobParameter, List<Object>> getPossibleParameterValues( String jobName, String groupName) throws SQLException, NumberFormatException, ParseException { Map<RunnerJobParameter, List<Object>> paramValues = new HashMap<RunnerJobParameter, List<Object>>(); RunnerJob job = reportService.getJob(jobName, groupName); DataSource ds = datasourceService.getJDBCDataSource(job.getDatasource()); Connection conn = ds.getConnection(); try { SQLProcessor sqlProcessor = new SQLProcessorImpl(); if ((job.getIsBurst() == null) || (!job.getIsBurst())) { for (RunnerJobParameter p : job.getParameters()) { paramValues.put(p, null); } return paramValues; } logger.debug("getting burst result for " + jobName + "/" + groupName); ResultSet rs = sqlProcessor.getResults(conn, job.getBurstQuery()); for (RunnerJobParameter p : job.getParameters()) { List<Object> values = new LinkedList<Object>(); if ((p.getParameterBurstColumn() != null) && (!p.getParameterBurstColumn().isEmpty())) { // rs.beforeFirst(); logger.debug("getting values for parameter: " + p.getDescription()); while (rs.next()) { Object value = rs .getObject(p.getParameterBurstColumn()); if (!values.contains(value)) { logger.debug("found value: " + value); values.add(value); } } } paramValues.put(p, values); rs.close(); //trying to fix issue 100 } } finally { conn.close(); } return paramValues; } private Map<String, ResultSet> getResultsForJob(RunnerJob job, Connection conn) throws SQLException, NumberFormatException, ParseException { SQLProcessor sqlProcessor = new SQLProcessorImpl(); Map<String, ResultSet> results = new HashMap<String, ResultSet>(); List<String> processed = new LinkedList<String>(); if ((job.getIsBurst() != null) && (job.getIsBurst())) { ResultSet burstResults = sqlProcessor.getResults(conn, job.getBurstQuery()); List<RunnerJobParameter> params = job.getParameters(); while (burstResults.next()) { // populate the parameters List<RunnerJobParameter> populatedParams = new LinkedList<RunnerJobParameter>(); String name = ""; for (RunnerJobParameter param : params) { RunnerJobParameter paramNew = new RunnerJobParameter(); // copy the parameter paramNew.setParameterIdx(param.getParameterIdx()); paramNew.setRunnerJob(job); paramNew.setParameterBurstColumn(param .getParameterBurstColumn()); paramNew.setParameterType(param.getParameterType()); paramNew.setParameterValue(param.getParameterValue()); // is this parameter populated via the burst query if ((param.getParameterBurstColumn() != null) && (!param.getParameterBurstColumn().isEmpty())) { // Check that the parameter has not already been // assigned a value by the user if (((param.getParameterValue() == null) || param .getParameterValue().isEmpty())) { // no value already assigned so pull from DB paramNew.setParameterValue(burstResults.getObject( param.getParameterBurstColumn()).toString()); populatedParams.add(paramNew); logger.trace("added populated param" + param.getParameterIdx() + " - value - " + param.getParameterValue()); } else { // value already assigned so carry on using that. logger.trace("using overide value" + param.getParameterValue()); populatedParams.add(paramNew); } } else { // not populated via the bursting query logger.trace("standard parameter"); populatedParams.add(paramNew); } if (!name.isEmpty()) { name = name + "_"; } name = name + paramNew.getParameterValue(); } logger.trace("file/tab is called:" + name); // process the query with the results in if (!processed.contains(name)) { // hack to prevent it repeating its self on the viewer ResultSet rs = sqlProcessor.getResults(conn, job.getQuery(), populatedParams); if (rs.next()) { rs.beforeFirst(); results.put(name, rs); } processed.add(name); } } } else { String name = "Results"; ResultSet rs; if ((job.getParameters() != null) && (job.getParameters().size() > 0)) { rs = sqlProcessor.getResults(conn, job.getQuery(), job.getParameters()); } else { rs = sqlProcessor.getResults(conn, job.getQuery()); } results.put(name, rs); } return results; } private void renderReport(ResultSet results, String label, String url, AbstractRenderer renderer) throws RenderException, IOException, SQLException { renderer.generateReport(results, label, url); } public void setReportService(ReportService reportService) { this.reportService = reportService; } public void setDatasourceService(DatasourceService datasourceService) { this.datasourceService = datasourceService; } public void setConfigurationService(ConfigurationService configurationService) { this.configurationService = configurationService; } }