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 }