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