diff --git a/.gitignore b/.gitignore index 6dbb6f8..30a8cfc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,6 @@ /sma-server/target/ db/ sma-server.conf -sma-server.log +sma-server*.log .flattened-pom.xml .idea diff --git a/sma-client/src/test/java/fr/insalyon/creatis/sma/client/Constants.java b/sma-client/src/test/java/fr/insalyon/creatis/sma/client/Constants.java index 91eb91d..53a5b01 100644 --- a/sma-client/src/test/java/fr/insalyon/creatis/sma/client/Constants.java +++ b/sma-client/src/test/java/fr/insalyon/creatis/sma/client/Constants.java @@ -3,5 +3,5 @@ public final class Constants { // in millis - public static int MAX_WAIT_SPAM = 15000; + public static final int MAX_WAIT_SPAM_SEC = 15; } diff --git a/sma-client/src/test/java/fr/insalyon/creatis/sma/client/SMAClientAndServerTest.java b/sma-client/src/test/java/fr/insalyon/creatis/sma/client/SMAClientAndServerTest.java index b136f9a..d200bc0 100644 --- a/sma-client/src/test/java/fr/insalyon/creatis/sma/client/SMAClientAndServerTest.java +++ b/sma-client/src/test/java/fr/insalyon/creatis/sma/client/SMAClientAndServerTest.java @@ -19,8 +19,8 @@ import com.icegreen.greenmail.util.GreenMail; import com.icegreen.greenmail.util.ServerSetupTest; -import fr.insalyon.creatis.sma.server.Configuration; import fr.insalyon.creatis.sma.server.SmaServer; +import fr.insalyon.creatis.sma.server.utils.Configuration; import jakarta.mail.internet.MimeMessage; public class SMAClientAndServerTest { @@ -42,7 +42,7 @@ private void mockConfig(SmtpServer server) { when(configuration.getMailProtocol()).thenReturn(server.getProtocol()); when(configuration.getMailHost()).thenReturn(server.getBindTo()); when(configuration.getMailPort()).thenReturn(server.getPort()); - when(configuration.getMailSslTrust()).thenReturn(server.getBindTo()); + when(configuration.isMailSslTrust()).thenReturn(false); when(configuration.isMailAuth()).thenReturn(false); when(configuration.getMailUsername()).thenReturn(""); @@ -50,8 +50,8 @@ private void mockConfig(SmtpServer server) { when(configuration.getMailFrom()).thenReturn("test@test.com"); when(configuration.getMailFromName()).thenReturn("test"); - when(configuration.getMailMaxRuns()).thenReturn(5); - + when(configuration.getMailMaxRuns()).thenReturn(10); + when(configuration.getMailPort()).thenReturn(server.getPort()); Configuration.getInstance().setConfiguration(configuration); } @@ -79,10 +79,11 @@ public void spamMailClient() throws Exception { final String message = "je suis vraiment trop fort"; final String subject = "wow ce titre est incroyable"; final String username = "bliblou"; + final int nmails = 1000; String[] randomAddress; client = new SMAClient(InetAddress.getLocalHost(), configuration.getPort()); - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < nmails; i++) { randomAddress = generateRandomAddress(); client.sendEmail(subject, message, randomAddress, true, username); System.err.println("Sending mail to : " + randomAddress[0]); @@ -90,11 +91,11 @@ public void spamMailClient() throws Exception { before = Instant.now(); System.err.println("Waiting for mails to reach de server !"); - while (mailServer.getReceivedMessages().length != 1000) { - System.err.println("Waiting !"); + while (mailServer.getReceivedMessages().length != nmails) { + System.err.println("Waiting ! (" + mailServer.getReceivedMessages().length + " mails arrived)"); Thread.sleep(1000); after = Instant.now(); - assertFalse(Duration.between(before, after).toMillis() > Constants.MAX_WAIT_SPAM); + assertFalse(Duration.between(before, after).toSeconds() > Constants.MAX_WAIT_SPAM_SEC); } System.err.println("All mails sended !"); diff --git a/sma-common/pom.xml b/sma-common/pom.xml index 382d8b2..bd17f60 100644 --- a/sma-common/pom.xml +++ b/sma-common/pom.xml @@ -12,13 +12,4 @@ sma-common jar SMA-Common - - - - junit - junit - 4.10 - test - - diff --git a/sma-server/pom.xml b/sma-server/pom.xml index 457aaa6..bf250fe 100644 --- a/sma-server/pom.xml +++ b/sma-server/pom.xml @@ -66,7 +66,7 @@ com.h2database h2 - 1.4.177 + 2.3.232 diff --git a/sma-server/sma-conf.example b/sma-server/sma-conf.example index c6067b5..170b3a9 100644 --- a/sma-server/sma-conf.example +++ b/sma-server/sma-conf.example @@ -5,8 +5,8 @@ mail.transport.protocol = smtps mail.host = smtp.creatis.insa-lyon.fr mail.port = 1 -# if an host is trusted, then SSL certificate won't be checked! -mail.smtp.ssl.trust = localhost +# if true, then only mail.host will be trusted, SSL certificate won't be checked! +mail.smtp.ssl.trust = True mail.auth = True mail.username = b diff --git a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/Main.java b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/Main.java index 9389b38..8a714bc 100644 --- a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/Main.java +++ b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/Main.java @@ -32,10 +32,6 @@ */ package fr.insalyon.creatis.sma.server; -/** - * - * @author Rafael Ferreira da Silva - */ public class Main { public static void main(String[] args) { diff --git a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/SmaServer.java b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/SmaServer.java index 188bb24..d49d190 100644 --- a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/SmaServer.java +++ b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/SmaServer.java @@ -4,51 +4,80 @@ import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import fr.insalyon.creatis.sma.common.Communication; -import fr.insalyon.creatis.sma.server.execution.Executor; -import fr.insalyon.creatis.sma.server.execution.MessageCleanerPool; +import fr.insalyon.creatis.sma.server.business.MessagePoolBusiness; +import fr.insalyon.creatis.sma.server.dao.MessagePoolDAO; +import fr.insalyon.creatis.sma.server.dao.h2.MessagePoolData; +import fr.insalyon.creatis.sma.server.execution.ScheduledTasksCreator; +import fr.insalyon.creatis.sma.server.execution.executors.CommunicationExecutor; +import fr.insalyon.creatis.sma.server.utils.Configuration; +import fr.insalyon.creatis.sma.server.utils.Constants; -/** - * SmaServer - */ public class SmaServer extends Thread { - private static final Logger logger = Logger.getLogger(Main.class); + private static final Logger LOG = Logger.getLogger(Main.class); + private final ScheduledExecutorService tasksExecutor; + private final ExecutorService socketExecutor; + private final ExecutorService sendMessageExecutor; + private final Configuration config; + + private final MessagePoolDAO messagePoolDAO; + private final MessagePoolBusiness messagePoolBusiness; private boolean started = false; + public SmaServer() { + PropertyConfigurator.configure(Main.class.getClassLoader().getResource("smaLog4j.properties")); + + config = Configuration.getInstance(); + tasksExecutor = Executors.newSingleThreadScheduledExecutor(); + socketExecutor = Executors.newCachedThreadPool(); + sendMessageExecutor = Executors.newFixedThreadPool(config.getMailMaxRuns()); + + messagePoolDAO = new MessagePoolData(); + messagePoolBusiness = new MessagePoolBusiness(messagePoolDAO); + + schedule(); + } + public synchronized void waitToBeReady() throws InterruptedException { while (started == false) { Thread.sleep(1000); } } + + public final void schedule() { + ScheduledTasksCreator creator = new ScheduledTasksCreator(); + + tasksExecutor.scheduleWithFixedDelay( + creator.getPoolCleanerTask(messagePoolDAO), 0, Constants.CLEANER_POOL_SLEEP_HOURS, TimeUnit.HOURS); + tasksExecutor.scheduleWithFixedDelay( + creator.getMessagePoolTask(sendMessageExecutor, messagePoolDAO, messagePoolBusiness), 0, Constants.MESSAGE_POOL_SLEEP_SECONDS, TimeUnit.SECONDS); + } + @Override public void run() { - PropertyConfigurator.configure(Main.class.getClassLoader().getResource("smaLog4j.properties")); - Configuration.getInstance(); + LOG.info("Starting SMA Server on port " + config.getPort()); - logger.info("Starting SMA Server on port " + Configuration.getInstance().getPort()); - - // Pools - MessageCleanerPool.getInstance(); - - // Socket - try (ServerSocket serverSocket = new ServerSocket( - Configuration.getInstance().getPort(), 50, InetAddress.getByName("0.0.0.0"))) { - + try (ServerSocket serverSocket = new ServerSocket(config.getPort(), 50, InetAddress.getByName("0.0.0.0"))) { started = true; + while (true) { Socket socket = serverSocket.accept(); Communication communication = new Communication(socket); - new Executor(communication).start(); - } + socketExecutor.submit(new CommunicationExecutor(communication, messagePoolBusiness)); + } } catch (IOException ex) { - logger.error("Error processing a request ", ex); + LOG.error("Error processing a request ", ex); } } -} \ No newline at end of file +} diff --git a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/business/MessagePoolBusiness.java b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/business/MessagePoolBusiness.java index 9b13a69..a84b2ce 100644 --- a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/business/MessagePoolBusiness.java +++ b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/business/MessagePoolBusiness.java @@ -35,20 +35,9 @@ package fr.insalyon.creatis.sma.server.business; import fr.insalyon.creatis.sma.common.bean.MessageOperation; -import fr.insalyon.creatis.sma.server.Configuration; +import fr.insalyon.creatis.sma.common.bean.OperationStatus; import fr.insalyon.creatis.sma.server.dao.DAOException; -import fr.insalyon.creatis.sma.server.dao.DAOFactory; -import fr.insalyon.creatis.sma.server.execution.MessagePool; -import java.io.UnsupportedEncodingException; -import java.util.Date; -import java.util.Properties; -import jakarta.mail.Message; -import jakarta.mail.MessagingException; -import jakarta.mail.Session; -import jakarta.mail.Transport; -import jakarta.mail.internet.InternetAddress; -import jakarta.mail.internet.MimeMessage; -import org.apache.log4j.Logger; +import fr.insalyon.creatis.sma.server.dao.MessagePoolDAO; /** * @@ -56,83 +45,28 @@ */ public class MessagePoolBusiness { - private final static Logger logger = Logger.getLogger(MessagePoolBusiness.class); + private final MessagePoolDAO messagePoolDAO; - /** - * - * @param operation - * @throws BusinessException - */ - public void addOperation(MessageOperation operation) throws BusinessException { + public MessagePoolBusiness(MessagePoolDAO messagePoolDAO) { + this.messagePoolDAO = messagePoolDAO; + } + public void addOperation(MessageOperation operation) throws BusinessException { try { - DAOFactory.getDAOFactory().getMessagePoolDAO().add(operation); - MessagePool.getInstance(); + messagePoolDAO.add(operation); - } catch (DAOException ex) { - throw new BusinessException(ex); + } catch (DAOException e) { + throw new BusinessException(e); } } - public void sendEmail(String ownerEmail, String owner, String subject, - String content, String[] recipients, boolean direct) throws BusinessException { - // see https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html + public void updateStatus(MessageOperation operation, OperationStatus status) throws BusinessException { try { - logger.info("Sending email to: " + String.join(" ", recipients)); - Configuration conf = Configuration.getInstance(); - Properties props = new Properties(); - props.setProperty("mail.transport.protocol", conf.getMailProtocol()); - props.setProperty("mail.smtp.host", conf.getMailHost()); - props.setProperty("mail.smtp.port", String.valueOf(conf.getMailPort())); - props.setProperty("mail.smtp.auth", String.valueOf(conf.isMailAuth())); - props.setProperty("mail.smtp.starttls.enable", String.valueOf(conf.isMailAuth())); - props.setProperty("mail.smtp.ssl.trust", conf.getMailSslTrust()); - - Session session = Session.getDefaultInstance(props); - session.setDebug(false); - - MimeMessage mimeMessage = new MimeMessage(session); - mimeMessage.setContent(content, "text/html"); - mimeMessage.addHeader("Content-Type", "text/html"); - - InternetAddress from = new InternetAddress(ownerEmail, owner); - mimeMessage.setReplyTo(new InternetAddress[]{from}); - mimeMessage.setFrom(from); - mimeMessage.setSentDate(new Date()); - mimeMessage.setSubject(subject); - - Transport transport = session.getTransport(); - - if (conf.isMailAuth()) { - transport.connect( - conf.getMailHost(), conf.getMailPort(), - conf.getMailUsername(), conf.getMailPassword()); - } else { - transport.connect(); - } - - InternetAddress[] addressTo = null; - - if (recipients != null && recipients.length > 0) { - addressTo = new InternetAddress[recipients.length]; - for (int i = 0; i < recipients.length; i++) { - addressTo[i] = new InternetAddress(recipients[i]); - } - if (direct) { - mimeMessage.setRecipients(Message.RecipientType.TO, addressTo); - } else { - mimeMessage.setRecipients(Message.RecipientType.BCC, addressTo); - } - - transport.sendMessage(mimeMessage, addressTo); - transport.close(); + operation.setStatus(status); + messagePoolDAO.update(operation); - } else { - logger.warn("There's no recipients to send the email."); - } - } catch (UnsupportedEncodingException | MessagingException ex) { - logger.error(ex); - throw new BusinessException(ex); + } catch (DAOException e){ + throw new BusinessException(e); } } } diff --git a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/dao/DAOFactory.java b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/dao/DAOFactory.java deleted file mode 100644 index ad3ba54..0000000 --- a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/dao/DAOFactory.java +++ /dev/null @@ -1,66 +0,0 @@ -/* Copyright CNRS-CREATIS - * - * Rafael Ferreira da Silva - * rafael.silva@creatis.insa-lyon.fr - * http://www.rafaelsilva.com - * - * This software is a grid-enabled data-driven workflow manager and editor. - * - * This software is governed by the CeCILL license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/ or redistribute the software under the terms of the CeCILL - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL license and that you accept its terms. - */ -package fr.insalyon.creatis.sma.server.dao; - -/** - * - * @author Rafael Ferreira da Silva - */ -public abstract class DAOFactory { - - private static final int H2 = 1; - private static int factory = H2; - - public static DAOFactory getDAOFactory() { - - switch (factory) { - case H2: - return H2DAOFactory.getInstance(); - default: - return null; - } - } - - protected DAOFactory() { - connect(); - createTables(); - } - - protected abstract void connect(); - - protected abstract void createTables(); - - public abstract MessagePoolDAO getMessagePoolDAO(); -} diff --git a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/dao/H2DAOFactory.java b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/dao/H2DAOFactory.java deleted file mode 100644 index 79bf393..0000000 --- a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/dao/H2DAOFactory.java +++ /dev/null @@ -1,125 +0,0 @@ -/* Copyright CNRS-CREATIS - * - * Rafael Ferreira da Silva - * rafael.silva@creatis.insa-lyon.fr - * http://www.rafaelsilva.com - * - * This software is a grid-enabled data-driven workflow manager and editor. - * - * This software is governed by the CeCILL license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/ or redistribute the software under the terms of the CeCILL - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL license and that you accept its terms. - */ -package fr.insalyon.creatis.sma.server.dao; - -import fr.insalyon.creatis.sma.server.dao.h2.MessagePoolData; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.sql.Statement; -import org.apache.log4j.Logger; - -/** - * - * @author Rafael Ferreira da Silva - */ -public class H2DAOFactory extends DAOFactory { - - private static final Logger logger = Logger.getLogger(H2DAOFactory.class); - private static H2DAOFactory instance; - private final String DRIVER = "org.h2.Driver"; - private final String DBURL = "jdbc:h2:./db/sma.db"; - private Connection connection; - - public static H2DAOFactory getInstance() { - if (instance == null) { - instance = new H2DAOFactory(); - } - return instance; - } - - private H2DAOFactory() { - super(); - } - - @Override - protected void connect() { - try { - Class.forName(DRIVER); - connection = DriverManager.getConnection(DBURL + ";create=true"); - connection.setAutoCommit(true); - - } catch (SQLException ex) { - try { - connection = DriverManager.getConnection(DBURL); - connection.setAutoCommit(true); - - } catch (SQLException ex1) { - logException(ex); - } - } catch (ClassNotFoundException ex) { - logException(ex); - } - } - - @Override - protected void createTables() { - try { - Statement stat = connection.createStatement(); - stat.executeUpdate("CREATE TABLE MessagePool (" - + "id VARCHAR(100), " - + "registration TIMESTAMP, " - + "fromEmail VARCHAR(255), " - + "fromName VARCHAR(255), " - + "subject VARCHAR(255), " - + "contents CLOB, " - + "recipients CLOB, " - + "direct BOOLEAN, " - + "status VARCHAR(50), " - + "username VARCHAR(255), " - + "retrycount INT, " - + "PRIMARY KEY (id)" - + ")"); - stat.executeUpdate("CREATE INDEX user_idx ON MessagePool(username)"); - - } catch (SQLException ex) { - logger.info("Table MessagePool already exists!"); - } - } - - @Override - public MessagePoolDAO getMessagePoolDAO() { - return MessagePoolData.getInstance(connection); - } - - private void logException(Exception ex) { - logger.error(ex); - if (logger.isDebugEnabled()) { - for (StackTraceElement stack : ex.getStackTrace()) { - logger.debug(stack); - } - } - } -} diff --git a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/dao/h2/H2Factory.java b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/dao/h2/H2Factory.java new file mode 100644 index 0000000..832c1e3 --- /dev/null +++ b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/dao/h2/H2Factory.java @@ -0,0 +1,102 @@ +/* Copyright CNRS-CREATIS + * + * Rafael Ferreira da Silva + * rafael.silva@creatis.insa-lyon.fr + * http://www.rafaelsilva.com + * + * This software is a grid-enabled data-driven workflow manager and editor. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ +package fr.insalyon.creatis.sma.server.dao.h2; + +import fr.insalyon.creatis.sma.server.utils.Constants; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +import org.apache.log4j.Logger; +import org.h2.jdbcx.JdbcConnectionPool; + +/** + * + * @author Rafael Ferreira da Silva + */ +public class H2Factory { + + private static final Logger LOG = Logger.getLogger(H2Factory.class); + private static H2Factory instance; + private final String DBURL = "jdbc:h2:file:./db/sma.dbl"; + private JdbcConnectionPool connectionPool; + + public static H2Factory getInstance() { + if (instance == null) { + instance = new H2Factory(); + } + return instance; + } + + private H2Factory() { + connectionPool = JdbcConnectionPool.create(DBURL, "sa", ""); + connectionPool.setMaxConnections(Constants.MAX_DB_CONNECTIONS); + connectionPool.setLoginTimeout(Constants.TIMEOUT_POOL_SECONDS); + + createTables(); + } + + private void createTables() { + try (Connection connection = getConnection()) { + try (Statement st = connection.createStatement()) { + st.executeUpdate("CREATE TABLE MessagePool (" + + "id VARCHAR(100), " + + "registration TIMESTAMP, " + + "fromEmail VARCHAR(255), " + + "fromName VARCHAR(255), " + + "subject VARCHAR(255), " + + "contents CLOB, " + + "recipients CLOB, " + + "direct BOOLEAN, " + + "status VARCHAR(50), " + + "username VARCHAR(255), " + + "retrycount INT, " + + "PRIMARY KEY (id)" + + ")"); + st.executeUpdate("CREATE INDEX user_idx ON MessagePool(username)"); + } + } catch (SQLException ex) { + LOG.info("Table MessagePool already exists!"); + } + } + + Connection getConnection() throws SQLException { + Connection connection = connectionPool.getConnection(); + + connection.setAutoCommit(true); + return connection; + } +} diff --git a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/dao/h2/MessagePoolData.java b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/dao/h2/MessagePoolData.java index f2b634e..d5f261c 100644 --- a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/dao/h2/MessagePoolData.java +++ b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/dao/h2/MessagePoolData.java @@ -52,29 +52,16 @@ */ public class MessagePoolData implements MessagePoolDAO { - private final static Logger logger = Logger.getLogger(MessagePoolData.class); - private static MessagePoolData instance; - private Connection connection; - - public synchronized static MessagePoolData getInstance(Connection connection) { - if (instance == null) { - instance = new MessagePoolData(connection); - } - return instance; - } - - private MessagePoolData(Connection connection) { - this.connection = connection; - } + private final static Logger LOG = Logger.getLogger(MessagePoolData.class); @Override public void add(MessageOperation operation) throws DAOException { + String query = "INSERT INTO MessagePool(id, registration, fromEmail, fromName, subject, " + + "contents, recipients, direct, status, username, retrycount) " + + "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; - try { - PreparedStatement ps = connection.prepareStatement("INSERT INTO " - + "MessagePool(id, registration, fromEmail, fromName, subject, " - + "contents, recipients, direct, status, username, retrycount) " - + "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + try (Connection connection = H2Factory.getInstance().getConnection(); + PreparedStatement ps = connection.prepareStatement(query)) { ps.setString(1, operation.getId()); ps.setTimestamp(2, new Timestamp(operation.getRegistration().getTime())); @@ -87,24 +74,23 @@ public void add(MessageOperation operation) throws DAOException { ps.setString(9, operation.getStatus().name()); ps.setString(10, operation.getUsername()); ps.setInt(11, operation.getRetryCount()); - ps.execute(); - ps.close(); + ps.executeUpdate(); } catch (SQLException ex) { - logger.error(ex); + LOG.error(ex); throw new DAOException(ex); } } @Override public void update(MessageOperation operation) throws DAOException { + String query = "UPDATE MessagePool SET registration = ?, fromEmail = ?, fromName = ?, " + + "subject = ?, contents = ?, recipients = ?, direct = ?, " + + "status = ?, username = ?, retrycount = ? WHERE id = ?"; + + try (Connection connection = H2Factory.getInstance().getConnection(); + PreparedStatement ps = connection.prepareStatement(query)) { - try { - PreparedStatement ps = connection.prepareStatement("UPDATE MessagePool " - + "SET registration = ?, fromEmail = ?, fromName = ?, " - + "subject = ?, contents = ?, recipients = ?, direct = ?, " - + "status = ?, username = ?, retrycount = ? " - + "WHERE id = ?"); ps.setTimestamp(1, new Timestamp(operation.getRegistration().getTime())); ps.setString(2, operation.getFromEmail()); ps.setString(3, operation.getFromName()); @@ -117,38 +103,36 @@ public void update(MessageOperation operation) throws DAOException { ps.setInt(10, operation.getRetryCount()); ps.setString(11, operation.getId()); ps.executeUpdate(); - ps.close(); } catch (SQLException ex) { - logger.error(ex); + LOG.error(ex); throw new DAOException(ex); } } @Override public void remove(MessageOperation operation) throws DAOException { - try { - PreparedStatement ps = connection.prepareStatement("DELETE " - + "FROM MessagePool WHERE id=?"); + String query = "DELETE FROM MessagePool WHERE id=?"; + + try (Connection connection = H2Factory.getInstance().getConnection(); + PreparedStatement ps = connection.prepareStatement(query)) { ps.setString(1, operation.getId()); ps.execute(); - ps.close(); } catch (SQLException ex) { - logger.error(ex); + LOG.error(ex); throw new DAOException(ex); } } @Override public List getPendingOperations() throws DAOException { + String query = getSelect() + "WHERE status = ? OR STATUS = ? ORDER BY registration"; - try { + try (Connection connection = H2Factory.getInstance().getConnection(); + PreparedStatement ps = connection.prepareStatement(query)) { - PreparedStatement ps = connection.prepareStatement(getSelect() - + "WHERE status = ? OR status = ? " - + "ORDER BY registration"); ps.setString(1, OperationStatus.Queued.name()); ps.setString(2, OperationStatus.Rescheduled.name()); @@ -157,38 +141,35 @@ public List getPendingOperations() throws DAOException { while (rs.next()) { operations.add(getMessageOperation(rs)); } - ps.close(); return operations; } catch (SQLException ex) { - logger.error(ex); + LOG.error(ex); throw new DAOException(ex); } } @Override public List getOldOperations(Date date) throws DAOException { + List operations = new ArrayList(); + String query = getSelect() + "WHERE registration < ? AND (status = ? OR status = ?)" + + "ORDER BY registration"; - try { - List operations = new ArrayList(); - PreparedStatement ps = connection.prepareStatement( - getSelect() - + "WHERE registration < ? " - + "AND (status = ? OR status = ?)" - + "ORDER BY registration"); + try (Connection connection = H2Factory.getInstance().getConnection(); + PreparedStatement ps = connection.prepareStatement(query)) { ps.setTimestamp(1, new Timestamp(date.getTime())); ps.setString(2, OperationStatus.Done.name()); ps.setString(3, OperationStatus.Failed.name()); + ResultSet rs = ps.executeQuery(); while (rs.next()) { operations.add(getMessageOperation(rs)); } - ps.close(); return operations; } catch (SQLException ex) { - logger.error(ex); + LOG.error(ex); throw new DAOException(ex); } } diff --git a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/Command.java b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/Command.java index e0aea6d..e84ee3a 100644 --- a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/Command.java +++ b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/Command.java @@ -45,7 +45,6 @@ public abstract class Command { protected Communication communication; protected Command(Communication communication) { - this.communication = communication; } diff --git a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/MessageCleanerPool.java b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/MessageCleanerPool.java deleted file mode 100644 index d50d22a..0000000 --- a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/MessageCleanerPool.java +++ /dev/null @@ -1,97 +0,0 @@ -/* Copyright CNRS-CREATIS - * - * Rafael Ferreira da Silva - * rafael.silva@creatis.insa-lyon.fr - * http://www.rafaelsilva.com - * - * This software is governed by the CeCILL license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/ or redistribute the software under the terms of the CeCILL - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL license and that you accept its terms. - */ -package fr.insalyon.creatis.sma.server.execution; - -import fr.insalyon.creatis.sma.common.bean.MessageOperation; -import fr.insalyon.creatis.sma.server.Configuration; -import fr.insalyon.creatis.sma.server.dao.DAOException; -import fr.insalyon.creatis.sma.server.dao.DAOFactory; -import fr.insalyon.creatis.sma.server.dao.MessagePoolDAO; -import java.util.Calendar; -import org.apache.log4j.Logger; - -/** - * - * @author Rafael Ferreira da Silva - */ -public class MessageCleanerPool extends Thread { - - private static final Logger logger = Logger.getLogger(MessagePool.class); - private static MessageCleanerPool instance; - private volatile boolean stop; - private MessagePoolDAO messagePoolDAO; - - public static MessageCleanerPool getInstance() { - - if (instance == null) { - instance = new MessageCleanerPool(); - instance.start(); - } - return instance; - } - - private MessageCleanerPool() { - - this.stop = false; - } - - @Override - public void run() { - - while (!stop) { - try { - Calendar cal = Calendar.getInstance(); - cal.add(Calendar.DATE, -(Configuration.getInstance().getMaxHistory())); - - messagePoolDAO = DAOFactory.getDAOFactory().getMessagePoolDAO(); - - for (MessageOperation operation : messagePoolDAO.getOldOperations(cal.getTime())) { - messagePoolDAO.remove(operation); - logger.info("Removed: " + operation.getRegistration() - + ", FROM: " + operation.getFromName() - + ", TO: " + operation.getRecipientsAsString()); - } - - } catch (DAOException ex) { - // do nothing - } - - try { - sleep(86400000); - } catch (InterruptedException ex) { - logger.error(ex); - } - } - instance = null; - } -} diff --git a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/MessagePool.java b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/MessagePool.java deleted file mode 100644 index 036f080..0000000 --- a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/MessagePool.java +++ /dev/null @@ -1,146 +0,0 @@ -/* Copyright CNRS-CREATIS - * - * Rafael Ferreira da Silva - * rafael.silva@creatis.insa-lyon.fr - * http://www.rafaelsilva.com - * - * This software is governed by the CeCILL license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/ or redistribute the software under the terms of the CeCILL - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL license and that you accept its terms. - */ -package fr.insalyon.creatis.sma.server.execution; - -import fr.insalyon.creatis.sma.common.bean.MessageOperation; -import fr.insalyon.creatis.sma.common.bean.OperationStatus; -import fr.insalyon.creatis.sma.server.Configuration; -import fr.insalyon.creatis.sma.server.business.BusinessException; -import fr.insalyon.creatis.sma.server.business.MessagePoolBusiness; -import fr.insalyon.creatis.sma.server.dao.DAOException; -import fr.insalyon.creatis.sma.server.dao.DAOFactory; -import fr.insalyon.creatis.sma.server.dao.MessagePoolDAO; -import java.util.List; -import org.apache.log4j.Logger; - -/** - * - * @author Rafael Ferreira da Silva - */ -public class MessagePool extends Thread { - - private static final Logger logger = Logger.getLogger(MessagePool.class); - private static MessagePool instance; - private MessagePoolDAO messagePoolDAO; - private static volatile int running = 0; - - public static MessagePool getInstance() { - - if (instance == null) { - instance = new MessagePool(); - instance.start(); - } - return instance; - } - - private MessagePool() { - } - - @Override - public void run() { - - try { - messagePoolDAO = DAOFactory.getDAOFactory().getMessagePoolDAO(); - List pendingOperations = messagePoolDAO.getPendingOperations(); - - while (!pendingOperations.isEmpty()) { - - for (MessageOperation mo : pendingOperations) { - if (running < Configuration.getInstance().getMailMaxRuns()) { - running++; - logger.info("[MessagePool] Processing operation '" + mo.getId() + "'."); - updateStatus(mo, OperationStatus.Running); - new Execute(mo).start(); - } else { - break; - } - } - pendingOperations = messagePoolDAO.getPendingOperations(); - } - } catch (DAOException ex) { - // do nothing - } - instance = null; - } - - private void updateStatus(MessageOperation operation, OperationStatus status) throws DAOException { - - operation.setStatus(status); - messagePoolDAO.update(operation); - } - - class Execute extends Thread { - - private MessageOperation operation; - - public Execute(MessageOperation operation) { - this.operation = operation; - } - - @Override - public void run() { - - try { - new MessagePoolBusiness().sendEmail( - operation.getFromEmail(), - operation.getFromName(), - operation.getSubject(), - operation.getContents(), - operation.getRecipients(), - operation.isDirect()); - updateStatus(operation, OperationStatus.Done); - - } catch (DAOException | BusinessException ex) { - logger.error(ex); - retry(); - } finally { - running--; - MessagePool.getInstance(); - } - } - - private void retry() { - - try { - if (operation.getRetryCount() == Configuration.getInstance().getMaxRetryCount()) { - updateStatus(operation, OperationStatus.Failed); - } else { - operation.incrementRetryCount(); - updateStatus(operation, OperationStatus.Rescheduled); - } - } catch (DAOException ex) { - // do nothing - } - } - } -} diff --git a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/ScheduledTasksCreator.java b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/ScheduledTasksCreator.java new file mode 100644 index 0000000..ab694dc --- /dev/null +++ b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/ScheduledTasksCreator.java @@ -0,0 +1,116 @@ +/* Copyright CNRS-CREATIS + * + * Rafael Ferreira da Silva + * rafael.silva@creatis.insa-lyon.fr + * http://www.rafaelsilva.com + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ +package fr.insalyon.creatis.sma.server.execution; + +import fr.insalyon.creatis.sma.common.bean.MessageOperation; +import fr.insalyon.creatis.sma.server.business.MessagePoolBusiness; +import fr.insalyon.creatis.sma.server.dao.DAOException; +import fr.insalyon.creatis.sma.server.dao.MessagePoolDAO; +import fr.insalyon.creatis.sma.server.execution.executors.MessageExecutor; +import fr.insalyon.creatis.sma.server.utils.Configuration; +import fr.insalyon.creatis.sma.server.utils.Constants; + +import java.util.Calendar; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; + +import org.apache.log4j.Logger; + +public class ScheduledTasksCreator { + + private static final Logger LOG = Logger.getLogger(ScheduledTasksCreator.class); + + public PoolCleaner getPoolCleanerTask(MessagePoolDAO messagePoolDAO) { + return new PoolCleaner(messagePoolDAO); + } + + public MessagePool getMessagePoolTask(ExecutorService executorService, MessagePoolDAO messagePoolDAO, MessagePoolBusiness messagePoolBusiness) { + return new MessagePool(executorService, messagePoolDAO, messagePoolBusiness); + } + + public static class PoolCleaner implements Runnable { + private final MessagePoolDAO messagePoolDAO; + + public PoolCleaner(MessagePoolDAO messagePoolDAO) { + this.messagePoolDAO = messagePoolDAO; + } + + @Override + public void run() { + LOG.info("Running Message Cleaner Pool"); + + try { + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.DATE, - (Configuration.getInstance().getMaxHistory())); + + for (MessageOperation operation : messagePoolDAO.getOldOperations(cal.getTime())) { + messagePoolDAO.remove(operation); + LOG.info("Removed: " + operation.getRegistration() + + ", FROM: " + operation.getFromName() + + ", TO: " + operation.getRecipientsAsString()); + } + + } catch (DAOException e) { + LOG.warn("Failed to run Message Cleaner Pool properly!", e); + } finally { + LOG.info("Finishing Message Cleaner Pool"); + } + } + } + + public static class MessagePool implements Runnable { + private final ExecutorService executor; + private final MessagePoolDAO messagePoolDAO; + private final MessagePoolBusiness messagePoolBusiness; + + public MessagePool(ExecutorService executorService, MessagePoolDAO messagePoolDAO, MessagePoolBusiness messagePoolBusiness) { + this.executor = executorService; + this.messagePoolDAO = messagePoolDAO; + this.messagePoolBusiness = messagePoolBusiness; + } + + @Override + public void run() { + try { + List callablesOperations = messagePoolDAO.getPendingOperations().stream() + .map(op -> new MessageExecutor(op, messagePoolBusiness)).toList(); + + executor.invokeAll(callablesOperations, Constants.MESSAGE_POOL_MAX_WAIT_SECONDS, TimeUnit.SECONDS); + + } catch (DAOException | InterruptedException e) { + LOG.warn("Failed to run Message Pool properly!", e); + } + } + } +} diff --git a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/command/SendEmailCommand.java b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/command/SendEmailCommand.java index 37a1c1e..f8e2b69 100644 --- a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/command/SendEmailCommand.java +++ b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/command/SendEmailCommand.java @@ -36,10 +36,10 @@ import fr.insalyon.creatis.sma.common.Communication; import fr.insalyon.creatis.sma.common.bean.MessageOperation; -import fr.insalyon.creatis.sma.server.Configuration; import fr.insalyon.creatis.sma.server.business.BusinessException; import fr.insalyon.creatis.sma.server.business.MessagePoolBusiness; import fr.insalyon.creatis.sma.server.execution.Command; +import fr.insalyon.creatis.sma.server.utils.Configuration; /** * @@ -47,14 +47,15 @@ */ public class SendEmailCommand extends Command { - private String subject; - private String contents; - private String recipients; - private boolean direct; - private String username; + private final String subject; + private final String contents; + private final String recipients; + private final boolean direct; + private final String username; + private final MessagePoolBusiness poolBusiness; - public SendEmailCommand(Communication communication, String subject, - String contents, String recipients, String direct, String username) { + public SendEmailCommand(Communication communication, MessagePoolBusiness poolBusiness, + String subject, String contents, String recipients, String direct, String username) { super(communication); this.subject = subject; @@ -62,6 +63,7 @@ public SendEmailCommand(Communication communication, String subject, this.recipients = recipients; this.direct = Boolean.valueOf(direct); this.username = username; + this.poolBusiness = poolBusiness; } @Override @@ -71,7 +73,7 @@ public void execute() { Configuration.getInstance().getMailFrom(), Configuration.getInstance().getMailFromName(), subject, contents, recipients, direct, username); - new MessagePoolBusiness().addOperation(operation); + poolBusiness.addOperation(operation); communication.sendMessage(operation.getId()); communication.sendSucessMessage(); diff --git a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/Executor.java b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/executors/CommunicationExecutor.java similarity index 81% rename from sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/Executor.java rename to sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/executors/CommunicationExecutor.java index 267db88..5fa3da2 100644 --- a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/Executor.java +++ b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/executors/CommunicationExecutor.java @@ -32,11 +32,13 @@ * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ -package fr.insalyon.creatis.sma.server.execution; +package fr.insalyon.creatis.sma.server.execution.executors; import fr.insalyon.creatis.sma.common.Communication; import fr.insalyon.creatis.sma.common.Constants; import fr.insalyon.creatis.sma.common.ExecutorConstants; +import fr.insalyon.creatis.sma.server.business.MessagePoolBusiness; +import fr.insalyon.creatis.sma.server.execution.Command; import fr.insalyon.creatis.sma.server.execution.command.SendEmailCommand; import java.io.IOException; import org.apache.log4j.Logger; @@ -45,13 +47,15 @@ * * @author Rafael Ferreira da Silva */ -public class Executor extends Thread { +public class CommunicationExecutor extends Thread { - private static final Logger logger = Logger.getLogger(Executor.class); - private Communication communication; + private static final Logger LOG = Logger.getLogger(CommunicationExecutor.class); + private final Communication communication; + private final MessagePoolBusiness poolBusiness; - public Executor(Communication communication) { + public CommunicationExecutor(Communication communication, MessagePoolBusiness poolBusiness) { this.communication = communication; + this.poolBusiness = poolBusiness; } @Override @@ -68,23 +72,17 @@ public void run() { logException(new Exception("Error during message receive: " + message)); } } catch (IOException ex) { - logger.error(ex); + LOG.error(ex); } finally { try { communication.close(); } catch (IOException ex) { - logger.error(ex); + LOG.error(ex); } } } - /** - * - * @param message - * @return - */ private Command parseCommand(String message) { - try { String[] tk = message.split(Constants.MSG_SEP_1); int command = Integer.parseInt(tk[0]); @@ -92,7 +90,7 @@ private Command parseCommand(String message) { switch (command) { case ExecutorConstants.MESSAGEPOOL_ADD_OPERATION: - return new SendEmailCommand(communication, tk[1], tk[2], tk[3], tk[4], tk[5]); + return new SendEmailCommand(communication, poolBusiness, tk[1], tk[2], tk[3], tk[4], tk[5]); default: logException(new Exception("Command not recognized: " + message)); @@ -106,20 +104,10 @@ private Command parseCommand(String message) { return null; } - /** - * - * @param ex - */ private void logException(Exception ex) { - communication.sendErrorMessage(ex.getMessage()); communication.sendEndOfMessage(); - logger.error(ex.getMessage()); - if (logger.isDebugEnabled()) { - for (StackTraceElement stack : ex.getStackTrace()) { - logger.debug(stack); - } - } + LOG.error("Exception occured", ex); } } diff --git a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/executors/MessageExecutor.java b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/executors/MessageExecutor.java new file mode 100644 index 0000000..e0aad52 --- /dev/null +++ b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/execution/executors/MessageExecutor.java @@ -0,0 +1,160 @@ +/* Copyright CNRS-CREATIS + * + * Rafael Ferreira da Silva + * rafael.silva@creatis.insa-lyon.fr + * http://www.rafaelsilva.com + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ +package fr.insalyon.creatis.sma.server.execution.executors; + +import fr.insalyon.creatis.sma.common.bean.MessageOperation; +import fr.insalyon.creatis.sma.common.bean.OperationStatus; +import fr.insalyon.creatis.sma.server.business.BusinessException; +import fr.insalyon.creatis.sma.server.business.MessagePoolBusiness; +import fr.insalyon.creatis.sma.server.utils.Configuration; +import jakarta.mail.Message; +import jakarta.mail.MessagingException; +import jakarta.mail.Session; +import jakarta.mail.Transport; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; + +import java.io.UnsupportedEncodingException; +import java.util.Date; +import java.util.Properties; +import java.util.concurrent.Callable; + +import org.apache.log4j.Logger; + +public class MessageExecutor implements Callable { + + private static final Logger LOG = Logger.getLogger(MessageExecutor.class); + private final MessageOperation operation; + private final MessagePoolBusiness poolBusiness; + + public MessageExecutor(MessageOperation operation, MessagePoolBusiness messagePoolBusiness) { + this.operation = operation; + this.poolBusiness = messagePoolBusiness; + } + + @Override + public Void call() throws Exception { + LOG.info("[MessagePool] Processing operation '" + operation.getId() + "'."); + + try { + poolBusiness.updateStatus(operation, OperationStatus.Running); + sendEmail( + operation.getFromEmail(), + operation.getFromName(), + operation.getSubject(), + operation.getContents(), + operation.getRecipients(), + operation.isDirect()); + poolBusiness.updateStatus(operation, OperationStatus.Done); + + } catch (BusinessException ex) { + LOG.error(ex); + retry(); + } + return null; + } + + public void sendEmail(String ownerEmail, String owner, String subject, + String content, String[] recipients, boolean direct) throws BusinessException { + // see https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html + try { + LOG.info("Sending email to: " + String.join(" ", recipients)); + Configuration conf = Configuration.getInstance(); + Properties props = new Properties(); + props.setProperty("mail.transport.protocol", conf.getMailProtocol()); + props.setProperty("mail.smtp.host", conf.getMailHost()); + props.setProperty("mail.smtp.port", String.valueOf(conf.getMailPort())); + props.setProperty("mail.smtp.auth", String.valueOf(conf.isMailAuth())); + props.setProperty("mail.smtp.starttls.enable", String.valueOf(conf.isMailAuth())); + + if (conf.isMailSslTrust()) { + props.setProperty("mail.smtp.ssl.trust", conf.getMailHost()); + } + + Session session = Session.getDefaultInstance(props); + session.setDebug(false); + + MimeMessage mimeMessage = new MimeMessage(session); + mimeMessage.setContent(content, "text/html"); + mimeMessage.addHeader("Content-Type", "text/html"); + + InternetAddress from = new InternetAddress(ownerEmail, owner); + mimeMessage.setReplyTo(new InternetAddress[]{from}); + mimeMessage.setFrom(from); + mimeMessage.setSentDate(new Date()); + mimeMessage.setSubject(subject); + + Transport transport = session.getTransport(); + + if (conf.isMailAuth()) { + transport.connect( + conf.getMailHost(), conf.getMailPort(), + conf.getMailUsername(), conf.getMailPassword()); + } else { + transport.connect(); + } + + InternetAddress[] addressTo = null; + + if (recipients != null && recipients.length > 0) { + addressTo = new InternetAddress[recipients.length]; + for (int i = 0; i < recipients.length; i++) { + addressTo[i] = new InternetAddress(recipients[i]); + } + if (direct) { + mimeMessage.setRecipients(Message.RecipientType.TO, addressTo); + } else { + mimeMessage.setRecipients(Message.RecipientType.BCC, addressTo); + } + + transport.sendMessage(mimeMessage, addressTo); + transport.close(); + + } else { + LOG.warn("There's no recipients to send the email."); + } + } catch (UnsupportedEncodingException | MessagingException ex) { + LOG.error(ex); + throw new BusinessException(ex); + } + } + + public void retry() throws BusinessException { + if (operation.getRetryCount() == Configuration.getInstance().getMaxRetryCount()) { + poolBusiness.updateStatus(operation, OperationStatus.Failed); + } else { + operation.incrementRetryCount(); + poolBusiness.updateStatus(operation, OperationStatus.Rescheduled); + } + } +} diff --git a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/Configuration.java b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/utils/Configuration.java similarity index 93% rename from sma-server/src/main/java/fr/insalyon/creatis/sma/server/Configuration.java rename to sma-server/src/main/java/fr/insalyon/creatis/sma/server/utils/Configuration.java index 296e610..9470f76 100644 --- a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/Configuration.java +++ b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/utils/Configuration.java @@ -30,7 +30,7 @@ * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ -package fr.insalyon.creatis.sma.server; +package fr.insalyon.creatis.sma.server.utils; import java.io.File; import org.apache.commons.configuration.ConfigurationException; @@ -43,7 +43,7 @@ */ public class Configuration { - private static final Logger logger = Logger.getLogger(Configuration.class); + private static final Logger LOG = Logger.getLogger(Configuration.class); private static Configuration instance; private static final String confFile = "sma-server.conf"; // General @@ -52,8 +52,8 @@ public class Configuration { private int maxRetryCount; private int mailPort; private boolean mailAuth; + private boolean mailSslTrust; private String mailHost; - private String mailSslTrust; private String mailUsername; private String mailPassword; private String mailProtocol; @@ -62,7 +62,6 @@ public class Configuration { private int mailMaxRuns; public static Configuration getInstance() { - if (instance == null) { instance = new Configuration(); } @@ -75,7 +74,7 @@ public void setConfiguration(Configuration config) { private Configuration() { try { - logger.info("Loading configuration file."); + LOG.info("Loading configuration file."); PropertiesConfiguration config = new PropertiesConfiguration(new File(confFile)); port = config.getInt(Constants.LAB_AGENT_PORT, 8082); @@ -85,12 +84,12 @@ private Configuration() { maxHistory = config.getInt(Constants.LAB_AGENT_MAX_HISTORY, 90); maxRetryCount = config.getInt(Constants.LAB_AGENT_RETRYCOUNT, 5); mailHost = config.getString(Constants.LAB_MAIL_HOST, "smtp.localhost"); - mailSslTrust = config.getString(Constants.LAB_MAIL_SSL_TRUST, ""); + mailSslTrust = config.getBoolean(Constants.LAB_MAIL_SSL_TRUST, false); mailPort = config.getInt(Constants.LAB_MAIL_PORT, 25); mailProtocol = config.getString(Constants.LAB_MAIL_PROTOCOL, "smtp"); mailFrom = config.getString(Constants.LAB_MAIL_FROM, "example@example.com"); mailFromName = config.getString(Constants.LAB_MAIL_FROM_NAME, "Example"); - mailMaxRuns = config.getInt(Constants.LAB_MAIL_MAX_RUNS, 5); + mailMaxRuns = config.getInt(Constants.LAB_MAIL_MAX_RUNS, 50); config.setProperty(Constants.LAB_AGENT_PORT, port); config.setProperty(Constants.LAB_AGENT_RETRYCOUNT, maxRetryCount); @@ -112,7 +111,7 @@ private Configuration() { config.save(); } catch (ConfigurationException ex) { - logger.error(ex); + LOG.error(ex); } } @@ -136,7 +135,7 @@ public String getMailHost() { return mailHost; } - public String getMailSslTrust() { + public boolean isMailSslTrust() { return mailSslTrust; } diff --git a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/Constants.java b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/utils/Constants.java similarity index 88% rename from sma-server/src/main/java/fr/insalyon/creatis/sma/server/Constants.java rename to sma-server/src/main/java/fr/insalyon/creatis/sma/server/utils/Constants.java index fc5dba0..69923c6 100644 --- a/sma-server/src/main/java/fr/insalyon/creatis/sma/server/Constants.java +++ b/sma-server/src/main/java/fr/insalyon/creatis/sma/server/utils/Constants.java @@ -30,7 +30,7 @@ * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ -package fr.insalyon.creatis.sma.server; +package fr.insalyon.creatis.sma.server.utils; /** * @@ -49,6 +49,12 @@ public class Constants { public static final String LAB_MAIL_FROM = "mail.from"; public static final String LAB_MAIL_FROM_NAME = "mail.from.name"; public static final String LAB_MAIL_MAX_RUNS = "mail.max.simultaneous.runs"; + + public static final int CLEANER_POOL_SLEEP_HOURS = 24; + public static final int MESSAGE_POOL_SLEEP_SECONDS = 2; + public static final int MESSAGE_POOL_MAX_WAIT_SECONDS = 60; + public static final int TIMEOUT_POOL_SECONDS = 30; + public static final int MAX_DB_CONNECTIONS = 25; public static final String LAB_MAIL_AUTH = "mail.auth"; public static final String LAB_MAIL_SSL_TRUST = "mail.smtp.ssl.trust"; }