View Javadoc

1   /************************************************************************
2    * Copyright (c) 2000-2006 The Apache Software Foundation.             *
3    * All rights reserved.                                                *
4    * ------------------------------------------------------------------- *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you *
6    * may not use this file except in compliance with the License. You    *
7    * may obtain a copy of the License at:                                *
8    *                                                                     *
9    *     http://www.apache.org/licenses/LICENSE-2.0                      *
10   *                                                                     *
11   * Unless required by applicable law or agreed to in writing, software *
12   * distributed under the License is distributed on an "AS IS" BASIS,   *
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or     *
14   * implied.  See the License for the specific language governing       *
15   * permissions and limitations under the License.                      *
16   ***********************************************************************/
17  
18  package org.apache.james.transport.mailets;
19  
20  import org.apache.avalon.cornerstone.services.datasources.DataSourceSelector;
21  import org.apache.avalon.excalibur.datasource.DataSourceComponent;
22  import org.apache.avalon.framework.service.ServiceManager;
23  import org.apache.james.Constants;
24  import org.apache.james.util.JDBCUtil;
25  import org.apache.mailet.MailAddress;
26  import org.apache.mailet.MailetException;
27  
28  import javax.mail.MessagingException;
29  import javax.mail.internet.ParseException;
30  
31  import java.sql.Connection;
32  import java.sql.DatabaseMetaData;
33  import java.sql.PreparedStatement;
34  import java.sql.ResultSet;
35  import java.sql.SQLException;
36  import java.sql.Statement;
37  import java.util.Collection;
38  import java.util.Vector;
39  
40  /***
41   * Rewrites recipient addresses based on a database table.  The connection
42   * is configured by passing the URL to a conn definition.  You need to set
43   * the table name to check (or view) along with the source and target columns
44   * to use.  For example,
45   * <mailet match="All" class="JDBCListserv">
46   *   <data_source>maildb</datasource>
47   *   <listserv_id>mylistserv</listserv_id>
48   *   <listserv_table>source_email_address</listserv_table>
49   *   <members_table>target_email_address</members_table>
50   * </mailet>
51   *
52   * This mailet will cache the settings available when first initialized.  If you wish
53   * it to reload for each message, add the init parameter
54   * <cache_settings>false</cache_settings>
55   *
56   */
57  public class JDBCListserv extends GenericListserv {
58  
59      protected DataSourceComponent datasource;
60      protected String listservID = null;
61      protected String listservTable = null;
62      protected String membersTable = null;
63      protected boolean cacheSettings = true;
64  
65      //Settings for this listserv
66      protected Collection members = null;
67      protected boolean membersOnly = true;
68      protected boolean attachmentsAllowed = true;
69      protected boolean replyToList = true;
70      protected MailAddress listservAddress = null;
71      protected String subjectPrefix = null;
72  
73      //Queries to DB
74      protected String listservQuery = null;
75      protected String membersQuery = null;
76  
77      /***
78       * The JDBCUtil helper class
79       */
80      private final JDBCUtil theJDBCUtil =
81              new JDBCUtil() {
82                  protected void delegatedLog(String logString) {
83                      log("JDBCListserv: " + logString);
84                  }
85              };
86  
87      /***
88       * Initialize the mailet
89       */
90      public void init() throws MessagingException {
91          if (getInitParameter("data_source") == null) {
92              throw new MailetException("data_source not specified for JDBCListserv");
93          }
94          if (getInitParameter("listserv_id") == null) {
95              throw new MailetException("listserv_id not specified for JDBCListserv");
96          }
97          if (getInitParameter("listserv_table") == null) {
98              throw new MailetException("listserv_table not specified for JDBCListserv");
99          }
100         if (getInitParameter("members_table") == null) {
101             throw new MailetException("members_table not specified for JDBCListserv");
102         }
103 
104         String datasourceName = getInitParameter("data_source");
105         listservID = getInitParameter("listserv_id");
106         listservTable = getInitParameter("listserv_table");
107         membersTable = getInitParameter("members_table");
108 
109         if (getInitParameter("cache_settings") != null) {
110             try {
111                 cacheSettings = new Boolean(getInitParameter("cache_settings")).booleanValue();
112             } catch (Exception e) {
113                 //ignore error
114             }
115         }
116 
117         Connection conn = null;
118 
119         try {
120             ServiceManager componentManager = (ServiceManager)getMailetContext().getAttribute(Constants.AVALON_COMPONENT_MANAGER);
121             // Get the DataSourceSelector service
122             DataSourceSelector datasources = (DataSourceSelector)componentManager.lookup(DataSourceSelector.ROLE);
123             // Get the data-source required.
124             datasource = (DataSourceComponent)datasources.select(datasourceName);
125 
126             conn = datasource.getConnection();
127 
128             // Check if the required listserv table exists. If not, complain.
129             DatabaseMetaData dbMetaData = conn.getMetaData();
130             // Need to ask in the case that identifiers are stored, ask the DatabaseMetaInfo.
131             // Try UPPER, lower, and MixedCase, to see if the table is there.
132             if (!(theJDBCUtil.tableExists(dbMetaData, listservTable)))  {
133                 StringBuffer exceptionBuffer =
134                     new StringBuffer(128)
135                             .append("Could not find table '")
136                             .append(listservTable)
137                             .append("' in datasource '")
138                             .append(datasourceName)
139                             .append("'");
140                 throw new MailetException(exceptionBuffer.toString());
141             }
142 
143             // Check if the required members table exists. If not, complain.
144             // Need to ask in the case that identifiers are stored, ask the DatabaseMetaInfo.
145             // Try UPPER, lower, and MixedCase, to see if the table is there.
146             if (!( theJDBCUtil.tableExists(dbMetaData, membersTable)))  {
147                 StringBuffer exceptionBuffer =
148                     new StringBuffer(128)
149                             .append("Could not find table '")
150                             .append(membersTable)
151                             .append("' in datasource '")
152                             .append(datasourceName)
153                             .append("'");
154                 throw new MailetException(exceptionBuffer.toString());
155             }
156 
157             StringBuffer queryBuffer =
158                 new StringBuffer(256)
159                         .append("SELECT members_only, attachments_allowed, reply_to_list, subject_prefix, list_address FROM ")
160                         .append(listservTable)
161                         .append(" WHERE listserv_id = ?");
162             listservQuery = queryBuffer.toString();
163             queryBuffer =
164                 new StringBuffer(128)
165                         .append("SELECT member FROM ")
166                         .append(membersTable)
167                         .append(" WHERE listserv_id = ?");
168             membersQuery = queryBuffer.toString();
169 
170             //Always load settings at least once... if we aren't caching, we will load at each getMembers() call
171             loadSettings();
172         } catch (MailetException me) {
173             throw me;
174         } catch (Exception e) {
175             throw new MessagingException("Error initializing JDBCListserv", e);
176         } finally {
177             theJDBCUtil.closeJDBCConnection(conn);
178         }
179     }
180 
181     /***
182      * Returns a Collection of MailAddress objects of members to receive this email
183      */
184     public Collection getMembers() throws MessagingException {
185         if (!cacheSettings) {
186             loadSettings();
187         }
188 
189         return members;
190     }
191 
192     /***
193      * Returns whether this list should restrict to senders only
194      */
195     public boolean isMembersOnly() throws MessagingException {
196         return membersOnly;
197     }
198 
199     /***
200      * Returns whether this listserv allow attachments
201      */
202     public boolean isAttachmentsAllowed() throws MessagingException {
203         return attachmentsAllowed;
204     }
205 
206     /***
207      * Returns whether listserv should add reply-to header
208      *
209      * @return whether listserv should add a reply-to header
210      */
211     public boolean isReplyToList() throws MessagingException {
212         return replyToList;
213     }
214 
215     /***
216      * The email address that this listserv processes on.  If returns null, will use the
217      * recipient of the message, which hopefully will be the correct email address assuming
218      * the matcher was properly specified.
219      */
220     public MailAddress getListservAddress() throws MessagingException {
221         return listservAddress;
222     }
223 
224     /***
225      * An optional subject prefix which will be surrounded by [].
226      */
227     public String getSubjectPrefix() throws MessagingException {
228         return subjectPrefix;
229     }
230 
231     /***
232      * Loads the configuration settings for this mailet from the database.
233      *
234      * @throws MessagingException if a problem occurs while accessing the database or
235      *                            the required parameters are not present
236      */
237     protected void loadSettings() throws MessagingException {
238         Connection conn = null;
239         PreparedStatement stmt = null;
240         ResultSet rs = null;
241 
242         try {
243             //Load members
244             conn = datasource.getConnection();
245             try {
246                 stmt = conn.prepareStatement(membersQuery);
247                 stmt.setString(1, listservID);
248                 rs = stmt.executeQuery();
249                 Collection tmpMembers = new Vector();
250                 while (rs.next()) {
251                     String address = rs.getString(1);
252                     try {
253                         MailAddress mailAddress = new MailAddress(address);
254                         tmpMembers.add(mailAddress);
255                     } catch (ParseException pe) {
256                         //don't stop... just log and continue
257                         StringBuffer exceptionBuffer =
258                             new StringBuffer(64)
259                                     .append("error parsing address '")
260                                     .append(address)
261                                     .append("' in listserv '")
262                                     .append(listservID)
263                                     .append("'");
264                         log(exceptionBuffer.toString());
265                     }
266                 }
267                 members = tmpMembers;
268             } finally {
269                 ResultSet localRS = rs;
270                 // Clear reference to result set
271                 rs = null;
272                 theJDBCUtil.closeJDBCResultSet(localRS);
273                 Statement localStmt = stmt;
274                 // Clear reference to statement
275                 stmt = null;
276                 theJDBCUtil.closeJDBCStatement(localStmt);
277             }
278 
279             stmt = conn.prepareStatement(listservQuery);
280             stmt.setString(1, listservID);
281             rs = stmt.executeQuery();
282             if (!rs.next()) {
283                 StringBuffer exceptionBuffer =
284                     new StringBuffer(64)
285                             .append("Could not find listserv record for '")
286                             .append(listservID)
287                             .append("'");
288                 throw new MailetException(exceptionBuffer.toString());
289             }
290             membersOnly = rs.getBoolean("members_only");
291             attachmentsAllowed = rs.getBoolean("attachments_allowed");
292             replyToList = rs.getBoolean("reply_to_list");
293             subjectPrefix = rs.getString("subject_prefix");
294             String address = rs.getString("list_address");
295             if (address == null) {
296                 listservAddress = null;
297             } else {
298                 try {
299                     listservAddress = new MailAddress(address);
300                 } catch (ParseException pe) {
301                     //log and ignore
302                     StringBuffer logBuffer =
303                         new StringBuffer(128)
304                                 .append("invalid listserv address '")
305                                 .append(listservAddress)
306                                 .append("' for listserv '")
307                                 .append(listservID)
308                                 .append("'");
309                     log(logBuffer.toString());
310                     listservAddress = null;
311                 }
312             }
313         } catch (SQLException sqle) {
314             throw new MailetException("Problem loading settings", sqle);
315         } finally {
316             theJDBCUtil.closeJDBCResultSet(rs);
317             theJDBCUtil.closeJDBCStatement(stmt);
318             theJDBCUtil.closeJDBCConnection(conn);
319         }
320     }
321 
322     /***
323      * Return a string describing this mailet.
324      *
325      * @return a string describing this mailet
326      */
327     public String getMailetInfo() {
328         return "JDBC listserv mailet";
329     }
330 }