001/****************************************************************
002 * Licensed to the Apache Software Foundation (ASF) under one   *
003 * or more contributor license agreements.  See the NOTICE file *
004 * distributed with this work for additional information        *
005 * regarding copyright ownership.  The ASF licenses this file   *
006 * to you under the Apache License, Version 2.0 (the            *
007 * "License"); you may not use this file except in compliance   *
008 * with the License.  You may obtain a copy of the License at   *
009 *                                                              *
010 *   http://www.apache.org/licenses/LICENSE-2.0                 *
011 *                                                              *
012 * Unless required by applicable law or agreed to in writing,   *
013 * software distributed under the License is distributed on an  *
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
015 * KIND, either express or implied.  See the License for the    *
016 * specific language governing permissions and limitations      *
017 * under the License.                                           *
018 ****************************************************************/
019package org.apache.james.rrt.jdbc;
020
021import java.io.InputStream;
022import java.sql.Connection;
023import java.sql.DatabaseMetaData;
024import java.sql.PreparedStatement;
025import java.sql.ResultSet;
026import java.sql.SQLException;
027import java.util.ArrayList;
028import java.util.HashMap;
029import java.util.List;
030import java.util.Map;
031
032import javax.annotation.PostConstruct;
033import javax.inject.Inject;
034import javax.sql.DataSource;
035
036import org.apache.commons.configuration.ConfigurationException;
037import org.apache.commons.configuration.HierarchicalConfiguration;
038import org.apache.james.filesystem.api.FileSystem;
039import org.apache.james.rrt.api.RecipientRewriteTableException;
040import org.apache.james.rrt.lib.AbstractRecipientRewriteTable;
041import org.apache.james.rrt.lib.Mappings;
042import org.apache.james.rrt.lib.MappingsImpl;
043import org.apache.james.util.sql.JDBCUtil;
044import org.apache.james.util.sql.SqlResources;
045
046/**
047 * Class responsible to implement the Virtual User Table in database with JDBC
048 * access.
049 * 
050 * @deprecated use JPARecipientRewriteTable
051 */
052@Deprecated
053public class JDBCRecipientRewriteTable extends AbstractRecipientRewriteTable {
054
055    private DataSource dataSource = null;
056
057    private String tableName = "RecipientRewriteTable";
058
059    /**
060     * Contains all of the sql strings for this component.
061     */
062    private SqlResources sqlQueries;
063
064    /**
065     * The name of the SQL configuration file to be used to configure this
066     * repository.
067     */
068    private String sqlFileName;
069
070    private FileSystem fileSystem;
071
072    /**
073     * The JDBCUtil helper class
074     */
075    private final JDBCUtil theJDBCUtil = new JDBCUtil() {
076        protected void delegatedLog(String logString) {
077            getLogger().debug("JDBCRecipientRewriteTable: " + logString);
078        }
079    };
080
081    @PostConstruct
082    public void init() throws Exception {
083
084        StringBuffer logBuffer;
085        if (getLogger().isDebugEnabled()) {
086            getLogger().debug(this.getClass().getName() + ".initialize()");
087        }
088
089        // Test the connection to the database, by getting the DatabaseMetaData.
090        Connection conn = dataSource.getConnection();
091        PreparedStatement createStatement = null;
092
093        try {
094            // Initialise the sql strings.
095
096            InputStream sqlFile;
097
098            try {
099                sqlFile = fileSystem.getResource(sqlFileName);
100            } catch (Exception e) {
101                getLogger().error(e.getMessage(), e);
102                throw e;
103            }
104
105            if (getLogger().isDebugEnabled()) {
106                logBuffer = new StringBuffer(128).append("Reading SQL resources from file: ").append(sqlFileName).append(", section ").append(this.getClass().getName()).append(".");
107                getLogger().debug(logBuffer.toString());
108            }
109
110            // Build the statement parameters
111            Map<String, String> sqlParameters = new HashMap<String, String>();
112            if (tableName != null) {
113                sqlParameters.put("table", tableName);
114            }
115
116            sqlQueries = new SqlResources();
117            sqlQueries.init(sqlFile, this.getClass().getName(), conn, sqlParameters);
118
119            // Check if the required table exists. If not, create it.
120            DatabaseMetaData dbMetaData = conn.getMetaData();
121
122            // Need to ask in the case that identifiers are stored, ask the
123            // DatabaseMetaInfo.
124            // Try UPPER, lower, and MixedCase, to see if the table is there.
125            if (!(theJDBCUtil.tableExists(dbMetaData, tableName))) {
126
127                // Users table doesn't exist - create it.
128                createStatement = conn.prepareStatement(sqlQueries.getSqlString("createTable", true));
129                createStatement.execute();
130
131                if (getLogger().isInfoEnabled()) {
132                    logBuffer = new StringBuffer(64).append("JdbcVirtalUserTable: Created table '").append(tableName).append("'.");
133                    getLogger().info(logBuffer.toString());
134                }
135            }
136
137        } finally {
138            theJDBCUtil.closeJDBCStatement(createStatement);
139            theJDBCUtil.closeJDBCConnection(conn);
140        }
141    }
142
143    @Inject
144    public void setFileSystem(FileSystem fileSystem) {
145        this.fileSystem = fileSystem;
146    }
147
148    @Inject
149    public void setDataSource(DataSource dataSource) {
150        this.dataSource = dataSource;
151    }
152
153    protected void doConfigure(HierarchicalConfiguration conf) throws ConfigurationException {
154
155        String destination = conf.getString("[@destinationURL]", null);
156
157        if (destination == null) {
158            throw new ConfigurationException("destinationURL must configured");
159        }
160
161        // normalize the destination, to simplify processing.
162        if (!destination.endsWith("/")) {
163            destination += "/";
164        }
165        // Parse the DestinationURL for the name of the datasource,
166        // the table to use, and the (optional) repository Key.
167        // Split on "/", starting after "db://"
168        List<String> urlParams = new ArrayList<String>();
169        int start = 5;
170
171        int end = destination.indexOf('/', start);
172        while (end > -1) {
173            urlParams.add(destination.substring(start, end));
174            start = end + 1;
175            end = destination.indexOf('/', start);
176        }
177
178        // Build SqlParameters and get datasource name from URL parameters
179        if (urlParams.size() == 0) {
180            String exceptionBuffer = "Malformed destinationURL - Must be of the format '" + "db://<data-source>'.  Was passed " + conf.getString("[@destinationURL]");
181            throw new ConfigurationException(exceptionBuffer);
182        }
183
184        if (urlParams.size() >= 2) {
185            tableName = urlParams.get(1);
186        }
187
188        if (getLogger().isDebugEnabled()) {
189            String logBuffer = "Parsed URL: table = '" + tableName + "'";
190            getLogger().debug(logBuffer);
191        }
192
193        sqlFileName = conf.getString("sqlFile");
194
195    }
196
197    /**
198     * @throws RecipientRewriteTableException
199     * @see org.apache.james.rrt.lib.AbstractRecipientRewriteTable#addMappingInternal(String,
200     *      String, String)
201     */
202    protected void addMappingInternal(String user, String domain, String regex) throws RecipientRewriteTableException {
203        String fixedUser = getFixedUser(user);
204        String fixedDomain = getFixedDomain(domain);
205        Mappings map = getUserDomainMappings(fixedUser, fixedDomain);
206        if (map != null && map.size() != 0) {
207            Mappings updatedMappings = MappingsImpl.from(map).add(regex).build();
208            doUpdateMapping(fixedUser, fixedDomain, updatedMappings.serialize());
209        }
210        doAddMapping(fixedUser, fixedDomain, regex);
211    }
212
213    /**
214     * @see org.apache.james.rrt.lib.AbstractRecipientRewriteTable#mapAddressInternal(java.lang.String,
215     *      java.lang.String)
216     */
217    protected String mapAddressInternal(String user, String domain) throws RecipientRewriteTableException {
218        Connection conn = null;
219        PreparedStatement mappingStmt = null;
220        try {
221            conn = dataSource.getConnection();
222            mappingStmt = conn.prepareStatement(sqlQueries.getSqlString("selectMappings", true));
223
224            ResultSet mappingRS = null;
225            try {
226                mappingStmt.setString(1, user);
227                mappingStmt.setString(2, domain);
228                mappingRS = mappingStmt.executeQuery();
229                if (mappingRS.next()) {
230                    return mappingRS.getString(1);
231                }
232            } finally {
233                theJDBCUtil.closeJDBCResultSet(mappingRS);
234            }
235
236        } catch (SQLException sqle) {
237            getLogger().error("Error accessing database", sqle);
238            throw new RecipientRewriteTableException("Error accessing database", sqle);
239        } finally {
240            theJDBCUtil.closeJDBCStatement(mappingStmt);
241            theJDBCUtil.closeJDBCConnection(conn);
242        }
243        return null;
244    }
245
246    /**
247     * @throws RecipientRewriteTableException
248     * @see org.apache.james.rrt.lib.AbstractRecipientRewriteTable#mapAddress(java.lang.String,
249     *      java.lang.String)
250     */
251    protected Mappings getUserDomainMappingsInternal(String user, String domain) throws RecipientRewriteTableException {
252        Connection conn = null;
253        PreparedStatement mappingStmt = null;
254        try {
255            conn = dataSource.getConnection();
256            mappingStmt = conn.prepareStatement(sqlQueries.getSqlString("selectUserDomainMapping", true));
257            ResultSet mappingRS = null;
258            try {
259                mappingStmt.setString(1, user);
260                mappingStmt.setString(2, domain);
261                mappingRS = mappingStmt.executeQuery();
262                if (mappingRS.next()) {
263                    return MappingsImpl.fromRawString(mappingRS.getString(1));
264                }
265            } finally {
266                theJDBCUtil.closeJDBCResultSet(mappingRS);
267            }
268        } catch (SQLException sqle) {
269            getLogger().error("Error accessing database", sqle);
270            throw new RecipientRewriteTableException("Error accessing database", sqle);
271        } finally {
272            theJDBCUtil.closeJDBCStatement(mappingStmt);
273            theJDBCUtil.closeJDBCConnection(conn);
274        }
275        return null;
276    }
277
278    /**
279     * @throws RecipientRewriteTableException
280     * @see org.apache.james.rrt.lib.AbstractRecipientRewriteTable#getAllMappingsInternal()
281     */
282    protected Map<String, Mappings> getAllMappingsInternal() throws RecipientRewriteTableException {
283        Connection conn = null;
284        PreparedStatement mappingStmt = null;
285        Map<String, Mappings> mapping = new HashMap<String, Mappings>();
286        try {
287            conn = dataSource.getConnection();
288            mappingStmt = conn.prepareStatement(sqlQueries.getSqlString("selectAllMappings", true));
289            ResultSet mappingRS = null;
290            try {
291                mappingRS = mappingStmt.executeQuery();
292                while (mappingRS.next()) {
293                    String user = mappingRS.getString(1);
294                    String domain = mappingRS.getString(2);
295                    String map = mappingRS.getString(3);
296                    mapping.put(user + "@" + domain, MappingsImpl.fromRawString(map));
297                }
298                if (mapping.size() > 0)
299                    return mapping;
300            } finally {
301                theJDBCUtil.closeJDBCResultSet(mappingRS);
302            }
303
304        } catch (SQLException sqle) {
305            getLogger().error("Error accessing database", sqle);
306            throw new RecipientRewriteTableException("Error accessing database", sqle);
307        } finally {
308            theJDBCUtil.closeJDBCStatement(mappingStmt);
309            theJDBCUtil.closeJDBCConnection(conn);
310        }
311        return null;
312    }
313
314    /**
315     * @throws RecipientRewriteTableException
316     * @see org.apache.james.rrt.lib.AbstractRecipientRewriteTable#removeMappingInternal(String,
317     *      String, String)
318     */
319    protected void removeMappingInternal(String user, String domain, String mapping) throws RecipientRewriteTableException {
320        String fixedUser = getFixedUser(user);
321        String fixedDomain = getFixedDomain(domain);
322        Mappings map = getUserDomainMappings(fixedUser, fixedDomain);
323        if (map != null && map.size() > 1) {
324            Mappings updatedMappings = map.remove(mapping);
325            doUpdateMapping(fixedUser, fixedDomain, updatedMappings.serialize());
326        } else {
327            doRemoveMapping(fixedUser, fixedDomain, mapping);
328        }
329    }
330
331    /**
332     * Update the mapping for the given user and domain
333     * 
334     * @param user
335     *            the user
336     * @param domain
337     *            the domain
338     * @param mapping
339     *            the mapping
340     * @return true if update was successfully
341     * @throws RecipientRewriteTableException
342     */
343    private void doUpdateMapping(String user, String domain, String mapping) throws RecipientRewriteTableException {
344        Connection conn = null;
345        PreparedStatement mappingStmt = null;
346
347        try {
348            conn = dataSource.getConnection();
349            mappingStmt = conn.prepareStatement(sqlQueries.getSqlString("updateMapping", true));
350
351            ResultSet mappingRS = null;
352            try {
353                mappingStmt.setString(1, mapping);
354                mappingStmt.setString(2, user);
355                mappingStmt.setString(3, domain);
356
357                if (mappingStmt.executeUpdate() < 1) {
358                    throw new RecipientRewriteTableException("Mapping not found");
359                }
360            } finally {
361                theJDBCUtil.closeJDBCResultSet(mappingRS);
362            }
363
364        } catch (SQLException sqle) {
365            getLogger().error("Error accessing database", sqle);
366            throw new RecipientRewriteTableException("Error accessing database", sqle);
367        } finally {
368            theJDBCUtil.closeJDBCStatement(mappingStmt);
369            theJDBCUtil.closeJDBCConnection(conn);
370        }
371    }
372
373    /**
374     * Remove a mapping for the given user and domain
375     * 
376     * @param user
377     *            the user
378     * @param domain
379     *            the domain
380     * @param mapping
381     *            the mapping
382     * @return true if succesfully
383     * @throws RecipientRewriteTableException
384     */
385    private void doRemoveMapping(String user, String domain, String mapping) throws RecipientRewriteTableException {
386        Connection conn = null;
387        PreparedStatement mappingStmt = null;
388
389        try {
390            conn = dataSource.getConnection();
391            mappingStmt = conn.prepareStatement(sqlQueries.getSqlString("deleteMapping", true));
392
393            ResultSet mappingRS = null;
394            try {
395                mappingStmt.setString(1, user);
396                mappingStmt.setString(2, domain);
397                mappingStmt.setString(3, mapping);
398                if (mappingStmt.executeUpdate() < 1) {
399                    throw new RecipientRewriteTableException("Mapping not found");
400                }
401            } finally {
402                theJDBCUtil.closeJDBCResultSet(mappingRS);
403            }
404
405        } catch (SQLException sqle) {
406            getLogger().error("Error accessing database", sqle);
407        } finally {
408            theJDBCUtil.closeJDBCStatement(mappingStmt);
409            theJDBCUtil.closeJDBCConnection(conn);
410        }
411    }
412
413    /**
414     * Add mapping for given user and domain
415     * 
416     * @param user
417     *            the user
418     * @param domain
419     *            the domain
420     * @param mapping
421     *            the mapping
422     * @return true if successfully
423     * @throws RecipientRewriteTableException
424     */
425    private void doAddMapping(String user, String domain, String mapping) throws RecipientRewriteTableException {
426        Connection conn = null;
427        PreparedStatement mappingStmt = null;
428
429        try {
430            conn = dataSource.getConnection();
431            mappingStmt = conn.prepareStatement(sqlQueries.getSqlString("addMapping", true));
432
433            ResultSet mappingRS = null;
434            try {
435                mappingStmt.setString(1, user);
436                mappingStmt.setString(2, domain);
437                mappingStmt.setString(3, mapping);
438
439                if (mappingStmt.executeUpdate() < 1) {
440                    throw new RecipientRewriteTableException("Mapping not found");
441                }
442            } finally {
443                theJDBCUtil.closeJDBCResultSet(mappingRS);
444            }
445
446        } catch (SQLException sqle) {
447            getLogger().error("Error accessing database", sqle);
448        } finally {
449            theJDBCUtil.closeJDBCStatement(mappingStmt);
450            theJDBCUtil.closeJDBCConnection(conn);
451        }
452    }
453
454}