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