/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ro.nextreports.server.schedule; import java.io.File; import java.io.Serializable; import java.sql.Connection; import java.sql.Time; import java.sql.Timestamp; import java.text.MessageFormat; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; import java.util.UUID; import java.util.ArrayList; import javax.jcr.RepositoryException; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.HTTP; import org.joda.time.DateTime; import org.joda.time.Seconds; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.mail.javamail.JavaMailSenderImpl; import ro.nextreports.server.StorageConstants; import ro.nextreports.server.audit.AuditEvent; import ro.nextreports.server.audit.Auditor; import ro.nextreports.server.distribution.Destination; import ro.nextreports.server.distribution.DestinationType; import ro.nextreports.server.distribution.DistributionContext; import ro.nextreports.server.distribution.DistributionException; import ro.nextreports.server.distribution.Distributor; import ro.nextreports.server.distribution.DistributorFactory; import ro.nextreports.server.domain.AclEntry; import ro.nextreports.server.domain.NextContent; import ro.nextreports.server.domain.Report; import ro.nextreports.server.domain.ReportResultEvent; import ro.nextreports.server.domain.ReportRuntimeTemplate; import ro.nextreports.server.domain.RunReportHistory; import ro.nextreports.server.domain.SchedulerBatchDefinition; import ro.nextreports.server.domain.SchedulerJob; import ro.nextreports.server.domain.Settings; import ro.nextreports.server.domain.ShortcutType; import ro.nextreports.server.domain.SmtpAlertDestination; import ro.nextreports.server.exception.FormatNotSupportedException; import ro.nextreports.server.exception.ReportEngineException; import ro.nextreports.server.report.ReportConstants; import ro.nextreports.server.report.next.NextUtil; import ro.nextreports.server.report.util.ReportUtil; import ro.nextreports.server.service.DataSourceService; import ro.nextreports.server.service.ReportService; import ro.nextreports.server.service.SecurityService; import ro.nextreports.server.service.StorageService; import ro.nextreports.server.util.ConnectionUtil; import ro.nextreports.server.util.PermissionUtil; import ro.nextreports.server.util.StorageUtil; import ro.nextreports.server.web.language.LanguageManager; import ro.nextreports.engine.ReportRunner; import ro.nextreports.engine.ReportRunnerException; import ro.nextreports.engine.condition.ConditionalExpression; import ro.nextreports.engine.condition.exception.ConditionalException; import ro.nextreports.engine.exporter.Alert; import ro.nextreports.engine.exporter.exception.NoDataFoundException; import ro.nextreports.engine.queryexec.IdName; import ro.nextreports.engine.queryexec.QueryParameter; /** * @author Decebal Suiu * @author Mihai Dinca-Panaitescu */ public class RunReportJob implements Job { private static final Logger LOG = LoggerFactory.getLogger(RunReportJob.class); public static final String SCHEDULER_JOB = "SCHEDULER_JOB"; public static final String MAIL_SENDER = "MAIL_SENDER"; public static final String REPORT_SERVICE = "REPORT_SERVICE"; public static final String STORAGE_SERVICE = "STORAGE_SERVICE"; public static final String SECURITY_SERVICE = "SECURITY_SERVICE"; public static final String DATASOURCE_SERVICE = "DATASOURCE_SERVICE"; public static final String RUNNER_ID = "RUNNER_ID"; public static final String RUNNER_TYPE = "RUNNER_TYPE"; public static final String RUNNER_KEY = "RUNNER_KEY"; public static final String REPORT_TYPE = "REPORT_TYPE"; public static final String AUDIT_EVENT = " AUDIT_EVENT"; public static final String AUDITOR = "AUDITOR"; private Map<Serializable, String> batchMailMap; public void execute(JobExecutionContext context) throws JobExecutionException { JobDataMap dataMap = context.getMergedJobDataMap(); SchedulerJob schedulerJob = (SchedulerJob) dataMap.get(SCHEDULER_JOB); Report report = schedulerJob.getReport(); DataSourceService dataSourceService = (DataSourceService) dataMap.get(DATASOURCE_SERVICE); StorageService storageService = (StorageService) dataMap.get(STORAGE_SERVICE); final JavaMailSenderImpl mailSender = (JavaMailSenderImpl) dataMap.get(MAIL_SENDER); if (storageService.getSettings().getMailServer().getUsername() != null) { mailSender.setUsername(storageService.getSettings().getMailServer().getUsername()); mailSender.setPassword(storageService.getSettings().getMailServer().getPassword()); mailSender.getJavaMailProperties().put("mail.smtp.auth", true); } else if (mailSender.getUsername() == null) { // username password are not set inside configuration xml file mailSender.getJavaMailProperties().put("mail.smtp.auth", false); } if (storageService.getSettings().getMailServer().getEnableTls() != null) { mailSender.getJavaMailProperties().put("mail.smtp.starttls.enable", storageService.getSettings().getMailServer().getEnableTls()); } Locale locale = LanguageManager.getInstance().getLocale(storageService.getSettings().getLanguage()); ResourceBundle bundle = ResourceBundle.getBundle("ro.nextreports.server.web.NextServerApplication", locale); SchedulerBatchDefinition batchDef = schedulerJob.getBatchDefinition(); List<IdName> batchValues = new ArrayList<IdName>(); QueryParameter reportParameter = ReportUtil.getBatchQueryParameter(schedulerJob, storageService.getSettings()); if (reportParameter != null) { try { batchValues = dataSourceService.getParameterValues(report.getDataSource(), reportParameter); } catch (Exception e) { LOG.error(e.getMessage(), e); e.printStackTrace(); } } if (batchValues.isEmpty()) { executeOneReport(context, bundle, null); } else { if ((batchDef != null) && (batchDef.getDataQuery() != null)) { try { batchMailMap = ReportUtil.getBatchMailMap(batchDef.getDataQuery(), storageService, report.getDataSource()); } catch (Exception e) { LOG.error(e.getMessage(), e); } } for (IdName batchValue : batchValues) { schedulerJob.getReportRuntime().updateParameterValue(reportParameter.getName(), batchValue); executeOneReport(context, bundle, batchValue); } } } private void executeOneReport(JobExecutionContext context, ResourceBundle bundle, IdName batchValue) { JobDataMap dataMap = context.getMergedJobDataMap(); SchedulerJob schedulerJob = (SchedulerJob) dataMap.get(SCHEDULER_JOB); Report report = schedulerJob.getReport(); ReportService reportService = (ReportService) dataMap.get(REPORT_SERVICE); DataSourceService dataSourceService = (DataSourceService) dataMap.get(DATASOURCE_SERVICE); StorageService storageService = (StorageService) dataMap.get(STORAGE_SERVICE); final SecurityService securityService = (SecurityService) dataMap.get(SECURITY_SERVICE); List<Destination> destinations = schedulerJob.getDestinations(); final JavaMailSenderImpl mailSender = (JavaMailSenderImpl) dataMap.get(MAIL_SENDER); final String mailFrom = storageService.getSettings().getMailServer().getFrom(); String reportsPath = new File(storageService.getSettings().getReportsHome()).getAbsolutePath(); Auditor auditor = (Auditor) dataMap.get(AUDITOR); AuditEvent auditEvent = (AuditEvent) dataMap.get(AUDIT_EVENT); String runnerType = dataMap.getString(RUNNER_TYPE); String runnerId = dataMap.getString(RUNNER_ID); String creator = ""; AclEntry[] granted = securityService.getGrantedById(report.getId()); if (RunReportHistory.SCHEDULER.equals(runnerType)) { creator = schedulerJob.getCreatedBy(); } else if (RunReportHistory.USER.equals(runnerType)) { try { creator = storageService.getEntityById(runnerId).getName(); } catch (Exception e) { e.printStackTrace(); LOG.error(e.getMessage(), e); } } String key = dataMap.getString(RUNNER_KEY); boolean error = false; String message = "Ok"; String url = ""; String fileName = null; Connection connection = null; try { /* try { Thread.sleep(2 * 60 * 1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } */ // for Scheduled reports // take care for INTERVAL template values // and set start_date and end_date accordingly if (!schedulerJob.isRunNow()) { ReportRuntimeTemplate template = schedulerJob.getTemplate(); if (template != null) { ShortcutType type = template.getShortcutType(); if ((type != null) && !ShortcutType.NONE.equals(type)) { Date[] dates = type.getTimeShortcutType().getDates(); Map<String, Object> map = schedulerJob.getReportRuntime().getParametersValues(); if (map.containsKey(QueryParameter.INTERVAL_START_DATE_NAME)) { Object start = map.get(QueryParameter.INTERVAL_START_DATE_NAME); Object value = null; if (start instanceof Date) { value = dates[0]; } else if (start instanceof Timestamp) { value = new Timestamp(dates[0].getTime()); } else if (start instanceof Time) { value = new Time(dates[0].getTime()); } if (value != null) { schedulerJob.getReportRuntime().updateParameterValue(QueryParameter.INTERVAL_START_DATE_NAME, (Serializable)value); } } if (map.containsKey(QueryParameter.INTERVAL_END_DATE_NAME)) { Object start = map.get(QueryParameter.INTERVAL_END_DATE_NAME); Object value = null; if (start instanceof Date) { value = dates[1]; } else if (start instanceof Timestamp) { value = new Timestamp(dates[1].getTime()); } else if (start instanceof Time) { value = new Time(dates[1].getTime()); } if (value != null) { schedulerJob.getReportRuntime().updateParameterValue(QueryParameter.INTERVAL_END_DATE_NAME, (Serializable)value); } } } } } if (LOG.isDebugEnabled()) { LOG.debug("Run report '" + report.getPath() + "'"); // debug runtime parameters Map<String, Object> parametersValues = schedulerJob.getReportRuntime().getParametersValues(); StringBuilder sb = new StringBuilder("\r\nRuntime parameters for '"); sb.append(report.getPath()); sb.append("'\r\n"); sb.append(ReportUtil.getDebugParameters(parametersValues)); LOG.debug(sb.toString()); } String[] result = new String[2]; if (report.isAlarmType() || report.isIndicatorType() || report.isDisplayType()) { // for alarm alert there is no url ro.nextreports.engine.Report nextReport = NextUtil.getNextReport(storageService.getSettings(), (NextContent) report.getContent()); final ReportRunner reportRunner = new ReportRunner(); reportRunner.setParameterValues(schedulerJob.getReportRuntime().getParametersValues()); reportRunner.setReport(nextReport); if (report.isAlarmType()) { reportRunner.setFormat(ReportRunner.ALARM_FORMAT); } else if (report.isDisplayType()) { reportRunner.setFormat(ReportRunner.DISPLAY_FORMAT); } else { reportRunner.setFormat(ReportRunner.INDICATOR_FORMAT); } try { connection = ConnectionUtil.createConnection(storageService, report.getDataSource()); } catch (RepositoryException e) { throw new ReportRunnerException("Cannot connect to database", e); } reportRunner.setConnection(connection); List<SmtpAlertDestination> alertDestinations = getAlertDestinations(destinations); if (alertDestinations.size() == 0) { LOG.error("Alarm '" + report.getPath() + "' has no alert destination."); ConnectionUtil.closeConnection(connection); return; } List<Alert> alerts = new ArrayList<Alert>(); for (final SmtpAlertDestination alertDestination : alertDestinations) { alerts.add(new Alert() { protected boolean isActive(Object value) { ConditionalExpression ce = new ConditionalExpression(alertDestination.getOperator()); ce.setRightOperand(ce.getOperand(alertDestination.getRightOperand())); ce.setRightOperand2(ce.getOperand(alertDestination.getRightOperand2())); ce.setLeftOperand((Serializable) value); try { boolean result = ce.evaluate(); return result; } catch (ConditionalException e) { e.printStackTrace(); } return false; } protected void run(Object value, String message) { Distributor alertDistributor = DistributorFactory.getDistributor(DestinationType.ALERT.toString()); try { DistributionContext distributionContext = new DistributionContext(); distributionContext.setSecurityService(securityService); distributionContext.setMailFrom(mailFrom); distributionContext.setMailSender(mailSender); String sValue = (value == null) ? "" : value.toString(); String mailMessage = alertDestination.getMailBody().replaceAll("\\$\\{val\\}", sValue); distributionContext.setMessage(mailMessage); alertDestination.setMailBody(mailMessage); distributionContext.setAlertMessage(message); alertDistributor.distribute(null, alertDestination, distributionContext); } catch (DistributionException e) { String failedMessage = "Distribution " + alertDestination.getName(); message += "\r\n" + failedMessage + " : " + e.getMessage(); LOG.error(message, e); e.printStackTrace(); } } }); } reportRunner.setAlerts(alerts); reportRunner.run(); } else { // not null when run String author = schedulerJob.getCreator(); if (author == null) { // when schedule author = schedulerJob.getCreatedBy(); } result = reportService.reportToURL(report, schedulerJob.getReportRuntime(), author, key); if (result == null) { fileName = ""; url = ReportConstants.ETL_FORMAT; } else { fileName = result[0]; url = result[1]; } } } catch (ReportEngineException e) { error = true; message = e.getMessage(); LOG.error(message, e); e.printStackTrace(); } catch (FormatNotSupportedException e) { error = true; message = e.getMessage(); LOG.error(message, e); e.printStackTrace(); } catch (NoDataFoundException e) { error = false; message = bundle.getString("ActionContributor.Run.nodata"); } catch (InterruptedException e) { error = true; message = bundle.getString("ActionContributor.Run.interrupted"); } catch (Throwable t) { error = true; if (t instanceof OutOfMemoryError) { message = bundle.getString("ActionContributor.Run.toomany"); } else { message = t.getMessage(); } LOG.error(message, t); t.printStackTrace(); } finally { ConnectionUtil.closeConnection(connection); if (RunReportHistory.USER.equals(runnerType)) { String eventMessage = error ? bundle.getString("ActionContributor.Run.error") : message; String dynamicUrl = ""; if (!error) { String fName = url.substring(url.lastIndexOf("/") + 1); dynamicUrl = reportService.getReportURL(fName); } ReportResultEvent event = new ReportResultEvent(creator, report.getName(), dynamicUrl, eventMessage); reportService.notifyReportListener(event); integrationPost(storageService.getSettings(), event); } } List<Distributor> distributors = new ArrayList<Distributor>(); DistributionContext distributionContext = new DistributionContext(); distributionContext.setSecurityService(securityService); distributionContext.setStorageService(storageService); distributionContext.setDataSource(report.getDataSource()); distributionContext.setError(error); distributionContext.setMailFrom(mailFrom); distributionContext.setMailSender(mailSender); distributionContext.setMessage(message); distributionContext.setReportsPath(reportsPath); distributionContext.setUrl(url); distributionContext.setReportName(report.getName()); distributionContext.setParameterValues(schedulerJob.getReportRuntime().getHistoryParametersValues()); // contains also the dynamic values if (batchMailMap != null) { distributionContext.setBatchMailMap(batchMailMap); } if (batchValue != null) { distributionContext.setBatchValue(batchValue.getId()); } if (fileName != null) { int errors = 0; File exportedFile = new File(reportsPath, fileName); for (Destination destination : destinations) { Distributor distributor = DistributorFactory.getDistributor(destination.getType()); distributors.add(distributor); try { distributor.distribute(exportedFile, destination, distributionContext); } catch (DistributionException e) { LOG.error(e.getMessage(), e); String s = bundle.getString("ActionContributor.Run.distributionFailed"); String failedMessage = MessageFormat.format(s, destination.getName()); error = true; if (errors == 0) { message = failedMessage + " : " + e.getMessage(); } else { message += "\r\n" + failedMessage + " : " + e.getMessage(); } errors++; } } } else { if (!report.isAlarmType() && !report.isIndicatorType() && !report.isDisplayType()) { // if 'No data' the email is not sent // send error through mail if (error) { for (Destination destination : destinations) { if (DestinationType.SMTP.toString().equals(destination.getType())) { Distributor distributor = DistributorFactory.getDistributor(destination.getType()); distributors.add(distributor); try { distributor.distribute(null, destination, distributionContext); } catch (DistributionException e) { String s = bundle.getString("ActionContributor.Run.distributionFailed"); String failedMessage = MessageFormat.format(s, destination.getName()); error = true; message += "\r\n" + failedMessage + " : " + e.getMessage(); } } } } } } // save history RunReportHistory runHistory = new RunReportHistory(); String name = UUID.randomUUID().toString(); runHistory.setName(name); String path = null; try { if (!StorageUtil.isVersion(report)) { path = storageService.getEntityById(report.getId()).getPath(); } else { String versionId = StorageUtil.getVersionableId(report); path = storageService.getEntityById(versionId).getPath(); } } catch (Exception e) { e.printStackTrace(); LOG.error(e.getMessage(), e); } path += StorageConstants.PATH_SEPARATOR + "runHistory" + StorageConstants.PATH_SEPARATOR + name; runHistory.setPath(path); if (LOG.isDebugEnabled()) { LOG.debug("Create run history '" + runHistory.getPath() + "'"); } runHistory.setRunnerId(runnerId); runHistory.setRunnerType(runnerType); runHistory.setStartDate(context.getScheduledFireTime()); runHistory.setEndDate(new Date()); runHistory.setDuration(Seconds.secondsBetween(new DateTime(runHistory.getStartDate()), new DateTime(runHistory.getEndDate())).getSeconds()); runHistory.setError(error); runHistory.setMessage(message); runHistory.setUrl(url); runHistory.setParametersValues(schedulerJob.getReportRuntime().getHistoryParametersValues(), schedulerJob.getReportRuntime().getHistoryParametersDisplayNames()); // audit report runs auditEvent.setDate(runHistory.getStartDate()); auditEvent.getContext().put("DURATION", runHistory.getDuration()); if (error) { auditEvent.setErrorMessage(message); } auditor.logEvent(auditEvent); if (!report.isAlarmType() && !report.isIndicatorType() && !report.isIndicatorType()) { try { storageService.addEntity(runHistory); } catch (Exception e) { e.printStackTrace(); LOG.error(e.getMessage(), e); } // after distribute // for send mail distributor : update acl for run report history for (Distributor distributor : distributors) { distributor.afterDistribute(runHistory, distributionContext); } // create full permissions for the creator if (creator != null) { try { securityService.grantUser(path, creator, PermissionUtil.getFullPermissions(), false); } catch (Exception e) { e.printStackTrace(); LOG.error(e.getMessage(), e); } } } // security breach? (reports with data filtered by user) // create permissions for those who have permission on report // if (granted != null) { // for (AclEntry aclEntry : granted) { // try { // if (aclEntry.getType() == AclEntry.USER_TYPE) { // securityService.grantUser(path, aclEntry.getName(), aclEntry.getPermissions(), false); // } else if (aclEntry.getType() == AclEntry.GROUP_TYPE) { // securityService.grantGroup(path, aclEntry.getName(), aclEntry.getPermissions(), false); // } // } catch (Exception e) { // e.printStackTrace(); // LOG.error(e.getMessage(), e); // } // } // } } private List<SmtpAlertDestination> getAlertDestinations(List<Destination> destinations) { List<SmtpAlertDestination> alertDestinations = new ArrayList<SmtpAlertDestination>(); for (Destination destination : destinations) { if (DestinationType.ALERT.toString().equals(destination.getType())) { alertDestinations.add((SmtpAlertDestination)destination); } } return alertDestinations; } private void integrationPost(Settings settings, ReportResultEvent event) { String notifyUrl = settings.getIntegration().getNotifyUrl(); if ((notifyUrl == null) || notifyUrl.equals("")) { return; } DefaultHttpClient httpclient = new DefaultHttpClient(); try { HttpPost httpPost = new HttpPost(notifyUrl); List <NameValuePair> nvps = new ArrayList <NameValuePair>(); nvps.add(new BasicNameValuePair("report", event.getReportName())); nvps.add(new BasicNameValuePair("message", event.getResultMessage())); nvps.add(new BasicNameValuePair("url", event.getReportUrl())); httpPost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8)); // JSONObject jsonObj = new JSONObject(); // jsonObj.put("report", event.getReportName()); // jsonObj.put("message", event.getResultMessage()); // jsonObj.put("url", event.getReportUrl()); // StringEntity entity = new StringEntity(jsonObj.toString(), HTTP.UTF_8); // entity.setContentType("application/json"); httpclient.execute(httpPost); } catch (Exception e) { e.printStackTrace(); LOG.error(e.getMessage(), e); } finally { httpclient.getConnectionManager().shutdown(); } } }