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