1 16 package org.outerj.daisy.emailer.serverimpl; 17 18 import org.apache.avalon.framework.configuration.Configurable; 19 import org.apache.avalon.framework.configuration.Configuration; 20 import org.apache.avalon.framework.configuration.ConfigurationException; 21 import org.apache.avalon.framework.activity.Initializable; 22 import org.apache.avalon.framework.activity.Startable; 23 import org.apache.avalon.framework.logger.AbstractLogEnabled; 24 import org.apache.avalon.framework.service.Serviceable; 25 import org.apache.avalon.framework.service.ServiceManager; 26 import org.apache.avalon.framework.service.ServiceException; 27 import org.outerj.daisy.emailer.Emailer; 28 import org.outerj.daisy.repository.ExtensionRegistrar; 29 import org.outerj.daisy.repository.ExtensionProvider; 30 import org.outerj.daisy.repository.Repository; 31 32 import javax.mail.Session ; 33 import javax.mail.Message ; 34 import javax.mail.Transport ; 35 import javax.mail.internet.MimeMessage ; 36 import javax.mail.internet.InternetAddress ; 37 import javax.sql.DataSource ; 38 import java.util.Properties ; 39 import java.util.Date ; 40 import java.sql.*; 41 42 46 public class CommonEmailer extends AbstractLogEnabled implements Emailer, Serviceable, Configurable, Initializable, Startable { 47 private ExtensionRegistrar extensionRegistrar; 48 private String smtpHost; 49 private String smtpLocalhost; 50 private String emailCharSet; 51 private String defaultFrom; 52 private int emailThreadInterval; 53 private int maxTryCount; 54 private int retryInterval; 55 private long maxAge; 56 private Session session; 57 private DataSource dataSource; 58 private Thread emailerThread; 59 private boolean debugJavamail; 60 61 public void configure(Configuration configuration) throws ConfigurationException { 62 smtpHost = configuration.getChild("smtpHost").getValue(); 63 smtpLocalhost = configuration.getChild("smtpLocalhost").getValue(null); 64 emailCharSet = configuration.getChild("emailCharSet").getValue(null); 65 defaultFrom = configuration.getChild("fromAddress").getValue(); 66 emailThreadInterval = configuration.getChild("emailThreadInterval").getValueAsInteger(); 67 maxTryCount = configuration.getChild("maxTryCount").getValueAsInteger(); 68 retryInterval = configuration.getChild("retryInterval").getValueAsInteger(); 69 debugJavamail = configuration.getChild("javaMailDebug").getValueAsBoolean(false); 70 maxAge = configuration.getChild("maxAge").getValueAsLong(7) * 24 * 60 * 60 * 1000; 71 } 72 73 public void initialize() throws Exception { 74 Properties props = new Properties (); 75 props.put("mail.smtp.host", smtpHost); 76 if (smtpLocalhost != null) 77 props.put("mail.smtp.localhost", smtpLocalhost); 78 session = Session.getInstance(props); 79 if (debugJavamail) 80 session.setDebug(true); 81 82 extensionRegistrar.registerExtension("Emailer", new MyExtensionProvider()); 83 } 84 85 public void start() throws Exception { 86 emailerThread = new Thread (new EmailerThread(), "Daisy Emailer"); 87 emailerThread.start(); 88 } 89 90 public void stop() throws Exception { 91 emailerThread.interrupt(); 92 try { emailerThread.join(); } catch (InterruptedException e) {} 93 } 94 95 99 public void service(ServiceManager serviceManager) throws ServiceException { 100 dataSource = (DataSource )serviceManager.lookup("datasource"); 101 extensionRegistrar = (ExtensionRegistrar)serviceManager.lookup("extensionRegistrar"); 102 } 103 104 private class MyExtensionProvider implements ExtensionProvider { 105 public Object createExtension(Repository repository) { 106 return new LocalEmailer(repository, CommonEmailer.this); 107 } 108 } 109 110 private void sendEmail(String from, String to, String subject, String messageText) throws Exception { 111 MimeMessage msg = new MimeMessage (session); 112 msg.setFrom(new InternetAddress (from)); 113 msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to, true)); 114 msg.setSubject(subject); 115 if (emailCharSet != null) 116 msg.setText(messageText, emailCharSet); 117 else 118 msg.setText(messageText); 119 msg.setSentDate(new Date ()); 120 Transport.send(msg); 121 } 122 123 public void send(String to, String subject, String messageText) { 124 if (to == null) 125 throw new NullPointerException ("To address should not be null"); 126 if (subject == null) 127 throw new NullPointerException ("Subject should not be null"); 128 if (messageText == null) 129 throw new NullPointerException ("Message text should not be null"); 130 131 Connection conn = null; 132 PreparedStatement stmt = null; 133 try { 134 conn = dataSource.getConnection(); 135 stmt = conn.prepareStatement("insert into email_queue(from_address,to_address,subject,message,retry_count,created) values(?,?,?,?,?,?)"); 136 stmt.setNull(1, Types.VARCHAR); 137 stmt.setString(2, to); 138 stmt.setString(3, subject); 139 stmt.setString(4, messageText); 140 stmt.setInt(5, 0); 141 stmt.setTimestamp(6, new java.sql.Timestamp (System.currentTimeMillis())); 142 stmt.execute(); 143 } catch (SQLException e) { 144 throw new RuntimeException ("Failed to create email db record.", e); 145 } finally { 146 closeStatement(stmt); 147 closeConnection(conn); 148 } 149 } 150 151 private void closeConnection(Connection conn) { 152 if (conn != null) { 153 try { 154 conn.close(); 155 } catch (Exception e) { 156 getLogger().error("Error closing connection.", e); 157 } 158 } 159 } 160 161 private void closeStatement(PreparedStatement stmt) { 162 if (stmt != null) { 163 try { 164 stmt.close(); 165 } catch (Exception e) { 166 getLogger().error("Error closing prepared statement.", e); 167 } 168 } 169 } 170 171 class EmailerThread implements Runnable { 172 public void run() { 173 long lastInvocationTime = System.currentTimeMillis(); 174 bigLoop: while (true) { 175 try { 176 lastInvocationTime = System.currentTimeMillis(); 177 178 Connection conn = null; 179 PreparedStatement stmt = null; 180 PreparedStatement stmtUpdate = null; 181 PreparedStatement stmtDelete = null; 182 try { 183 conn = dataSource.getConnection(); 184 stmt = conn.prepareStatement("select id,from_address,to_address,subject,message,retry_count,created from email_queue where retry_count < ? and (last_try_time is null or last_try_time < ?) order by created"); 185 stmt.setLong(1, maxTryCount); 186 stmt.setTimestamp(2, new java.sql.Timestamp (System.currentTimeMillis() - (retryInterval * 60000))); 187 ResultSet rs = stmt.executeQuery(); 188 189 while (rs.next()) { 190 String from = rs.getString("from_address"); 191 if (from == null) 192 from = defaultFrom; 193 String to = rs.getString("to_address"); 194 String subject = rs.getString("subject"); 195 String message = rs.getString("message"); 196 int retryCount = rs.getInt("retry_count"); 197 long id = rs.getLong("id"); 198 boolean success = false; 199 200 try { 201 sendEmail(from, to, subject, message); 202 success = true; 203 } catch (Throwable e) { 204 if (stmtUpdate == null) 206 stmtUpdate = conn.prepareStatement("update email_queue set retry_count = ?, last_try_time = ?, error = ? where id = ?"); 207 stmtUpdate.setInt(1, retryCount + 1); 208 stmtUpdate.setTimestamp(2, new Timestamp(System.currentTimeMillis())); 209 stmtUpdate.setString(3, e.toString()); 210 stmtUpdate.setLong(4, id); 211 stmtUpdate.execute(); 212 } 213 214 if (success) { 215 if (stmtDelete == null) 216 stmtDelete = conn.prepareStatement("delete from email_queue where id = ?"); 217 stmtDelete.setLong(1, id); 218 stmtDelete.execute(); 219 } 220 } 221 222 stmt.close(); 223 224 stmt = conn.prepareStatement("delete from email_queue where retry_count >= ? and last_try_time < ?"); 226 stmt.setLong(1, maxTryCount); 227 stmt.setTimestamp(2, new Timestamp(System.currentTimeMillis() - maxAge)); 228 int messagesDeleted = stmt.executeUpdate(); 229 if (messagesDeleted > 0) 230 getLogger().warn("Removed " + messagesDeleted + " expired unsent messages from the email queue."); 231 } catch (SQLException e) { 232 throw new RuntimeException ("Database-related problem in emailer-thread.", e); 233 } finally { 234 closeStatement(stmt); 235 closeStatement(stmtUpdate); 236 closeStatement(stmtDelete); 237 closeConnection(conn); 238 } 239 } catch (Throwable e) { 240 getLogger().error("Error in the emailer thread.", e); 241 } 242 243 long sleepTime = emailThreadInterval - (System.currentTimeMillis() - lastInvocationTime); 247 if (sleepTime > 0) { 248 try { 249 Thread.sleep(sleepTime); 250 } catch (InterruptedException e) { 251 getLogger().info("Emailer shutting down."); 252 break bigLoop; 253 } 254 } 255 } 256 } 257 } 258 } 259 | Popular Tags |