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.GenericMailet;
26  import org.apache.mailet.Mail;
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.util.Collection;
39  import java.util.Iterator;
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="JDBCAlias">
48   *   <mappings>db://maildb/Aliases</mappings>
49   *   <source_column>source_email_address</source_column>
50   *   <target_column>target_email_address</target_column>
51   * </mailet>
52   *
53   */
54  public class JDBCAlias extends GenericMailet {
55  
56      protected DataSourceComponent datasource;
57      protected String query = null;
58  
59      // The JDBCUtil helper class
60      private final JDBCUtil theJDBCUtil =
61              new JDBCUtil() {
62                  protected void delegatedLog(String logString) {
63                      log("JDBCAlias: " + logString);
64                  }
65              };
66  
67      /***
68       * Initialize the mailet
69       */
70      public void init() throws MessagingException {
71          String mappingsURL = getInitParameter("mappings");
72  
73          String datasourceName = mappingsURL.substring(5);
74          int pos = datasourceName.indexOf("/");
75          String tableName = datasourceName.substring(pos + 1);
76          datasourceName = datasourceName.substring(0, pos);
77  
78          Connection conn = null;
79          if (getInitParameter("source_column") == null) {
80              throw new MailetException("source_column not specified for JDBCAlias");
81          }
82          if (getInitParameter("target_column") == null) {
83              throw new MailetException("target_column not specified for JDBCAlias");
84          }
85          try {
86              ServiceManager componentManager = (ServiceManager)getMailetContext().getAttribute(Constants.AVALON_COMPONENT_MANAGER);
87              // Get the DataSourceSelector service
88              DataSourceSelector datasources = (DataSourceSelector)componentManager.lookup(DataSourceSelector.ROLE);
89              // Get the data-source required.
90              datasource = (DataSourceComponent)datasources.select(datasourceName);
91  
92              conn = datasource.getConnection();
93  
94              // Check if the required table exists. If not, complain.
95              DatabaseMetaData dbMetaData = conn.getMetaData();
96              // Need to ask in the case that identifiers are stored, ask the DatabaseMetaInfo.
97              // Try UPPER, lower, and MixedCase, to see if the table is there.
98              if (!(theJDBCUtil.tableExists(dbMetaData, tableName)))  {
99                  StringBuffer exceptionBuffer =
100                     new StringBuffer(128)
101                             .append("Could not find table '")
102                             .append(tableName)
103                             .append("' in datasource '")
104                             .append(datasourceName)
105                             .append("'");
106                 throw new MailetException(exceptionBuffer.toString());
107             }
108 
109             //Build the query
110             StringBuffer queryBuffer =
111                 new StringBuffer(128)
112                         .append("SELECT ")
113                         .append(getInitParameter("target_column"))
114                         .append(" FROM ")
115                         .append(tableName)
116                         .append(" WHERE ")
117                         .append(getInitParameter("source_column"))
118                         .append(" = ?");
119             query = queryBuffer.toString();
120         } catch (MailetException me) {
121             throw me;
122         } catch (Exception e) {
123             throw new MessagingException("Error initializing JDBCAlias", e);
124         } finally {
125             theJDBCUtil.closeJDBCConnection(conn);
126         }
127     }
128 
129     public void service(Mail mail) throws MessagingException {
130         //Then loop through each address in the recipient list and try to map it according to the alias table
131 
132         Connection conn = null;
133         PreparedStatement mappingStmt = null;
134         ResultSet mappingRS = null;
135 
136         Collection recipients = mail.getRecipients();
137         Collection recipientsToRemove = new Vector();
138         Collection recipientsToAdd = new Vector();
139         try {
140             conn = datasource.getConnection();
141             mappingStmt = conn.prepareStatement(query);
142 
143 
144             for (Iterator i = recipients.iterator(); i.hasNext(); ) {
145                 try {
146                     MailAddress source = (MailAddress)i.next();
147                     mappingStmt.setString(1, source.toString());
148                     mappingRS = mappingStmt.executeQuery();
149                     if (!mappingRS.next()) {
150                         //This address was not found
151                         continue;
152                     }
153                     try {
154                         String targetString = mappingRS.getString(1);
155                         MailAddress target = new MailAddress(targetString);
156 
157                         //Mark this source address as an address to remove from the recipient list
158                         recipientsToRemove.add(source);
159                         recipientsToAdd.add(target);
160                     } catch (ParseException pe) {
161                         //Don't alias this address... there's an invalid address mapping here
162                         StringBuffer exceptionBuffer =
163                             new StringBuffer(128)
164                                     .append("There is an invalid alias from ")
165                                     .append(source)
166                                     .append(" to ")
167                                     .append(mappingRS.getString(1));
168                         log(exceptionBuffer.toString());
169                         continue;
170                     }
171                 } finally {
172                     ResultSet localRS = mappingRS;
173                     // Clear reference to result set
174                     mappingRS = null;
175                     theJDBCUtil.closeJDBCResultSet(localRS);
176                 }
177             }
178         } catch (SQLException sqle) {
179             throw new MessagingException("Error accessing database", sqle);
180         } finally {
181             theJDBCUtil.closeJDBCStatement(mappingStmt);
182             theJDBCUtil.closeJDBCConnection(conn);
183         }
184 
185         recipients.removeAll(recipientsToRemove);
186         recipients.addAll(recipientsToAdd);
187     }
188 
189     /***
190      * Return a string describing this mailet.
191      *
192      * @return a string describing this mailet
193      */
194     public String getMailetInfo() {
195         return "JDBC aliasing mailet";
196     }
197 
198 }