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.domain;
23  
24  import java.io.InputStream;
25  import java.sql.Connection;
26  import java.sql.DatabaseMetaData;
27  import java.sql.PreparedStatement;
28  import java.sql.ResultSet;
29  import java.sql.SQLException;
30  import java.util.ArrayList;
31  import java.util.HashMap;
32  import java.util.List;
33  import java.util.Map;
34  
35  import org.apache.avalon.cornerstone.services.datasources.DataSourceSelector;
36  import org.apache.avalon.excalibur.datasource.DataSourceComponent;
37  import org.apache.avalon.framework.activity.Initializable;
38  import org.apache.avalon.framework.configuration.Configurable;
39  import org.apache.avalon.framework.configuration.Configuration;
40  import org.apache.avalon.framework.configuration.ConfigurationException;
41  import org.apache.avalon.framework.service.ServiceException;
42  import org.apache.avalon.framework.service.ServiceManager;
43  import org.apache.avalon.framework.service.Serviceable;
44  import org.apache.james.services.FileSystem;
45  import org.apache.james.util.sql.JDBCUtil;
46  import org.apache.james.util.sql.SqlResources;
47  
48  /**
49   * Allow to query a costum table for domains
50   */
51  public class JDBCDomainList extends AbstractDomainList implements Serviceable,Configurable,Initializable {
52  
53      private DataSourceSelector datasources;
54      private DataSourceComponent dataSourceComponent;
55      private FileSystem fileSystem;
56      
57      private String tableName = null;
58      private String dataSourceName = null;
59      
60      /**
61       * Contains all of the sql strings for this component.
62       */
63      protected SqlResources sqlQueries;
64      
65      /**
66       * The name of the SQL configuration file to be used to configure this repository.
67       */
68      private String sqlFileName;
69  
70      protected String datasourceName;
71  
72      /**
73       * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
74       */
75      public void service(ServiceManager arg0) throws ServiceException {
76          super.service(arg0);
77          datasources = (DataSourceSelector)arg0.lookup(DataSourceSelector.ROLE); 
78          setFileSystem((FileSystem) arg0.lookup(FileSystem.ROLE));
79      }
80      
81      /**
82       * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
83       */
84      public void configure(Configuration arg0) throws ConfigurationException {
85          Configuration config = arg0.getChild("repositoryPath");
86      
87          if (config == null) {
88              throw new ConfigurationException("RepositoryPath must configured");
89          }
90          
91          String destination = config.getValue();
92          // normalize the destination, to simplify processing.
93          if ( ! destination.endsWith("/") ) {
94              destination += "/";
95          }
96          // Parse the DestinationURL for the name of the datasource,
97          // the table to use, and the (optional) repository Key.
98          // Split on "/", starting after "db://"
99          List urlParams = new ArrayList();
100         int start = 5;
101         
102         int end = destination.indexOf('/', start);
103         while ( end > -1 ) {
104             urlParams.add(destination.substring(start, end));
105             start = end + 1;
106             end = destination.indexOf('/', start);
107         }
108         
109         // Build SqlParameters and get datasource name from URL parameters
110         if (urlParams.size() != 2) {
111             StringBuffer exceptionBuffer =
112                 new StringBuffer(256)
113                         .append("Malformed destinationURL - Must be of the format '")
114                         .append("db://<data-source>/<table>'.  Was passed ")
115                         .append(arg0.getAttribute("repositoryPath"));
116             throw new ConfigurationException(exceptionBuffer.toString());
117         }
118         dataSourceName = (String)urlParams.get(0);
119         tableName = (String)urlParams.get(1);
120 
121 
122         if (getLogger().isDebugEnabled()) {
123             StringBuffer logBuffer =
124                 new StringBuffer(128)
125                         .append("Parsed URL: table = '")
126                         .append(tableName)
127                         .append("'");
128             getLogger().debug(logBuffer.toString());
129         }
130     
131         sqlFileName = arg0.getChild("sqlFile").getValue();
132         
133         Configuration autoConf = arg0.getChild("autodetect");
134         if (autoConf != null) {
135             setAutoDetect(autoConf.getValueAsBoolean(true));    
136         }
137         
138         Configuration autoIPConf = arg0.getChild("autodetectIP");
139         if (autoConf != null) {
140             setAutoDetectIP(autoIPConf.getValueAsBoolean(true));    
141         }
142     }
143     
144     /**
145      * @see org.apache.avalon.framework.activity.Initializable#initialize()
146      */
147     public void initialize() throws Exception {
148     
149         setDataSourceComponent((DataSourceComponent) datasources.select(dataSourceName));
150     
151         StringBuffer logBuffer = null;
152         if (getLogger().isDebugEnabled()) {
153             getLogger().debug(this.getClass().getName() + ".initialize()");
154         }
155 
156         // Test the connection to the database, by getting the DatabaseMetaData.
157         Connection conn = dataSourceComponent.getConnection();
158         PreparedStatement createStatement = null;
159 
160         try {
161             // Initialise the sql strings.
162 
163             InputStream sqlFile = null;
164             try {
165                 sqlFile = fileSystem.getResource(sqlFileName);
166                 sqlFileName = null;
167             } catch (Exception e) {
168                 getLogger().fatalError(e.getMessage(), e);
169                 throw e;
170             }
171 
172             if (getLogger().isDebugEnabled()) {
173                 logBuffer =
174                     new StringBuffer(128)
175                             .append("Reading SQL resources from file: ")
176                             .append(sqlFileName)
177                             .append(", section ")
178                             .append(this.getClass().getName())
179                             .append(".");
180                 getLogger().debug(logBuffer.toString());
181             }
182 
183             // Build the statement parameters
184             Map sqlParameters = new HashMap();
185             if (tableName != null) {
186                 sqlParameters.put("table", tableName);
187             }
188             
189             sqlQueries = new SqlResources();
190             sqlQueries.init(sqlFile, this.getClass().getName(),
191                             conn, sqlParameters);
192 
193             // Check if the required table exists. If not, create it.
194             DatabaseMetaData dbMetaData = conn.getMetaData();
195             // Need to ask in the case that identifiers are stored, ask the DatabaseMetaInfo.
196             // Try UPPER, lower, and MixedCase, to see if the table is there.
197            
198             if (!(theJDBCUtil.tableExists(dbMetaData, tableName))) {
199            
200                 // Users table doesn't exist - create it.
201                 createStatement =
202                     conn.prepareStatement(sqlQueries.getSqlString("createTable", true));
203                 createStatement.execute();
204 
205                 if (getLogger().isInfoEnabled()) {
206                     logBuffer =
207                         new StringBuffer(64)
208                                 .append("JdbcVirtalUserTable: Created table '")
209                                 .append(tableName)
210                                 .append("'.");
211                     getLogger().info(logBuffer.toString());
212                 }
213             }
214 
215           
216         } finally {
217             theJDBCUtil.closeJDBCStatement(createStatement);
218             theJDBCUtil.closeJDBCConnection(conn);
219         }
220     }
221     
222     /**
223      * The JDBCUtil helper class
224      */
225     private final JDBCUtil theJDBCUtil = new JDBCUtil() {
226         protected void delegatedLog(String logString) {
227             getLogger().debug("JDBCVirtualUserTable: " + logString);
228         }
229     };
230 
231     public void setDataSourceComponent(DataSourceComponent dataSourceComponent) {
232         this.dataSourceComponent = dataSourceComponent;
233     }
234     
235 
236     public void setFileSystem(FileSystem fileSystem) {
237         this.fileSystem = fileSystem;
238     }
239 
240     /**
241      * @see org.apache.james.domain.AbstractDomainList#getDomainListInternal()
242      */
243     protected List getDomainListInternal() {
244         List domains = new ArrayList();
245         Connection conn = null;
246         PreparedStatement mappingStmt = null;
247         
248         try {
249             conn = dataSourceComponent.getConnection();
250             mappingStmt = conn.prepareStatement(sqlQueries.getSqlString("selectDomains", true));
251 
252             ResultSet mappingRS = null;
253             try {
254                 mappingRS = mappingStmt.executeQuery();
255                 while (mappingRS.next()) {
256                     String domain = mappingRS.getString(1).toLowerCase();
257                     if(domains.contains(domains) == false) {
258                         domains.add(domain);
259                     }
260                 }
261             } finally {
262                 theJDBCUtil.closeJDBCResultSet(mappingRS);
263             }
264             
265         } catch (SQLException sqle) {
266             getLogger().error("Error accessing database", sqle);
267         } finally {
268             theJDBCUtil.closeJDBCStatement(mappingStmt);
269             theJDBCUtil.closeJDBCConnection(conn);
270         }
271         if (domains.size() == 0) {
272             return null;
273         } else {
274             return domains;
275         }
276     }
277 
278     /**
279      * @see org.apache.james.api.domainlist.DomainList#containsDomain(java.lang.String)
280      */
281     public boolean containsDomain(String domain) {
282         Connection conn = null;
283         PreparedStatement mappingStmt = null;
284         
285         try {
286             conn = dataSourceComponent.getConnection();
287             mappingStmt = conn.prepareStatement(sqlQueries.getSqlString("selectDomain", true));
288 
289             ResultSet mappingRS = null;
290             try {
291                 mappingStmt.setString(1, domain);
292                 mappingRS = mappingStmt.executeQuery();
293                 if (mappingRS.next()) {
294                     return true;
295                 }
296             } finally {
297                 theJDBCUtil.closeJDBCResultSet(mappingRS);
298             }
299             
300         } catch (SQLException sqle) {
301             getLogger().error("Error accessing database", sqle);
302         } finally {
303             theJDBCUtil.closeJDBCStatement(mappingStmt);
304             theJDBCUtil.closeJDBCConnection(conn);
305         }
306         return false;
307     }
308 
309     /**
310      * @see org.apache.james.domain.AbstractDomainList#addDomainInternal(java.lang.String)
311      */
312     protected boolean addDomainInternal(String domain) {
313         Connection conn = null;
314         PreparedStatement mappingStmt = null;
315         
316         try {
317             conn = dataSourceComponent.getConnection();
318             mappingStmt = conn.prepareStatement(sqlQueries.getSqlString("addDomain", true));
319 
320             ResultSet mappingRS = null;
321             try {
322             mappingStmt.setString(1, domain);
323                 if (mappingStmt.executeUpdate() > 0) {
324                     return true;
325                 }
326             } finally {
327                 theJDBCUtil.closeJDBCResultSet(mappingRS);
328             }
329             
330         } catch (SQLException sqle) {
331             getLogger().error("Error accessing database", sqle);
332         } finally {
333             theJDBCUtil.closeJDBCStatement(mappingStmt);
334             theJDBCUtil.closeJDBCConnection(conn);
335         }
336         return false;
337     }
338 
339     /**
340      * @see org.apache.james.domain.AbstractDomainList#removeDomainInternal(java.lang.String)
341      */
342     protected boolean removeDomainInternal(String domain) {
343         Connection conn = null;
344         PreparedStatement mappingStmt = null;
345         
346         try {
347             conn = dataSourceComponent.getConnection();
348             mappingStmt = conn.prepareStatement(sqlQueries.getSqlString("removeDomain", true));
349 
350             ResultSet mappingRS = null;
351             try {
352             mappingStmt.setString(1, domain);
353                 if (mappingStmt.executeUpdate() > 0) {
354                     return true;
355                 }
356             } finally {
357                 theJDBCUtil.closeJDBCResultSet(mappingRS);
358             }
359             
360         } catch (SQLException sqle) {
361             getLogger().error("Error accessing database", sqle);
362         } finally {
363             theJDBCUtil.closeJDBCStatement(mappingStmt);
364             theJDBCUtil.closeJDBCConnection(conn);
365         }
366         return false;
367     }
368 }