r2927 - in branches/pollen-1.2.3-1.2.x/pollen-ui/src: main/java/org/chorem/pollen/ui/pages/poll main/java/org/chorem/pollen/ui/services main/java/org/chorem/pollen/ui/utils main/resources test/java/org/chorem/pollen/ui/utils
Author: echatellier Date: 2010-03-10 19:08:53 +0100 (Wed, 10 Mar 2010) New Revision: 2927 Log: #136 : envoi de mail en masse (temporis?\195?\169e, avec reprise au redemarrage) Added: branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/java/org/chorem/pollen/ui/utils/SendMail.java branches/pollen-1.2.3-1.2.x/pollen-ui/src/test/java/org/chorem/pollen/ui/utils/SendMailTest.java Modified: branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/java/org/chorem/pollen/ui/pages/poll/PollCreation.java branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/java/org/chorem/pollen/ui/services/AppModule.java branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/java/org/chorem/pollen/ui/services/Configuration.java branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/resources/pollen.properties Modified: branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/java/org/chorem/pollen/ui/pages/poll/PollCreation.java =================================================================== --- branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/java/org/chorem/pollen/ui/pages/poll/PollCreation.java 2010-03-10 12:54:05 UTC (rev 2926) +++ branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/java/org/chorem/pollen/ui/pages/poll/PollCreation.java 2010-03-10 18:08:53 UTC (rev 2927) @@ -17,6 +17,7 @@ package org.chorem.pollen.ui.pages.poll; import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -47,7 +48,6 @@ import org.apache.tapestry5.ioc.annotations.Symbol; import org.apache.tapestry5.ioc.services.PropertyAccess; import org.apache.tapestry5.json.JSONObject; -import org.apache.tapestry5.services.Request; import org.apache.tapestry5.upload.services.UploadSymbols; import org.chorem.pollen.business.business.PreventRuleManager; import org.chorem.pollen.business.dto.ChoiceDTO; @@ -75,6 +75,7 @@ import org.chorem.pollen.ui.services.Configuration; import org.chorem.pollen.ui.utils.FeedUtil; import org.chorem.pollen.ui.utils.ImageUtil; +import org.chorem.pollen.ui.utils.SendMail; import org.chorem.pollen.ui.utils.UnitConverter; import org.slf4j.Logger; @@ -355,6 +356,9 @@ private ServicePollAccount servicePollAccount; @Inject private ServiceList serviceList; + + @Inject + private SendMail sendMailService; /** * Méthode appelée lorsqu'on souhaite accéder à l'étape suivante de la @@ -881,11 +885,13 @@ // Mails aux votants for (VotingListDTO list : poll.getVotingListDTOs()) { + + List<Map<String, String>> mailList = new ArrayList<Map<String,String>>(); for (PollAccountDTO account : list.getPollAccountDTOs()) { if (account.getEmail() != null) { String accountVoteURL = voteURL + ":" + account.getAccountId(); - data.put("to", account.getEmail()); + /*data.put("to", account.getEmail()); data.put("title", messages.format("votingEmail_subject", poll.getTitle())); data.put("msg", messages.format("votingEmail_msg", poll @@ -894,9 +900,25 @@ // FIXME call directly MailUtil.sendMail() // skip fill map, get from map... - PreventRuleManager.emailAction(data); + PreventRuleManager.emailAction(data);*/ + + Map<String, String> mailData = new HashMap<String, String>(); + mailData.put("receiver", account.getEmail()); + mailData.put("subject", messages.format("votingEmail_subject",poll.getTitle())); + mailData.put("body", messages.format("votingEmail_msg", poll.getTitle(), account.getVotingId(),accountVoteURL)); + mailList.add(mailData); } } + + // send mail preparation + try { + sendMailService.prepareMails(poll.getId(), mailList); + sendMailService.wakeUp(); + } catch (IOException ex) { + if (log.isErrorEnabled()) { + log.error("Can't prepare send mail on disk, mail won't be send !!!", ex); + } + } } } Modified: branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/java/org/chorem/pollen/ui/services/AppModule.java =================================================================== --- branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/java/org/chorem/pollen/ui/services/AppModule.java 2010-03-10 12:54:05 UTC (rev 2926) +++ branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/java/org/chorem/pollen/ui/services/AppModule.java 2010-03-10 18:08:53 UTC (rev 2927) @@ -37,6 +37,7 @@ import org.chorem.pollen.business.services.ServiceResults; import org.chorem.pollen.business.services.ServiceUser; import org.chorem.pollen.business.services.ServiceVote; +import org.chorem.pollen.ui.utils.SendMail; import org.slf4j.Logger; /** @@ -164,7 +165,32 @@ return new BackgroundWorkerImpl(messages, configuration, servicePoll); } + /* + * Contribution au démarrage de services tapestry. + * + * Ajout de SendMail + * + * @param configuration configuration tapestry + * @param conf configuration pollen + * + public static void contributeRegistryStartup(OrderedConfiguration<Runnable> configuration, Configuration conf) { + configuration.add("SendMail", new SendMail(conf)); + }*/ + /** + * Init send mail service. + * + * @param conf + * @return + */ + @EagerLoad + public SendMail buildSendMail(Configuration conf) { + SendMail res = new SendMail(conf); + res.start(); + return res; + } + + /** * Make configuration from a Properties file available as symbols. */ public PropertiesFileSymbolProvider buildConfigPropertiesFileSymbolProvider( Modified: branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/java/org/chorem/pollen/ui/services/Configuration.java =================================================================== --- branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/java/org/chorem/pollen/ui/services/Configuration.java 2010-03-10 12:54:05 UTC (rev 2926) +++ branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/java/org/chorem/pollen/ui/services/Configuration.java 2010-03-10 18:08:53 UTC (rev 2927) @@ -33,6 +33,7 @@ public static final String PROP_CONTACT_EMAIL = "adminEmail"; public static final String PROP_NB_VOTES_PER_PAGE = "pollen.ui.nbVotesPerPage"; + public static final String PROP_EMAIL_DIRECTORY = "pollen.emails.directory"; /** * Retourne les propriétés. Added: branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/java/org/chorem/pollen/ui/utils/SendMail.java =================================================================== --- branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/java/org/chorem/pollen/ui/utils/SendMail.java (rev 0) +++ branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/java/org/chorem/pollen/ui/utils/SendMail.java 2010-03-10 18:08:53 UTC (rev 2927) @@ -0,0 +1,249 @@ +/* *##% Pollen + * Copyright (C) 2009 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. ##%*/ + +package org.chorem.pollen.ui.utils; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.commons.io.FileUtils; +import org.chorem.pollen.business.utils.MailUtil; +import org.chorem.pollen.ui.services.Configuration; +import org.h2.util.IOUtils; +import org.nuiton.util.FileUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import au.com.bytecode.opencsv.CSVReader; +import au.com.bytecode.opencsv.CSVWriter; + +/** + * Mass mail management class. + * + * This class store email to send in a csv file, and send it (one per second) in + * a second time. + * This class can restart to send mail at application restart. + * + * For a mass mail sending to start, following files must be present: + * <ul> + * <li>xxx.emails : CSV file ("email", "subject", "body")</li> + * <li>xxx.index : next index to manage ( inited at 0)</li> + * </ul> + * + * @author chatellier + * @version $Revision$ + * + * Last update : $Date$ + * By : $Author$ + */ +public class SendMail extends Thread { + + /** logger. */ + private static final Logger log = LoggerFactory.getLogger(SendMail.class); + + public static final String EXTENSION_MAIL = ".mail"; + public static final String EXTENSION_INDEX = ".index"; + + /** Pollen configuration. */ + protected Configuration configuration; + + /** Mail storage directory. */ + protected File mailStorageDirectory; + + public SendMail(Configuration configuration) { + + this.configuration = configuration; + + // get email directory in configuration + // create it if not exists + String filename = configuration.getProperty(Configuration.PROP_EMAIL_DIRECTORY); + mailStorageDirectory = new File(filename); + if (!mailStorageDirectory.exists()) { + if (mailStorageDirectory.mkdirs()) { + if (log.isDebugEnabled()) { + log.debug("Email storage directory created in : " + mailStorageDirectory.getAbsolutePath()); + } + } + } + } + + /* + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + + // try to find existing files + while (true) { + try { + sendAllMails(); + + // bloque thread until next notify + sleep(); + } + catch (Exception ex) { + if (log.isErrorEnabled()) { + log.error("Error during SendMail main loop", ex); + } + } + } + } + + protected synchronized void sleep() throws InterruptedException { + wait(); + } + + public synchronized void wakeUp() { + notifyAll(); + } + + /** + * Look for all xx.index file, and restart mail sending on + * non ending mass mail sending. + * + * After execution, delete mail and index file. + * @throws IOException + */ + protected void sendAllMails() throws IOException { + + // don't do for, allways take the first found + // a new one can be created when managing one other + List<File> indexFiles = null; + do { + + // filter is java valid : .*\\.index + indexFiles = FileUtil.find(mailStorageDirectory, ".*\\" + EXTENSION_INDEX, false); + + if (!indexFiles.isEmpty()) { + File indexFile = indexFiles.get(0); + // convert index content to int + String indexContent = FileUtils.readFileToString(indexFile, "UTF-8"); + int index = Integer.parseInt(indexContent); + + // get mail content file + File mailFile = new File(indexFile.getAbsolutePath().replaceAll(EXTENSION_INDEX + "$", EXTENSION_MAIL)); + + if (log.isDebugEnabled()) { + log.debug("Managing mail file : " + mailFile + " (from index " + indexContent + ")"); + } + + Reader indexFileReader = new BufferedReader(new FileReader(mailFile)); + CSVReader cvsReader = new CSVReader(indexFileReader); + + int currentIndex = 0; + String[] currentLine = cvsReader.readNext(); + while (currentLine != null) { + String receiver = currentLine[0]; + String subject = currentLine[1]; + String body = currentLine[2]; + + log.debug("Props = " + configuration.getConf()); + + // index contains next index to treat so == is ok + if (currentIndex >= index) { + MailUtil.sendMail(configuration.getProperty("email_host"), + Integer.parseInt(configuration.getProperty("email_port")), + configuration.getProperty("email_from"), + receiver, subject, body); + + // index contains next index to treat + FileUtils.writeStringToFile(indexFile, String.valueOf(currentIndex + 1)); + + // wait 2 secondes between each mail to not + // load smtp server + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + if (log.isErrorEnabled()) { + log.error("Can't wait between mail", ex); + } + } + } + else { + if (log.isDebugEnabled()) { + log.debug("Mail to " + receiver + " already sent in a previous execution, skip."); + } + } + + currentIndex++; + currentLine = cvsReader.readNext(); + } + + // delete woth index and mail file + mailFile.delete(); + indexFile.delete(); + } + else { + if (log.isInfoEnabled()) { + log.info("No more index mail index file found, go to sleep a while :)"); + } + } + } while (!indexFiles.isEmpty()); + } + + /** + * Prepare mail list. + * + * TODO : improve configuration reading + * + * @param id + * @param mailData + * @throws IOException + */ + public void prepareMails(String id, List<Map<String, String>> mailData) throws IOException { + + Writer fileWriter = null; + CSVWriter cvsWriter = null; + try { + + // write CSV datas + File emailFile = new File(mailStorageDirectory, id + EXTENSION_MAIL); + fileWriter = new OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(emailFile))); + cvsWriter = new CSVWriter(fileWriter); + + for (Map<String, String> singleMailData : mailData) { + String[] nextLine = new String[] { + singleMailData.get("receiver"), + singleMailData.get("subject"), + singleMailData.get("body") + }; + cvsWriter.writeNext(nextLine); + } + + // write index (default to 0) + File indexFile = new File(mailStorageDirectory, id + EXTENSION_INDEX); + FileUtil.writeString(indexFile, "0"); + } + finally { + if (cvsWriter != null) { + cvsWriter.close(); + } + IOUtils.closeSilently(fileWriter); + } + + } +} Property changes on: branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/java/org/chorem/pollen/ui/utils/SendMail.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Modified: branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/resources/pollen.properties =================================================================== --- branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/resources/pollen.properties 2010-03-10 12:54:05 UTC (rev 2926) +++ branches/pollen-1.2.3-1.2.x/pollen-ui/src/main/resources/pollen.properties 2010-03-10 18:08:53 UTC (rev 2927) @@ -45,6 +45,9 @@ ## R\u00C3\u00A9pertoire des flux de syndication (Atom) feedDir=.pollen/feeds +## Repertoire de stockage des mails a envoyer +pollen.emails.directory=.pollen/emails + ## Nombre de votes affiches sur une page pollen.ui.nbVotesPerPage=100 Added: branches/pollen-1.2.3-1.2.x/pollen-ui/src/test/java/org/chorem/pollen/ui/utils/SendMailTest.java =================================================================== --- branches/pollen-1.2.3-1.2.x/pollen-ui/src/test/java/org/chorem/pollen/ui/utils/SendMailTest.java (rev 0) +++ branches/pollen-1.2.3-1.2.x/pollen-ui/src/test/java/org/chorem/pollen/ui/utils/SendMailTest.java 2010-03-10 18:08:53 UTC (rev 2927) @@ -0,0 +1,92 @@ +/* *##% Pollen + * Copyright (C) 2010 CodeLutin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. ##%*/ + +package org.chorem.pollen.ui.utils; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import junit.framework.Assert; + +import org.chorem.pollen.ui.services.Configuration; +import org.chorem.pollen.ui.services.ConfigurationImpl; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Class for send mail storage class. + * + * @author chatellier + * @version $Revision$ + * + * Last update : $Date$ + * By : $Author$ + */ +public class SendMailTest { + + protected static Configuration configuration; + protected static SendMail sendMail; + + @BeforeClass + public static void init() throws InterruptedException { + + Properties props = new Properties(); + props.setProperty(Configuration.PROP_EMAIL_DIRECTORY, "target" + File.separator + "massmail"); + // FIXME replace literal strings + props.setProperty("email_host", "smtp"); + props.setProperty("email_port", "25"); + props.setProperty("email_from", "pollenreminder@codelutin.com"); + + configuration = new ConfigurationImpl(props); + sendMail = new SendMail(configuration); + sendMail.start(); + Thread.sleep(3000); + } + + @Test + public void testAddMail() throws IOException, InterruptedException { + List<Map<String, String>> mailList = new ArrayList<Map<String,String>>(); + + Map<String, String> mailMap = new HashMap<String, String>(); + mailMap.put("receiver", "chatellier+test@codelutin.com"); + mailMap.put("subject", "Test 18:00"); + mailMap.put("body", "Test body"); + + Map<String, String> mailMap2 = new HashMap<String, String>(); + mailMap2.put("receiver", "chorlet+test@codelutin.com"); + mailMap2.put("subject", "CR, CR CR !!! 18:00"); + mailMap2.put("body", "Des cr, encore des \"CRs\" !!!"); + + mailList.add(mailMap); + mailList.add(mailMap2); + + sendMail.prepareMails("test", mailList); + + Assert.assertTrue(new File(configuration.getProperty(Configuration.PROP_EMAIL_DIRECTORY), "test.mail").exists()); + Assert.assertTrue(new File(configuration.getProperty(Configuration.PROP_EMAIL_DIRECTORY), "test.mail").exists()); + sendMail.wakeUp(); + + // TODO , en 5 secondes, il aura le temps d'envoyer 2 mails ? + Thread.sleep(5000); + Assert.assertFalse(new File(configuration.getProperty(Configuration.PROP_EMAIL_DIRECTORY), "test.mail").exists()); + Assert.assertFalse(new File(configuration.getProperty(Configuration.PROP_EMAIL_DIRECTORY), "test.mail").exists()); + } +} Property changes on: branches/pollen-1.2.3-1.2.x/pollen-ui/src/test/java/org/chorem/pollen/ui/utils/SendMailTest.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL
participants (1)
-
echatellier@users.chorem.org