KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > outerj > daisy > emailer > serverimpl > CommonEmailer


1 /*
2  * Copyright 2004 Outerthought bvba and Schaubroeck nv
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

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 JavaDoc;
33 import javax.mail.Message JavaDoc;
34 import javax.mail.Transport JavaDoc;
35 import javax.mail.internet.MimeMessage JavaDoc;
36 import javax.mail.internet.InternetAddress JavaDoc;
37 import javax.sql.DataSource JavaDoc;
38 import java.util.Properties JavaDoc;
39 import java.util.Date JavaDoc;
40 import java.sql.*;
41
42 /**
43  * @avalon.component version="1.0" name="emailer" lifestyle="singleton"
44  * @avalon.service type="org.outerj.daisy.emailer.Emailer"
45  */

46 public class CommonEmailer extends AbstractLogEnabled implements Emailer, Serviceable, Configurable, Initializable, Startable {
47     private ExtensionRegistrar extensionRegistrar;
48     private String JavaDoc smtpHost;
49     private String JavaDoc smtpLocalhost;
50     private String JavaDoc emailCharSet;
51     private String JavaDoc defaultFrom;
52     private int emailThreadInterval;
53     private int maxTryCount;
54     private int retryInterval;
55     private long maxAge;
56     private Session JavaDoc session;
57     private DataSource JavaDoc dataSource;
58     private Thread JavaDoc 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 JavaDoc {
74         Properties JavaDoc props = new Properties JavaDoc();
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 JavaDoc {
86         emailerThread = new Thread JavaDoc(new EmailerThread(), "Daisy Emailer");
87         emailerThread.start();
88     }
89
90     public void stop() throws Exception JavaDoc {
91         emailerThread.interrupt();
92         try { emailerThread.join(); } catch (InterruptedException JavaDoc e) {}
93     }
94
95     /**
96      * @avalon.dependency key="datasource" type="javax.sql.DataSource"
97      * @avalon.dependency key="extensionRegistrar" type="org.outerj.daisy.repository.ExtensionRegistrar"
98      */

99     public void service(ServiceManager serviceManager) throws ServiceException {
100         dataSource = (DataSource JavaDoc)serviceManager.lookup("datasource");
101         extensionRegistrar = (ExtensionRegistrar)serviceManager.lookup("extensionRegistrar");
102     }
103
104     private class MyExtensionProvider implements ExtensionProvider {
105         public Object JavaDoc createExtension(Repository repository) {
106             return new LocalEmailer(repository, CommonEmailer.this);
107         }
108     }
109
110     private void sendEmail(String JavaDoc from, String JavaDoc to, String JavaDoc subject, String JavaDoc messageText) throws Exception JavaDoc {
111         MimeMessage JavaDoc msg = new MimeMessage JavaDoc(session);
112         msg.setFrom(new InternetAddress JavaDoc(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 JavaDoc());
120         Transport.send(msg);
121     }
122
123     public void send(String JavaDoc to, String JavaDoc subject, String JavaDoc messageText) {
124         if (to == null)
125             throw new NullPointerException JavaDoc("To address should not be null");
126         if (subject == null)
127             throw new NullPointerException JavaDoc("Subject should not be null");
128         if (messageText == null)
129             throw new NullPointerException JavaDoc("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 JavaDoc(System.currentTimeMillis()));
142             stmt.execute();
143         } catch (SQLException e) {
144             throw new RuntimeException JavaDoc("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 JavaDoc 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 JavaDoc e) {
166                 getLogger().error("Error closing prepared statement.", e);
167             }
168         }
169     }
170
171     class EmailerThread implements Runnable JavaDoc {
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 JavaDoc(System.currentTimeMillis() - (retryInterval * 60000)));
187                         ResultSet rs = stmt.executeQuery();
188
189                         while (rs.next()) {
190                             String JavaDoc from = rs.getString("from_address");
191                             if (from == null)
192                                 from = defaultFrom;
193                             String JavaDoc to = rs.getString("to_address");
194                             String JavaDoc subject = rs.getString("subject");
195                             String JavaDoc 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 JavaDoc e) {
204                                 // update DB record
205
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                         // cleanup expired messages
225
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 JavaDoc("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 JavaDoc e) {
240                     getLogger().error("Error in the emailer thread.", e);
241                 }
242
243                 // sleeping is performed after the try-catch block, so that in case of an exception
244
// we also sleep (the remaining exceptions will probably be problems connecting
245
// to the database, in which case we better wait a bit before trying again)
246
long sleepTime = emailThreadInterval - (System.currentTimeMillis() - lastInvocationTime);
247                 if (sleepTime > 0) {
248                     try {
249                         Thread.sleep(sleepTime);
250                     } catch (InterruptedException JavaDoc e) {
251                         getLogger().info("Emailer shutting down.");
252                         break bigLoop;
253                     }
254                 }
255             }
256         }
257     }
258 }
259
Popular Tags