Java tutorial
/* * The contents of this file are subject to the Mozilla Public License * Version 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * The Original Code is Ziptie Client Framework. * * The Initial Developer of the Original Code is AlterPoint. * Portions created by AlterPoint are Copyright (C) 2006, * AlterPoint, Inc. All Rights Reserved. * * Contributor(s): */ package org.xerela.server.birt; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Arrays; import java.util.List; import java.util.Locale; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import org.apache.commons.mail.Email; import org.apache.commons.mail.EmailAttachment; import org.apache.commons.mail.EmailException; import org.apache.commons.mail.HtmlEmail; import org.apache.commons.mail.MultiPartEmail; import org.apache.log4j.Logger; import org.eclipse.birt.report.engine.api.EngineException; import org.eclipse.birt.report.engine.api.IReportEngine; import org.eclipse.birt.report.engine.api.IReportRunnable; import org.eclipse.birt.report.engine.api.IRunTask; import org.hibernate.classic.Session; import org.quartz.InterruptableJob; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.UnableToInterruptJobException; import org.xerela.provider.devices.DeviceResolutionElf; import org.xerela.provider.devices.ZDeviceLite; import org.xerela.provider.scheduler.ExecutionData; import org.xerela.provider.tools.PluginExecRecord; import org.xerela.server.birt.internal.BirtActivator; import org.xerela.zap.jta.TransactionElf; /** * ReportJob * * The ReportJob supports the following parameters in the JobData: * * ipResolutionScheme * ipResolutionData * * Note that an individual report may choose to ignore these parameters entirely. * * (Optional) Used for emailing of report after generation: * * report.email.format - if sending mail this is required * report.email.to - if sending mail this is required. semi-colon separated list of email addresses. * report.email.cc - optional. semi-colon separated list of email addresses. * report.email.link - either this must be true, the next attribute must be true, or both can be true * report.email.attachment - either this must be true, the previous attribute must be true, or both can be true */ public class ReportJob implements InterruptableJob { private static final Logger LOGGER = Logger.getLogger(ReportJob.class); private static final String REPORT_EMAIL_FORMAT = "report.email.format"; //$NON-NLS-1$ private static final String REPORT_EMAIL_TO = "report.email.to"; //$NON-NLS-1$ private static final String REPORT_EMAIL_CC = "report.email.cc"; //$NON-NLS-1$ private static final String REPORT_EMAIL_LINK = "report.email.link"; //$NON-NLS-1$ private static final String REPORT_EMAIL_ATTACHMENT = "report.email.attachment"; //$NON-NLS-1$ private static final String REPORT_TITLE = "tool"; //$NON-NLS-1$ private static final String MAIL_HOST_PROP = "mail.host"; //$NON-NLS-1$ private static final String MAIL_FROM_PROP = "mail.from"; //$NON-NLS-1$ private static final String MAIL_FROM_NAME_PROP = "mail.from.name"; //$NON-NLS-1$ private static final String MAIL_AUTH_USER_PROP = "mail.auth.user"; //$NON-NLS-1$ private static final String MAIL_AUTH_PASSWORD_PROP = "mail.auth.password"; //$NON-NLS-1$ private JobDataMap mergedJobDataMap; private volatile IRunTask runTask; private ExecutionData executionData; private String reportTitle; /** {@inheritDoc} */ public void execute(JobExecutionContext executionContext) throws JobExecutionException { mergedJobDataMap = executionContext.getMergedJobDataMap(); reportTitle = mergedJobDataMap.getString(REPORT_TITLE); executionData = (ExecutionData) executionContext.get(ExecutionData.class); JobDetail jobDetail = executionContext.getJobDetail(); LOGGER.info(Messages.bind(Messages.ReportJob_startingReportJob, reportTitle, jobDetail.getGroup() + '.' + jobDetail.getName())); ReportPluginManager reportPluginManager = BirtActivator.getReportPluginManager(); IReportRunnable reportDesign = reportPluginManager.getReportByTitle(reportTitle); if (reportDesign == null) { LOGGER.error(Messages.bind(Messages.ReportJob_reportDefinitionNotFound, reportTitle)); return; } List<ZDeviceLite> devices = null; File tmpFile = null; try { String ipData = mergedJobDataMap.getString("ipResolutionData"); //$NON-NLS-1$ String ipScheme = mergedJobDataMap.getString("ipResolutionScheme"); //$NON-NLS-1$ if (ipData != null && ipScheme != null) { devices = DeviceResolutionElf.resolveDevices(ipScheme, ipData); persistTempDevices(devices); } TransactionElf.beginOrJoinTransaction(); tmpFile = runReport(reportDesign, devices); TransactionElf.commit(); TransactionElf.beginOrJoinTransaction(); createExecRecord(); persistReport(tmpFile, executionData.getId()); TransactionElf.commit(); emailReport(tmpFile, executionContext); } catch (Exception e) { TransactionElf.rollback(); throw new JobExecutionException(e); } finally { if (tmpFile != null) { tmpFile.delete(); } if (devices != null) { deleteTempDevices(); } LOGGER.info(Messages.bind(Messages.ReportJob_reportJobFinished, jobDetail.getGroup() + '.' + jobDetail.getName())); } } /** {@inheritDoc} */ public void interrupt() throws UnableToInterruptJobException { if (runTask != null) { runTask.cancel(); } } /** * Run the specified report design. * * @param reportDesign the report design to run * @param devices a set of devices or <code>null</code> * @return the intermediate BIRT output file * @throws IOException thrown if there is an error writing the report to the file system * @throws EngineException thrown if there is an internal error in BIRT running the report */ private File runReport(IReportRunnable reportDesign, List<ZDeviceLite> devices) throws IOException, EngineException { IReportEngine reportEngine = BirtActivator.getReportPluginManager().getReportEngine(reportTitle); try { runTask = reportEngine.createRunTask(reportDesign); // Set the execution id as a parameter to the report runTask.setParameterValue("execution_id", new Integer(executionData.getId())); //$NON-NLS-1$ // Set the device list as a parameter to the report if (devices != null) { runTask.setParameterValue("zdevicelites", devices); //$NON-NLS-1$ } // Set the locale of the report String locale = mergedJobDataMap.getString("locale"); //$NON-NLS-1$ if (locale != null) { runTask.setLocale(new Locale(locale)); } // Get interactive inputs and set them as parameter values in the run task for (String key : mergedJobDataMap.getKeys()) { if (key.startsWith("input.")) //$NON-NLS-1$ { runTask.setParameterValue(key, mergedJobDataMap.getString(key)); } } File tmpFile = File.createTempFile("birt", ".rptdocument"); //$NON-NLS-1$ //$NON-NLS-2$ tmpFile.deleteOnExit(); // best effort delete, only works in clean jvm shutdown runTask.setErrorHandlingOption(IRunTask.CANCEL_ON_ERROR); runTask.run(tmpFile.getAbsolutePath()); runTask.close(); runTask = null; return tmpFile; } finally { reportEngine.destroy(); } } /** * Create a record of the execution. * * @param toolProperties */ private void createExecRecord() { PluginExecRecord execRecord = new PluginExecRecord(); execRecord.setPluginName(reportTitle); execRecord.setOutputFormat("birt(html|pdf)"); //$NON-NLS-1$ execRecord.setExecutionData(executionData); Session session = BirtActivator.getSessionFactory().getCurrentSession(); session.save(execRecord); } /** * Stream the binary intermediate output format file into a BLOB in the database. * * @param tmpFile intermediate output format file * @throws SQLException thrown if there is an error accessing the database * @throws IOException thrown if there is an error reading the file */ private void persistReport(File tmpFile, int executionId) throws SQLException, IOException { Connection connection = BirtActivator.getDataSource().getConnection(); try { PreparedStatement stmt = connection .prepareStatement("INSERT INTO birt_report (execution_id, details) VALUES (?,?)"); //$NON-NLS-1$ BufferedInputStream is = new BufferedInputStream(new FileInputStream(tmpFile)); stmt.setInt(1, executionId); stmt.setBinaryStream(2, is, (int) tmpFile.length()); int rowsUpdated = stmt.executeUpdate(); if (rowsUpdated == 0) { LOGGER.warn(Messages.ReportJob_reportPersistFailure); } stmt.close(); is.close(); } finally { connection.close(); } } private void persistTempDevices(List<ZDeviceLite> devices) throws SQLException { if (devices == null || devices.size() == 0) { return; } TransactionElf.beginOrJoinTransaction(); Connection connection = BirtActivator.getDataSource().getConnection(); PreparedStatement stmt = null; try { stmt = connection .prepareStatement("INSERT INTO birt_resolved_devices(device_id, execution_id) VALUES (?, ?)"); //$NON-NLS-1$ for (ZDeviceLite device : devices) { stmt.setInt(1, device.getDeviceId()); stmt.setInt(2, executionData.getId()); stmt.addBatch(); } int[] batchRc = stmt.executeBatch(); if (batchRc == null || batchRc[0] <= 0) { LOGGER.warn("Batch insert failed for temporary device table."); //$NON-NLS-1$ } } finally { if (stmt != null) { stmt.close(); } connection.close(); TransactionElf.commit(); } } /** * @throws JobExecutionException */ private void deleteTempDevices() throws JobExecutionException { TransactionElf.beginOrJoinTransaction(); Connection connection = null; try { connection = BirtActivator.getDataSource().getConnection(); PreparedStatement stmt = connection .prepareStatement("DELETE FROM birt_resolved_devices WHERE execution_id = ?"); //$NON-NLS-1$ stmt.setInt(1, executionData.getId()); stmt.execute(); stmt.close(); } catch (SQLException e) { TransactionElf.rollback(); throw new JobExecutionException("Exception deleting entries in temporary device table.", e); //$NON-NLS-1$ } finally { TransactionElf.commit(); try { connection.close(); } catch (SQLException e) { // nothing } } } /** * Email the report if the JobData defines the required parameters, * otherwise this method just returns without doing anything. * * @param intermediate the BIRT intermediate format file * @param executionContext the Quartz JobExecutionContext of this job */ @SuppressWarnings("nls") private void emailReport(File intermediate, JobExecutionContext executionContext) { JobDataMap jobData = executionContext.getMergedJobDataMap(); boolean emailAttachment = jobData.containsKey(REPORT_EMAIL_ATTACHMENT) ? jobData.getBooleanValue(REPORT_EMAIL_ATTACHMENT) : false; boolean emailLink = jobData.containsKey(REPORT_EMAIL_LINK) ? jobData.getBooleanValue(REPORT_EMAIL_LINK) : false; String emailTo = jobData.getString(REPORT_EMAIL_TO); String emailCc = jobData.getString(REPORT_EMAIL_CC); String reportFormat = jobData.getString(REPORT_EMAIL_FORMAT); if (!validateEmailProperties(emailAttachment, emailLink, emailTo, reportFormat)) { return; } try { Email email = null; if (reportFormat.equals("pdf")) { email = new MultiPartEmail(); } else if (reportFormat.equals("html")) { email = new HtmlEmail(); } setupEmail(executionContext, emailTo, emailCc, email); // Create the attachment if (emailAttachment) { final File render = RenderElf.render(intermediate, executionData.getId(), reportFormat); if (reportFormat.equals("pdf")) { EmailAttachment attachment = new EmailAttachment(); attachment.setPath(render.getAbsolutePath()); attachment.setDisposition(EmailAttachment.ATTACHMENT); attachment.setDescription(reportTitle); attachment.setName(String.format("%s.%s", reportTitle, reportFormat)); //$NON-NLS-1$ ((MultiPartEmail) email).attach(attachment); } else if (reportFormat.equals("html")) { HtmlEmail htmlEmail = (HtmlEmail) email; String html = stringFromFile(render); final String pathStub = render.getName().replaceFirst("\\.[a-z]+$", ""); //$NON-NLS-1$ //$NON-NLS-2$ File parentDir = new File(render.getParent()); File[] listFiles = parentDir.listFiles(new FileFilter() { public boolean accept(File pathname) { return pathname.getName().startsWith(pathStub) && !pathname.getName().endsWith("html"); //$NON-NLS-1$ } }); for (File image : listFiles) { String cid = htmlEmail.embed(image); String regex = "src=.+" + image.getName(); html = html.replaceAll(regex, "src=\"cid:" + cid); } htmlEmail.setHtmlMsg(html); } } if (emailLink) { } email.send(); } catch (AddressException ae) { LOGGER.error(Messages.bind(Messages.ReportJob_badAddresses, reportTitle), ae); } catch (EmailException ee) { LOGGER.error(Messages.bind(Messages.ReportJob_errorSending, reportTitle), ee); } catch (EngineException ee) { LOGGER.error(Messages.bind(Messages.ReportJob_errorSending, reportTitle), ee); } catch (IOException ie) { LOGGER.error(Messages.bind(Messages.ReportJob_errorSending, reportTitle), ie); } } private String stringFromFile(File render) throws IOException { BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(render)); StringBuilder sb = new StringBuilder(); char[] cbuf = new char[4096]; while (true) { int read = reader.read(cbuf); if (read <= 0) { break; } sb.append(cbuf, 0, read); } return sb.toString(); } finally { if (reader != null) { reader.close(); } } } private void setupEmail(JobExecutionContext executionContext, String emailTo, String emailCc, Email email) throws AddressException, EmailException { InternetAddress[] toAddrs = InternetAddress.parse(emailTo); email.setTo(Arrays.asList(toAddrs)); if (emailCc != null && emailCc.trim().length() > 0) { InternetAddress[] ccAddrs = InternetAddress.parse(emailCc); email.setCc(Arrays.asList(ccAddrs)); } email.setCharset("utf-8"); //$NON-NLS-1$ email.setHostName(System.getProperty(MAIL_HOST_PROP, "mail")); //$NON-NLS-1$ String authUser = System.getProperty(MAIL_AUTH_USER_PROP); if (authUser != null) { email.setAuthentication(authUser, System.getProperty(MAIL_AUTH_PASSWORD_PROP)); } email.setFrom(System.getProperty(MAIL_FROM_PROP), System.getProperty(MAIL_FROM_NAME_PROP)); email.setSubject( Messages.bind(Messages.ReportJob_emailSubject, executionContext.getJobDetail().getFullName())); email.addHeader("X-Mailer", "Xerela Mailer"); //$NON-NLS-1$ //$NON-NLS-2$ email.setDebug(Boolean.getBoolean("org.xerela.mail.debug")); //$NON-NLS-1$ } /** * Validate the email properties specified in the job data. * * @param emailAttachment * @param emailLink * @param emailTo * @param reportFormat * @return true if the parameters are valid for generating an email, false otherwise */ private boolean validateEmailProperties(Boolean emailAttachment, boolean emailLink, String emailTo, String reportFormat) { if (!emailAttachment && !emailLink) { return false; } if (emailTo == null) { LOGGER.error(Messages.bind(Messages.ReportJob_emptyAddresses, reportTitle)); return false; } if (reportFormat == null) { LOGGER.error(Messages.bind(Messages.ReportJob_noFormat, reportTitle)); return false; } return true; } }