1 21 package com.jaspersoft.jasperserver.api.engine.scheduling.quartz; 22 23 import java.io.ByteArrayOutputStream ; 24 import java.io.IOException ; 25 import java.io.PrintWriter ; 26 import java.io.StringWriter ; 27 import java.text.SimpleDateFormat ; 28 import java.util.ArrayList ; 29 import java.util.Date ; 30 import java.util.HashMap ; 31 import java.util.Iterator ; 32 import java.util.List ; 33 import java.util.Locale ; 34 import java.util.Map ; 35 import java.util.Set ; 36 import java.util.zip.ZipEntry ; 37 import java.util.zip.ZipOutputStream ; 38 39 import javax.mail.MessagingException ; 40 import javax.mail.internet.MimeMessage ; 41 42 import net.sf.jasperreports.engine.JRException; 43 import net.sf.jasperreports.engine.JRExporterParameter; 44 import net.sf.jasperreports.engine.JasperPrint; 45 import net.sf.jasperreports.engine.export.JExcelApiExporter; 46 import net.sf.jasperreports.engine.export.JRHtmlExporter; 47 import net.sf.jasperreports.engine.export.JRHtmlExporterParameter; 48 import net.sf.jasperreports.engine.export.JRHyperlinkProducerFactory; 49 import net.sf.jasperreports.engine.export.JRRtfExporter; 50 51 import org.apache.commons.logging.Log; 52 import org.apache.commons.logging.LogFactory; 53 import org.quartz.Job; 54 import org.quartz.JobDataMap; 55 import org.quartz.JobExecutionContext; 56 import org.quartz.JobExecutionException; 57 import org.quartz.SchedulerContext; 58 import org.springframework.core.io.ByteArrayResource; 59 import org.springframework.mail.javamail.JavaMailSender; 60 import org.springframework.mail.javamail.MimeMessageHelper; 61 62 import com.jaspersoft.jasperserver.api.JSException; 63 import com.jaspersoft.jasperserver.api.JSExceptionWrapper; 64 import com.jaspersoft.jasperserver.api.common.domain.ExecutionContext; 65 import com.jaspersoft.jasperserver.api.common.domain.LogEvent; 66 import com.jaspersoft.jasperserver.api.common.domain.impl.ExecutionContextImpl; 67 import com.jaspersoft.jasperserver.api.common.util.LocaleHelper; 68 import com.jaspersoft.jasperserver.api.engine.common.service.EngineService; 69 import com.jaspersoft.jasperserver.api.engine.common.service.LoggingService; 70 import com.jaspersoft.jasperserver.api.engine.common.service.SecurityContextProvider; 71 import com.jaspersoft.jasperserver.api.engine.jasperreports.domain.impl.ReportUnitRequest; 72 import com.jaspersoft.jasperserver.api.engine.jasperreports.domain.impl.ReportUnitResult; 73 import com.jaspersoft.jasperserver.api.engine.scheduling.domain.ReportJob; 74 import com.jaspersoft.jasperserver.api.engine.scheduling.domain.ReportJobIdHolder; 75 import com.jaspersoft.jasperserver.api.engine.scheduling.domain.ReportJobMailNotification; 76 import com.jaspersoft.jasperserver.api.engine.scheduling.domain.ReportJobRepositoryDestination; 77 import com.jaspersoft.jasperserver.api.engine.scheduling.service.ReportJobsPersistenceService; 78 import com.jaspersoft.jasperserver.api.metadata.common.domain.ContentResource; 79 import com.jaspersoft.jasperserver.api.metadata.common.domain.Folder; 80 import com.jaspersoft.jasperserver.api.metadata.common.domain.Resource; 81 import com.jaspersoft.jasperserver.api.metadata.common.domain.client.ContentResourceImpl; 82 import com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryService; 83 84 88 public class ReportExecutionJob implements Job { 89 90 private static final Log log = LogFactory.getLog(ReportExecutionJob.class); 91 92 public static final String REPORT_PARAMETER_SCHEDULED_TIME = "_ScheduledTime"; 93 94 public static final String REPOSITORY_FILENAME_SEQUENCE_SEPARATOR = "-"; 95 public static final String REPOSITORY_FILENAME_TIMESTAMP_SEQUENCE_PATTERN = "yyyyMMddHHmm"; 96 97 public static final String SCHEDULER_CONTEXT_KEY_JOB_PERSISTENCE_SERVICE = "jobPersistenceService"; 98 public static final String SCHEDULER_CONTEXT_KEY_ENGINE_SERVICE = "engineService"; 99 public static final String SCHEDULER_CONTEXT_KEY_REPOSITORY = "repositoryService"; 100 public static final String SCHEDULER_CONTEXT_KEY_MAIL_SENDER = "mailSender"; 101 public static final String SCHEDULER_CONTEXT_KEY_MAIL_FROM_ADDRESS = "mailFromAddress"; 102 public static final String SCHEDULER_CONTEXT_KEY_LOGGING_SERVICE = "loggingService"; 103 public static final String SCHEDULER_CONTEXT_KEY_SECURITY_CONTEXT_PROVIDER = "securityContextProvider"; 104 public static final String SCHEDULER_CONTEXT_KEY_HYPERLINK_PRODUCER_FACTORY = "hyperlinkProducerFactory"; 105 106 public static final String JOB_DATA_KEY_DETAILS_ID = "jobDetailsID"; 107 public static final String JOB_DATA_KEY_USERNAME = "jobUser"; 108 109 public static final String LOGGING_COMPONENT = "reportScheduler"; 110 111 protected String username; 112 protected ReportJob jobDetails; 113 protected JobExecutionContext jobContext; 114 protected SchedulerContext schedulerContext; 115 protected ExecutionContext executionContext; 116 117 public void execute(JobExecutionContext context) throws JobExecutionException { 118 try { 119 this.jobContext = context; 120 this.schedulerContext = jobContext.getScheduler().getContext(); 121 122 this.username = getUsername(); 123 SecurityContextProvider securityContextProvider = getSecurityContextProvider(); 124 securityContextProvider.setAuthenticatedUser(this.username); 125 try { 126 executionContext = getExecutionContext(); 127 jobDetails = getJobDetails(); 128 updateExecutionContextDetails(); 129 130 ReportUnitResult result = executeReport(); 131 sendToDestinations(result); 132 } catch (Exception e) { 133 handleException(e); 134 } finally { 135 securityContextProvider.revertAuthenticatedUser(); 136 } 137 } catch (JobExecutionException e) { 138 throw e; 139 } catch (Exception e) { 140 handleException(e); 141 } finally { 142 clear(); 143 } 144 } 145 146 protected void clear() { 147 jobContext = null; 148 schedulerContext = null; 149 jobDetails = null; 150 executionContext = null; 151 username = null; 152 } 153 154 protected String getUsername() { 155 JobDataMap jobDataMap = jobContext.getTrigger().getJobDataMap(); 156 return jobDataMap.getString(JOB_DATA_KEY_USERNAME); 157 } 158 159 protected ExecutionContext getExecutionContext() { 160 return new ExecutionContextImpl(); 161 } 162 163 protected void updateExecutionContextDetails() { 164 ((ExecutionContextImpl) executionContext).setLocale(getJobLocale()); 165 } 166 167 protected Locale getJobLocale() { 168 String localeCode = jobDetails.getOutputLocale(); 169 Locale locale; 170 if (localeCode != null && localeCode.length() > 0) { 171 locale = LocaleHelper.getInstance().getLocale(localeCode); 172 } else { 173 locale = null; 174 } 175 return locale; 176 } 177 178 protected void handleException(Exception exc) throws JobExecutionException { 179 log.error(exc, exc); 180 181 try { 182 logException(exc); 183 } catch (Exception e) { 184 log.error(e, e); 185 throwJobExecutionException(exc); 186 } 187 188 throwJobExecutionException(exc); 189 } 190 191 protected void throwJobExecutionException(Exception exc) throws JobExecutionException { 192 JobExecutionException jobExecutionException = new JobExecutionException(exc); 193 throw jobExecutionException; 194 } 195 196 protected void logException(Exception e) { 197 LoggingService loggingService = getLoggingService(); 198 LogEvent event = loggingService.instantiateLogEvent(); 199 event.setComponent(LOGGING_COMPONENT); 200 event.setType(LogEvent.TYPE_ERROR); 201 event.setMessageCode("log.error.report.job.failed"); 202 if (jobDetails != null) { 203 event.setResourceURI(jobDetails.getSource().getReportUnitURI()); 204 } 205 206 StringWriter writer = new StringWriter (); 207 PrintWriter printWriter = new PrintWriter (writer); 208 if (jobDetails != null) { 209 printWriter.println("Job: " + jobDetails.getLabel() + " (ID: " + jobDetails.getId() + ")"); 210 printWriter.println("Report unit: " + jobDetails.getSource().getReportUnitURI()); 211 } 212 printWriter.println("Quartz Job: " + jobContext.getJobDetail().getFullName()); 213 printWriter.println("Quartz Trigger: " + jobContext.getTrigger().getFullName()); 214 printWriter.println(e.getMessage()); 215 e.printStackTrace(printWriter); 216 printWriter.flush(); 217 event.setText(writer.toString()); 218 event.setState(LogEvent.STATE_UNREAD); 219 220 loggingService.log(event); 221 } 222 223 protected SecurityContextProvider getSecurityContextProvider() { 224 return (SecurityContextProvider) schedulerContext.get(SCHEDULER_CONTEXT_KEY_SECURITY_CONTEXT_PROVIDER); 225 } 226 227 protected LoggingService getLoggingService() { 228 return (LoggingService) schedulerContext.get(SCHEDULER_CONTEXT_KEY_LOGGING_SERVICE); 229 } 230 231 protected ReportJob getJobDetails() { 232 ReportJobsPersistenceService persistenceService = getPersistenceService(); 233 JobDataMap jobDataMap = jobContext.getTrigger().getJobDataMap(); 234 long jobId = jobDataMap.getLong(JOB_DATA_KEY_DETAILS_ID); 235 ReportJob job = persistenceService.loadJob(executionContext, new ReportJobIdHolder(jobId)); 236 return job; 237 } 238 239 protected ReportJobsPersistenceService getPersistenceService() { 240 return (ReportJobsPersistenceService) schedulerContext.get(SCHEDULER_CONTEXT_KEY_JOB_PERSISTENCE_SERVICE); 241 } 242 243 protected EngineService getEngineService() { 244 EngineService engineService = (EngineService) schedulerContext.get(SCHEDULER_CONTEXT_KEY_ENGINE_SERVICE); 245 return engineService; 246 } 247 248 protected ReportUnitResult executeReport() { 249 Map parametersMap = jobDetails.getSource().getParametersMap(); 250 if (parametersMap == null) { 251 parametersMap = new HashMap (); 252 } 253 putAdditionalParameters(parametersMap); 254 255 ReportUnitRequest request = new ReportUnitRequest(jobDetails.getSource().getReportUnitURI(), parametersMap); 256 257 EngineService engineService = getEngineService(); 258 return (ReportUnitResult) engineService.execute(executionContext, request); 259 } 260 261 protected void putAdditionalParameters(Map parametersMap) { 262 if (!parametersMap.containsKey(REPORT_PARAMETER_SCHEDULED_TIME)) { 263 Date scheduledFireTime = jobContext.getScheduledFireTime(); 264 parametersMap.put(REPORT_PARAMETER_SCHEDULED_TIME, scheduledFireTime); 265 } 266 } 267 268 protected void sendToDestinations(ReportUnitResult result) { 269 JasperPrint jasperPrint = result.getJasperPrint(); 270 272 String baseFilename = jobDetails.getBaseOutputFilename(); 273 if (jobDetails.getContentRepositoryDestination().isSequentialFilenames()) { 274 Date scheduledTime = jobContext.getScheduledFireTime(); 275 SimpleDateFormat format = new SimpleDateFormat (REPOSITORY_FILENAME_TIMESTAMP_SEQUENCE_PATTERN); 276 baseFilename = jobDetails.getBaseOutputFilename() + REPOSITORY_FILENAME_SEQUENCE_SEPARATOR + format.format(scheduledTime); 277 } else { 278 baseFilename = jobDetails.getBaseOutputFilename(); 279 } 280 281 ReportJobMailNotification mailNotification = jobDetails.getMailNotification(); 282 boolean hasMailAttachments = mailNotification != null && mailNotification.getResultSendType() == ReportJobMailNotification.RESULT_SEND_ATTACHMENT; 283 List mailAttachments = hasMailAttachments ? new ArrayList () : null; 284 Set outputFormats = jobDetails.getOutputFormats(); 285 for (Iterator it = outputFormats.iterator(); it.hasNext();) { 286 Byte format = (Byte ) it.next(); 287 ReportOutput output = getReportOutput(jasperPrint, format.byteValue(), baseFilename); 288 saveToRepository(output); 289 if (hasMailAttachments) { 290 mailAttachments.add(output); 291 } 292 } 293 294 sendMailNotification(mailAttachments, jobDetails); 295 } 296 297 protected ReportOutput getReportOutput(JasperPrint jasperPrint, byte format, String baseFilename) { 298 ReportOutput output; 299 switch (format) { 300 case ReportJob.OUTPUT_FORMAT_PDF: { 301 output = getPdfOutput(jasperPrint, baseFilename); 302 break; 303 } 304 case ReportJob.OUTPUT_FORMAT_HTML: { 305 output = getHtmlOutput(jasperPrint, baseFilename); 306 break; 307 } 308 case ReportJob.OUTPUT_FORMAT_XLS: { 309 output = getXlsOutput(jasperPrint, baseFilename); 310 break; 311 } 312 case ReportJob.OUTPUT_FORMAT_RTF: { 313 output = getRtfOutput(jasperPrint, baseFilename); 314 break; 315 } 316 default: 317 throw new JSException("Unknown report output format " + format); 318 } 319 return output; 320 } 321 322 protected String getChildrenFolderName(String resourceName) { 323 return getRepository().getChildrenFolderName(resourceName); 324 } 325 326 protected ReportOutput getPdfOutput(JasperPrint jasperPrint, String baseFilename) { 327 Map params = new HashMap (); 328 params.put(JRExporterParameter.JASPER_PRINT, jasperPrint); 329 ByteArrayOutputStream baos = new ByteArrayOutputStream (); 330 params.put(JRExporterParameter.OUTPUT_STREAM, baos); 331 getEngineService().exportToPdf(executionContext, jobDetails.getSource().getReportUnitURI(), params); 332 byte[] pdfData = baos.toByteArray(); 333 String filename = baseFilename + ".pdf"; 334 return new ReportOutput(pdfData, ContentResource.TYPE_PDF, filename); 335 } 336 337 protected ReportOutput getHtmlOutput(JasperPrint jasperPrint, String baseFilename) { 338 try { 339 String filename = baseFilename + ".html"; 340 JRHtmlExporter exporter = new JRHtmlExporter(); 341 exporter 342 .setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint); 343 ByteArrayOutputStream bout = new ByteArrayOutputStream (); 344 exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, bout); 345 Map images = new HashMap (); 346 exporter.setParameter(JRHtmlExporterParameter.IMAGES_MAP, images); 347 exporter.setParameter(JRHtmlExporterParameter.IMAGES_URI, 348 getChildrenFolderName(filename) + '/'); 349 350 JRHyperlinkProducerFactory hyperlinkProducerFactory = getHyperlinkProducerFactory(); 351 if (hyperlinkProducerFactory != null) { 352 exporter.setParameter(JRHtmlExporterParameter.HYPERLINK_PRODUCER_FACTORY, hyperlinkProducerFactory); 353 } 354 355 exporter.exportReport(); 356 357 byte[] htmlData = bout.toByteArray(); 358 ReportOutput htmlOutput = new ReportOutput(htmlData, 359 ContentResource.TYPE_HTML, filename); 360 361 for (Iterator it = images.entrySet().iterator(); it.hasNext();) { 362 Map.Entry imageEntry = (Map.Entry ) it.next(); 363 String imageName = (String ) imageEntry.getKey(); 364 byte[] imageData = (byte[]) imageEntry.getValue(); 365 ReportOutput image = new ReportOutput(imageData, 366 ContentResource.TYPE_HTML, imageName); htmlOutput.addChild(image); 368 } 369 370 return htmlOutput; 371 } catch (JRException e) { 372 throw new JSExceptionWrapper(e); 373 } 374 } 375 376 protected JRHyperlinkProducerFactory getHyperlinkProducerFactory() { 377 JRHyperlinkProducerFactory engineService = (JRHyperlinkProducerFactory) schedulerContext.get(SCHEDULER_CONTEXT_KEY_HYPERLINK_PRODUCER_FACTORY); 378 return engineService; 379 } 380 381 protected ReportOutput getXlsOutput(JasperPrint jasperPrint, String baseFilename) { 382 try { 383 JExcelApiExporter exporter = new JExcelApiExporter(); 384 exporter 385 .setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint); 386 ByteArrayOutputStream bout = new ByteArrayOutputStream (); 387 exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, bout); 388 exporter.exportReport(); 389 390 byte[] xlsData = bout.toByteArray(); 391 String filename = baseFilename + ".xls"; 392 return new ReportOutput(xlsData, ContentResource.TYPE_XLS, filename); 393 } catch (JRException e) { 394 throw new JSExceptionWrapper(e); 395 } 396 } 397 398 protected ReportOutput getRtfOutput(JasperPrint jasperPrint, String baseFilename) { 399 try { 400 JRRtfExporter exporter = new JRRtfExporter(); 401 exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint); 402 ByteArrayOutputStream bout = new ByteArrayOutputStream (); 403 exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, bout); 404 exporter.exportReport(); 405 406 byte[] rtfData = bout.toByteArray(); 407 String fileName = baseFilename + ".rtf"; 408 return new ReportOutput(rtfData, ContentResource.TYPE_RTF, fileName); 409 } catch (JRException e) { 410 throw new JSExceptionWrapper(e); 411 } 412 } 413 414 protected void saveToRepository(ReportOutput output) { 415 RepositoryService repositoryService = getRepository(); 416 ReportJobRepositoryDestination repositoryDestination = jobDetails.getContentRepositoryDestination(); 417 418 List children = output.getChildren(); 419 List childResources = new ArrayList (children.size()); 420 for (Iterator it = children.iterator(); it.hasNext();) { 421 ReportOutput childOutput = (ReportOutput) it.next(); 422 423 ContentResource childRes = new ContentResourceImpl(); 424 childRes.setName(childOutput.getFilename()); 425 childRes.setLabel(childOutput.getFilename()); 426 childRes.setFileType(childOutput.getFileType()); 427 childRes.setData(childOutput.getData()); 428 childResources.add(childRes); 429 } 430 431 ContentResource contentRes = null; 432 if (repositoryDestination.isOverwriteFiles()) { 433 String resURI = repositoryDestination.getFolderURI() + Folder.SEPARATOR + output.getFilename(); 434 Resource res = repositoryService.getResource(executionContext, resURI); 435 if (res != null) { 436 if (!(res instanceof ContentResource)) { 437 throw new JSException("Resurce \"" + resURI + "\" is not a content resource"); 438 } 439 contentRes = (ContentResource) res; 440 } 441 } 442 443 if (contentRes == null) { 444 contentRes = new ContentResourceImpl(); 445 contentRes.setName(output.getFilename()); 446 contentRes.setLabel(jobDetails.getBaseOutputFilename()); 447 contentRes.setParentFolder(repositoryDestination.getFolderURI()); 448 } 449 450 contentRes.setFileType(output.getFileType()); 451 contentRes.setData(output.getData()); 452 contentRes.setResources(childResources); 453 454 repositoryService.saveResource(null, contentRes); 455 } 456 457 protected RepositoryService getRepository() { 458 RepositoryService repositoryService = (RepositoryService) schedulerContext.get(SCHEDULER_CONTEXT_KEY_REPOSITORY); 459 return repositoryService; 460 } 461 462 protected void sendMailNotification(List mailAttachments, ReportJob job) { 463 ReportJobMailNotification mailNotification = job.getMailNotification(); 464 if (mailNotification != null) { 465 JavaMailSender mailSender = getMailSender(); 466 String fromAddress = getFromAddress(); 467 try { 468 MimeMessage message = mailSender.createMimeMessage(); 469 MimeMessageHelper messageHelper = new MimeMessageHelper(message, true); 470 messageHelper.setFrom(fromAddress); 471 messageHelper.setSubject(mailNotification.getSubject()); 472 messageHelper.setText(mailNotification.getMessageText()); 473 474 addMailRecipients(mailNotification, messageHelper); 475 476 if (mailAttachments != null) { 477 for (Iterator it = mailAttachments.iterator(); it.hasNext();) { 478 ReportOutput output = (ReportOutput) it.next(); 479 attachOutput(messageHelper, output); 480 } 481 } 482 483 mailSender.send(message); 484 } catch (MessagingException e) { 485 log.error("Error while sending report job result mail", e); 486 throw new JSExceptionWrapper(e); 487 } 488 } 489 } 490 491 protected void addMailRecipients(ReportJobMailNotification mailNotification, MimeMessageHelper messageHelper) throws MessagingException { 492 List toAddresses = mailNotification.getToAddresses(); 493 if (toAddresses != null && !toAddresses.isEmpty()) { 494 String [] addressArray = new String [toAddresses.size()]; 495 toAddresses.toArray(addressArray); 496 messageHelper.setTo(addressArray); 497 } 498 499 List ccAddresses = mailNotification.getCcAddresses(); 500 if (ccAddresses != null && !ccAddresses.isEmpty()) { 501 String [] addressArray = new String [ccAddresses.size()]; 502 ccAddresses.toArray(addressArray); 503 messageHelper.setCc(addressArray); 504 } 505 506 List bccAddresses = mailNotification.getBccAddresses(); 507 if (bccAddresses != null && !bccAddresses.isEmpty()) { 508 String [] addressArray = new String [bccAddresses.size()]; 509 bccAddresses.toArray(addressArray); 510 messageHelper.setBcc(addressArray); 511 } 512 } 513 514 protected void attachOutput(MimeMessageHelper messageHelper, ReportOutput output) throws MessagingException { 515 if (output.getChildren().isEmpty()) { 516 messageHelper.addAttachment(output.getFilename(), new ByteArrayResource(output.getData())); 517 } else { 518 ByteArrayOutputStream bout = new ByteArrayOutputStream (); 519 ZipOutputStream zipOut = new ZipOutputStream (bout); 520 try { 521 zipOut.putNextEntry(new ZipEntry (output.getFilename())); 522 zipOut.write(output.getData()); 523 zipOut.closeEntry(); 524 525 for (Iterator it = output.getChildren().iterator(); it.hasNext();) { 526 ReportOutput child = (ReportOutput) it.next(); 527 String childName = getChildrenFolderName(output.getFilename()) + '/' + child.getFilename(); 528 zipOut.putNextEntry(new ZipEntry (childName)); 529 zipOut.write(child.getData()); 530 zipOut.closeEntry(); 531 } 532 533 zipOut.finish(); 534 zipOut.flush(); 535 } catch (IOException e) { 536 throw new JSExceptionWrapper(e); 537 } 538 539 byte[] zipData = bout.toByteArray(); 540 String zipName = output.getFilename() + ".zip"; 541 messageHelper.addAttachment(zipName, new ByteArrayResource(zipData)); 542 } 543 } 544 545 protected String getFromAddress() { 546 String fromAddress = (String ) schedulerContext.get(SCHEDULER_CONTEXT_KEY_MAIL_FROM_ADDRESS); 547 return fromAddress; 548 } 549 550 protected JavaMailSender getMailSender() { 551 JavaMailSender mailSender = (JavaMailSender) schedulerContext.get(SCHEDULER_CONTEXT_KEY_MAIL_SENDER); 552 return mailSender; 553 } 554 555 556 protected static class ReportOutput { 557 private final byte[] data; 558 private final String fileType; 559 private final String filename; 560 private final List children; 561 562 public ReportOutput(byte[] data, String fileType, String filename) { 563 this.data = data; 564 this.fileType = fileType; 565 this.filename = filename; 566 this.children = new ArrayList (); 567 } 568 569 public byte[] getData() { 570 return data; 571 } 572 573 public String getFilename() { 574 return filename; 575 } 576 577 public String getFileType() { 578 return fileType; 579 } 580 581 public List getChildren() { 582 return children; 583 } 584 585 public void addChild(ReportOutput child) { 586 children.add(child); 587 } 588 } 589 } 590 | Popular Tags |