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