View Javadoc

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